17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd.
3*55fea89dSDan Cross *
47c478bd9Sstevel@tonic-gate * All rights reserved.
5*55fea89dSDan Cross *
67c478bd9Sstevel@tonic-gate * Permission is hereby granted, free of charge, to any person obtaining a
77c478bd9Sstevel@tonic-gate * copy of this software and associated documentation files (the
87c478bd9Sstevel@tonic-gate * "Software"), to deal in the Software without restriction, including
97c478bd9Sstevel@tonic-gate * without limitation the rights to use, copy, modify, merge, publish,
107c478bd9Sstevel@tonic-gate * distribute, and/or sell copies of the Software, and to permit persons
117c478bd9Sstevel@tonic-gate * to whom the Software is furnished to do so, provided that the above
127c478bd9Sstevel@tonic-gate * copyright notice(s) and this permission notice appear in all copies of
137c478bd9Sstevel@tonic-gate * the Software and that both the above copyright notice(s) and this
147c478bd9Sstevel@tonic-gate * permission notice appear in supporting documentation.
15*55fea89dSDan Cross *
167c478bd9Sstevel@tonic-gate * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
177c478bd9Sstevel@tonic-gate * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
187c478bd9Sstevel@tonic-gate * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
197c478bd9Sstevel@tonic-gate * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
207c478bd9Sstevel@tonic-gate * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
217c478bd9Sstevel@tonic-gate * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
227c478bd9Sstevel@tonic-gate * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
237c478bd9Sstevel@tonic-gate * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
247c478bd9Sstevel@tonic-gate * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25*55fea89dSDan Cross *
267c478bd9Sstevel@tonic-gate * Except as contained in this notice, the name of a copyright holder
277c478bd9Sstevel@tonic-gate * shall not be used in advertising or otherwise to promote the sale, use
287c478bd9Sstevel@tonic-gate * or other dealings in this Software without prior written authorization
297c478bd9Sstevel@tonic-gate * of the copyright holder.
307c478bd9Sstevel@tonic-gate */
317c478bd9Sstevel@tonic-gate
327c478bd9Sstevel@tonic-gate /*
337c478bd9Sstevel@tonic-gate * If file-system access is to be excluded, this module has no function,
347c478bd9Sstevel@tonic-gate * so all of its code should be excluded.
357c478bd9Sstevel@tonic-gate */
367c478bd9Sstevel@tonic-gate #ifndef WITHOUT_FILE_SYSTEM
377c478bd9Sstevel@tonic-gate
387c478bd9Sstevel@tonic-gate #include <stdio.h>
397c478bd9Sstevel@tonic-gate #include <stdlib.h>
407c478bd9Sstevel@tonic-gate #include <string.h>
417c478bd9Sstevel@tonic-gate #include <errno.h>
427c478bd9Sstevel@tonic-gate
437c478bd9Sstevel@tonic-gate #include "freelist.h"
447c478bd9Sstevel@tonic-gate #include "direader.h"
457c478bd9Sstevel@tonic-gate #include "pathutil.h"
467c478bd9Sstevel@tonic-gate #include "homedir.h"
477c478bd9Sstevel@tonic-gate #include "stringrp.h"
487c478bd9Sstevel@tonic-gate #include "libtecla.h"
497c478bd9Sstevel@tonic-gate #include "ioutil.h"
507c478bd9Sstevel@tonic-gate #include "expand.h"
517c478bd9Sstevel@tonic-gate #include "errmsg.h"
527c478bd9Sstevel@tonic-gate
537c478bd9Sstevel@tonic-gate /*
547c478bd9Sstevel@tonic-gate * Specify the number of elements to extend the files[] array by
557c478bd9Sstevel@tonic-gate * when it proves to be too small. This also sets the initial size
567c478bd9Sstevel@tonic-gate * of the array.
577c478bd9Sstevel@tonic-gate */
587c478bd9Sstevel@tonic-gate #define MATCH_BLK_FACT 256
597c478bd9Sstevel@tonic-gate
607c478bd9Sstevel@tonic-gate /*
617c478bd9Sstevel@tonic-gate * A list of directory iterators is maintained using nodes of the
627c478bd9Sstevel@tonic-gate * following form.
637c478bd9Sstevel@tonic-gate */
647c478bd9Sstevel@tonic-gate typedef struct DirNode DirNode;
657c478bd9Sstevel@tonic-gate struct DirNode {
667c478bd9Sstevel@tonic-gate DirNode *next; /* The next directory in the list */
677c478bd9Sstevel@tonic-gate DirNode *prev; /* The node that precedes this node in the list */
687c478bd9Sstevel@tonic-gate DirReader *dr; /* The directory reader object */
697c478bd9Sstevel@tonic-gate };
707c478bd9Sstevel@tonic-gate
717c478bd9Sstevel@tonic-gate typedef struct {
727c478bd9Sstevel@tonic-gate FreeList *mem; /* Memory for DirNode list nodes */
737c478bd9Sstevel@tonic-gate DirNode *head; /* The head of the list of used and unused cache nodes */
747c478bd9Sstevel@tonic-gate DirNode *next; /* The next unused node between head and tail */
757c478bd9Sstevel@tonic-gate DirNode *tail; /* The tail of the list of unused cache nodes */
767c478bd9Sstevel@tonic-gate } DirCache;
777c478bd9Sstevel@tonic-gate
787c478bd9Sstevel@tonic-gate /*
797c478bd9Sstevel@tonic-gate * Specify how many directory cache nodes to allocate at a time.
807c478bd9Sstevel@tonic-gate */
817c478bd9Sstevel@tonic-gate #define DIR_CACHE_BLK 20
827c478bd9Sstevel@tonic-gate
837c478bd9Sstevel@tonic-gate /*
847c478bd9Sstevel@tonic-gate * Set the maximum length allowed for usernames.
857c478bd9Sstevel@tonic-gate */
867c478bd9Sstevel@tonic-gate #define USR_LEN 100
877c478bd9Sstevel@tonic-gate
887c478bd9Sstevel@tonic-gate /*
897c478bd9Sstevel@tonic-gate * Set the maximum length allowed for environment variable names.
907c478bd9Sstevel@tonic-gate */
917c478bd9Sstevel@tonic-gate #define ENV_LEN 100
927c478bd9Sstevel@tonic-gate
937c478bd9Sstevel@tonic-gate /*
947c478bd9Sstevel@tonic-gate * Set the default number of spaces place between columns when listing
957c478bd9Sstevel@tonic-gate * a set of expansions.
967c478bd9Sstevel@tonic-gate */
977c478bd9Sstevel@tonic-gate #define EF_COL_SEP 2
987c478bd9Sstevel@tonic-gate
997c478bd9Sstevel@tonic-gate struct ExpandFile {
1007c478bd9Sstevel@tonic-gate ErrMsg *err; /* The error reporting buffer */
1017c478bd9Sstevel@tonic-gate StringGroup *sg; /* A list of string segments in which */
1027c478bd9Sstevel@tonic-gate /* matching filenames are stored. */
1037c478bd9Sstevel@tonic-gate DirCache cache; /* The cache of directory reader objects */
1047c478bd9Sstevel@tonic-gate PathName *path; /* The pathname being matched */
1057c478bd9Sstevel@tonic-gate HomeDir *home; /* Home-directory lookup object */
1067c478bd9Sstevel@tonic-gate int files_dim; /* The allocated dimension of result.files[] */
1077c478bd9Sstevel@tonic-gate char usrnam[USR_LEN+1]; /* A user name */
1087c478bd9Sstevel@tonic-gate char envnam[ENV_LEN+1]; /* An environment variable name */
1097c478bd9Sstevel@tonic-gate FileExpansion result; /* The container used to return the results of */
1107c478bd9Sstevel@tonic-gate /* expanding a path. */
1117c478bd9Sstevel@tonic-gate };
1127c478bd9Sstevel@tonic-gate
1137c478bd9Sstevel@tonic-gate static int ef_record_pathname(ExpandFile *ef, const char *pathname,
1147c478bd9Sstevel@tonic-gate int remove_escapes);
1157c478bd9Sstevel@tonic-gate static char *ef_cache_pathname(ExpandFile *ef, const char *pathname,
1167c478bd9Sstevel@tonic-gate int remove_escapes);
1177c478bd9Sstevel@tonic-gate static void ef_clear_files(ExpandFile *ef);
1187c478bd9Sstevel@tonic-gate
1197c478bd9Sstevel@tonic-gate static DirNode *ef_open_dir(ExpandFile *ef, const char *pathname);
1207c478bd9Sstevel@tonic-gate static DirNode *ef_close_dir(ExpandFile *ef, DirNode *node);
1217c478bd9Sstevel@tonic-gate static char *ef_expand_special(ExpandFile *ef, const char *path, int pathlen);
1227c478bd9Sstevel@tonic-gate static int ef_match_relative_pathname(ExpandFile *ef, DirReader *dr,
1237c478bd9Sstevel@tonic-gate const char *pattern, int separate);
1247c478bd9Sstevel@tonic-gate static int ef_matches_range(int c, const char *pattern, const char **endp);
1257c478bd9Sstevel@tonic-gate static int ef_string_matches_pattern(const char *file, const char *pattern,
1267c478bd9Sstevel@tonic-gate int xplicit, const char *nextp);
1277c478bd9Sstevel@tonic-gate static int ef_cmp_strings(const void *v1, const void *v2);
1287c478bd9Sstevel@tonic-gate
1297c478bd9Sstevel@tonic-gate /*
1307c478bd9Sstevel@tonic-gate * Encapsulate the formatting information needed to layout a
1317c478bd9Sstevel@tonic-gate * multi-column listing of expansions.
1327c478bd9Sstevel@tonic-gate */
1337c478bd9Sstevel@tonic-gate typedef struct {
1347c478bd9Sstevel@tonic-gate int term_width; /* The width of the terminal (characters) */
1357c478bd9Sstevel@tonic-gate int column_width; /* The number of characters within in each column. */
1367c478bd9Sstevel@tonic-gate int ncol; /* The number of columns needed */
1377c478bd9Sstevel@tonic-gate int nline; /* The number of lines needed */
1387c478bd9Sstevel@tonic-gate } EfListFormat;
1397c478bd9Sstevel@tonic-gate
1407c478bd9Sstevel@tonic-gate /*
1417c478bd9Sstevel@tonic-gate * Given the current terminal width, and a list of file expansions,
1427c478bd9Sstevel@tonic-gate * determine how to best use the terminal width to display a multi-column
1437c478bd9Sstevel@tonic-gate * listing of expansions.
1447c478bd9Sstevel@tonic-gate */
1457c478bd9Sstevel@tonic-gate static void ef_plan_listing(FileExpansion *result, int term_width,
1467c478bd9Sstevel@tonic-gate EfListFormat *fmt);
1477c478bd9Sstevel@tonic-gate
1487c478bd9Sstevel@tonic-gate /*
1497c478bd9Sstevel@tonic-gate * Display a given line of a multi-column list of file-expansions.
1507c478bd9Sstevel@tonic-gate */
1517c478bd9Sstevel@tonic-gate static int ef_format_line(FileExpansion *result, EfListFormat *fmt, int lnum,
1527c478bd9Sstevel@tonic-gate GlWriteFn *write_fn, void *data);
1537c478bd9Sstevel@tonic-gate
1547c478bd9Sstevel@tonic-gate /*.......................................................................
1557c478bd9Sstevel@tonic-gate * Create the resources needed to expand filenames.
1567c478bd9Sstevel@tonic-gate *
1577c478bd9Sstevel@tonic-gate * Output:
1587c478bd9Sstevel@tonic-gate * return ExpandFile * The new object, or NULL on error.
1597c478bd9Sstevel@tonic-gate */
new_ExpandFile(void)1607c478bd9Sstevel@tonic-gate ExpandFile *new_ExpandFile(void)
1617c478bd9Sstevel@tonic-gate {
1627c478bd9Sstevel@tonic-gate ExpandFile *ef; /* The object to be returned */
1637c478bd9Sstevel@tonic-gate /*
1647c478bd9Sstevel@tonic-gate * Allocate the container.
1657c478bd9Sstevel@tonic-gate */
1667c478bd9Sstevel@tonic-gate ef = (ExpandFile *) malloc(sizeof(ExpandFile));
1677c478bd9Sstevel@tonic-gate if(!ef) {
1687c478bd9Sstevel@tonic-gate errno = ENOMEM;
1697c478bd9Sstevel@tonic-gate return NULL;
1707c478bd9Sstevel@tonic-gate };
1717c478bd9Sstevel@tonic-gate /*
1727c478bd9Sstevel@tonic-gate * Before attempting any operation that might fail, initialize the
1737c478bd9Sstevel@tonic-gate * container at least up to the point at which it can safely be passed
1747c478bd9Sstevel@tonic-gate * to del_ExpandFile().
1757c478bd9Sstevel@tonic-gate */
1767c478bd9Sstevel@tonic-gate ef->err = NULL;
1777c478bd9Sstevel@tonic-gate ef->sg = NULL;
1787c478bd9Sstevel@tonic-gate ef->cache.mem = NULL;
1797c478bd9Sstevel@tonic-gate ef->cache.head = NULL;
1807c478bd9Sstevel@tonic-gate ef->cache.next = NULL;
1817c478bd9Sstevel@tonic-gate ef->cache.tail = NULL;
1827c478bd9Sstevel@tonic-gate ef->path = NULL;
1837c478bd9Sstevel@tonic-gate ef->home = NULL;
1847c478bd9Sstevel@tonic-gate ef->result.files = NULL;
1857c478bd9Sstevel@tonic-gate ef->result.nfile = 0;
1867c478bd9Sstevel@tonic-gate ef->usrnam[0] = '\0';
1877c478bd9Sstevel@tonic-gate ef->envnam[0] = '\0';
1887c478bd9Sstevel@tonic-gate /*
1897c478bd9Sstevel@tonic-gate * Allocate a place to record error messages.
1907c478bd9Sstevel@tonic-gate */
1917c478bd9Sstevel@tonic-gate ef->err = _new_ErrMsg();
1927c478bd9Sstevel@tonic-gate if(!ef->err)
1937c478bd9Sstevel@tonic-gate return del_ExpandFile(ef);
1947c478bd9Sstevel@tonic-gate /*
1957c478bd9Sstevel@tonic-gate * Allocate a list of string segments for storing filenames.
1967c478bd9Sstevel@tonic-gate */
1977c478bd9Sstevel@tonic-gate ef->sg = _new_StringGroup(_pu_pathname_dim());
1987c478bd9Sstevel@tonic-gate if(!ef->sg)
1997c478bd9Sstevel@tonic-gate return del_ExpandFile(ef);
2007c478bd9Sstevel@tonic-gate /*
2017c478bd9Sstevel@tonic-gate * Allocate a freelist for allocating directory cache nodes.
2027c478bd9Sstevel@tonic-gate */
2037c478bd9Sstevel@tonic-gate ef->cache.mem = _new_FreeList(sizeof(DirNode), DIR_CACHE_BLK);
2047c478bd9Sstevel@tonic-gate if(!ef->cache.mem)
2057c478bd9Sstevel@tonic-gate return del_ExpandFile(ef);
2067c478bd9Sstevel@tonic-gate /*
2077c478bd9Sstevel@tonic-gate * Allocate a pathname buffer.
2087c478bd9Sstevel@tonic-gate */
2097c478bd9Sstevel@tonic-gate ef->path = _new_PathName();
2107c478bd9Sstevel@tonic-gate if(!ef->path)
2117c478bd9Sstevel@tonic-gate return del_ExpandFile(ef);
2127c478bd9Sstevel@tonic-gate /*
2137c478bd9Sstevel@tonic-gate * Allocate an object for looking up home-directories.
2147c478bd9Sstevel@tonic-gate */
2157c478bd9Sstevel@tonic-gate ef->home = _new_HomeDir();
2167c478bd9Sstevel@tonic-gate if(!ef->home)
2177c478bd9Sstevel@tonic-gate return del_ExpandFile(ef);
2187c478bd9Sstevel@tonic-gate /*
2197c478bd9Sstevel@tonic-gate * Allocate an array for files. This will be extended later if needed.
2207c478bd9Sstevel@tonic-gate */
2217c478bd9Sstevel@tonic-gate ef->files_dim = MATCH_BLK_FACT;
2227c478bd9Sstevel@tonic-gate ef->result.files = (char **) malloc(sizeof(ef->result.files[0]) *
2237c478bd9Sstevel@tonic-gate ef->files_dim);
2247c478bd9Sstevel@tonic-gate if(!ef->result.files) {
2257c478bd9Sstevel@tonic-gate errno = ENOMEM;
2267c478bd9Sstevel@tonic-gate return del_ExpandFile(ef);
2277c478bd9Sstevel@tonic-gate };
2287c478bd9Sstevel@tonic-gate return ef;
2297c478bd9Sstevel@tonic-gate }
2307c478bd9Sstevel@tonic-gate
2317c478bd9Sstevel@tonic-gate /*.......................................................................
2327c478bd9Sstevel@tonic-gate * Delete a ExpandFile object.
2337c478bd9Sstevel@tonic-gate *
2347c478bd9Sstevel@tonic-gate * Input:
2357c478bd9Sstevel@tonic-gate * ef ExpandFile * The object to be deleted.
2367c478bd9Sstevel@tonic-gate * Output:
2377c478bd9Sstevel@tonic-gate * return ExpandFile * The deleted object (always NULL).
2387c478bd9Sstevel@tonic-gate */
del_ExpandFile(ExpandFile * ef)2397c478bd9Sstevel@tonic-gate ExpandFile *del_ExpandFile(ExpandFile *ef)
2407c478bd9Sstevel@tonic-gate {
2417c478bd9Sstevel@tonic-gate if(ef) {
2427c478bd9Sstevel@tonic-gate DirNode *dnode;
2437c478bd9Sstevel@tonic-gate /*
2447c478bd9Sstevel@tonic-gate * Delete the string segments.
2457c478bd9Sstevel@tonic-gate */
2467c478bd9Sstevel@tonic-gate ef->sg = _del_StringGroup(ef->sg);
2477c478bd9Sstevel@tonic-gate /*
2487c478bd9Sstevel@tonic-gate * Delete the cached directory readers.
2497c478bd9Sstevel@tonic-gate */
2507c478bd9Sstevel@tonic-gate for(dnode=ef->cache.head; dnode; dnode=dnode->next)
2517c478bd9Sstevel@tonic-gate dnode->dr = _del_DirReader(dnode->dr);
2527c478bd9Sstevel@tonic-gate /*
2537c478bd9Sstevel@tonic-gate * Delete the memory from which the DirNode list was allocated, thus
2547c478bd9Sstevel@tonic-gate * deleting the list at the same time.
2557c478bd9Sstevel@tonic-gate */
2567c478bd9Sstevel@tonic-gate ef->cache.mem = _del_FreeList(ef->cache.mem, 1);
2577c478bd9Sstevel@tonic-gate ef->cache.head = ef->cache.tail = ef->cache.next = NULL;
2587c478bd9Sstevel@tonic-gate /*
2597c478bd9Sstevel@tonic-gate * Delete the pathname buffer.
2607c478bd9Sstevel@tonic-gate */
2617c478bd9Sstevel@tonic-gate ef->path = _del_PathName(ef->path);
2627c478bd9Sstevel@tonic-gate /*
2637c478bd9Sstevel@tonic-gate * Delete the home-directory lookup object.
2647c478bd9Sstevel@tonic-gate */
2657c478bd9Sstevel@tonic-gate ef->home = _del_HomeDir(ef->home);
2667c478bd9Sstevel@tonic-gate /*
2677c478bd9Sstevel@tonic-gate * Delete the array of pointers to files.
2687c478bd9Sstevel@tonic-gate */
2697c478bd9Sstevel@tonic-gate if(ef->result.files) {
2707c478bd9Sstevel@tonic-gate free(ef->result.files);
2717c478bd9Sstevel@tonic-gate ef->result.files = NULL;
2727c478bd9Sstevel@tonic-gate };
2737c478bd9Sstevel@tonic-gate /*
2747c478bd9Sstevel@tonic-gate * Delete the error report buffer.
2757c478bd9Sstevel@tonic-gate */
2767c478bd9Sstevel@tonic-gate ef->err = _del_ErrMsg(ef->err);
2777c478bd9Sstevel@tonic-gate /*
2787c478bd9Sstevel@tonic-gate * Delete the container.
2797c478bd9Sstevel@tonic-gate */
2807c478bd9Sstevel@tonic-gate free(ef);
2817c478bd9Sstevel@tonic-gate };
2827c478bd9Sstevel@tonic-gate return NULL;
2837c478bd9Sstevel@tonic-gate }
2847c478bd9Sstevel@tonic-gate
2857c478bd9Sstevel@tonic-gate /*.......................................................................
2867c478bd9Sstevel@tonic-gate * Expand a pathname, converting ~user/ and ~/ patterns at the start
2877c478bd9Sstevel@tonic-gate * of the pathname to the corresponding home directories, replacing
2887c478bd9Sstevel@tonic-gate * $envvar with the value of the corresponding environment variable,
2897c478bd9Sstevel@tonic-gate * and then, if there are any wildcards, matching these against existing
2907c478bd9Sstevel@tonic-gate * filenames.
2917c478bd9Sstevel@tonic-gate *
2927c478bd9Sstevel@tonic-gate * If no errors occur, a container is returned containing the array of
2937c478bd9Sstevel@tonic-gate * files that resulted from the expansion. If there were no wildcards
2947c478bd9Sstevel@tonic-gate * in the input pathname, this will contain just the original pathname
2957c478bd9Sstevel@tonic-gate * after expansion of ~ and $ expressions. If there were any wildcards,
2967c478bd9Sstevel@tonic-gate * then the array will contain the files that matched them. Note that
2977c478bd9Sstevel@tonic-gate * if there were any wildcards but no existing files match them, this
2987c478bd9Sstevel@tonic-gate * is counted as an error and NULL is returned.
2997c478bd9Sstevel@tonic-gate *
3007c478bd9Sstevel@tonic-gate * The supported wildcards and their meanings are:
3017c478bd9Sstevel@tonic-gate * * - Match any sequence of zero or more characters.
3027c478bd9Sstevel@tonic-gate * ? - Match any single character.
3037c478bd9Sstevel@tonic-gate * [chars] - Match any single character that appears in 'chars'.
3047c478bd9Sstevel@tonic-gate * If 'chars' contains an expression of the form a-b,
3057c478bd9Sstevel@tonic-gate * then any character between a and b, including a and b,
3067c478bd9Sstevel@tonic-gate * matches. The '-' character looses its special meaning
3077c478bd9Sstevel@tonic-gate * as a range specifier when it appears at the start
3087c478bd9Sstevel@tonic-gate * of the sequence of characters.
3097c478bd9Sstevel@tonic-gate * [^chars] - The same as [chars] except that it matches any single
3107c478bd9Sstevel@tonic-gate * character that doesn't appear in 'chars'.
3117c478bd9Sstevel@tonic-gate *
3127c478bd9Sstevel@tonic-gate * Wildcard expressions are applied to individual filename components.
3137c478bd9Sstevel@tonic-gate * They don't match across directory separators. A '.' character at
3147c478bd9Sstevel@tonic-gate * the beginning of a filename component must also be matched
3157c478bd9Sstevel@tonic-gate * explicitly by a '.' character in the input pathname, since these
3167c478bd9Sstevel@tonic-gate * are UNIX's hidden files.
3177c478bd9Sstevel@tonic-gate *
3187c478bd9Sstevel@tonic-gate * Input:
3197c478bd9Sstevel@tonic-gate * ef ExpandFile * The pathname expansion resource object.
3207c478bd9Sstevel@tonic-gate * path char * The path name to be expanded.
3217c478bd9Sstevel@tonic-gate * pathlen int The length of the suffix of path[] that
3227c478bd9Sstevel@tonic-gate * constitutes the filename to be expanded,
3237c478bd9Sstevel@tonic-gate * or -1 to specify that the whole of the
3247c478bd9Sstevel@tonic-gate * path string should be used. Note that
3257c478bd9Sstevel@tonic-gate * regardless of the value of this argument,
3267c478bd9Sstevel@tonic-gate * path[] must contain a '\0' terminated
3277c478bd9Sstevel@tonic-gate * string, since this function checks that
3287c478bd9Sstevel@tonic-gate * pathlen isn't mistakenly too long.
3297c478bd9Sstevel@tonic-gate * Output:
3307c478bd9Sstevel@tonic-gate * return FileExpansion * A pointer to a container within the given
3317c478bd9Sstevel@tonic-gate * ExpandFile object. This contains an array
3327c478bd9Sstevel@tonic-gate * of the pathnames that resulted from expanding
3337c478bd9Sstevel@tonic-gate * ~ and $ expressions and from matching any
3347c478bd9Sstevel@tonic-gate * wildcards, sorted into lexical order.
3357c478bd9Sstevel@tonic-gate * This container and its contents will be
3367c478bd9Sstevel@tonic-gate * recycled on subsequent calls, so if you need
3377c478bd9Sstevel@tonic-gate * to keep the results of two successive runs,
3387c478bd9Sstevel@tonic-gate * you will either have to allocate a private
3397c478bd9Sstevel@tonic-gate * copy of the array, or use two ExpandFile
3407c478bd9Sstevel@tonic-gate * objects.
3417c478bd9Sstevel@tonic-gate *
3427c478bd9Sstevel@tonic-gate * On error NULL is returned. A description
3437c478bd9Sstevel@tonic-gate * of the error can be acquired by calling the
3447c478bd9Sstevel@tonic-gate * ef_last_error() function.
3457c478bd9Sstevel@tonic-gate */
ef_expand_file(ExpandFile * ef,const char * path,int pathlen)3467c478bd9Sstevel@tonic-gate FileExpansion *ef_expand_file(ExpandFile *ef, const char *path, int pathlen)
3477c478bd9Sstevel@tonic-gate {
3487c478bd9Sstevel@tonic-gate DirNode *dnode; /* A directory-reader cache node */
3497c478bd9Sstevel@tonic-gate const char *dirname; /* The name of the top level directory of the search */
3507c478bd9Sstevel@tonic-gate const char *pptr; /* A pointer into path[] */
3517c478bd9Sstevel@tonic-gate int wild; /* True if the path contains any wildcards */
3527c478bd9Sstevel@tonic-gate /*
3537c478bd9Sstevel@tonic-gate * Check the arguments.
3547c478bd9Sstevel@tonic-gate */
3557c478bd9Sstevel@tonic-gate if(!ef || !path) {
3567c478bd9Sstevel@tonic-gate if(ef) {
3577c478bd9Sstevel@tonic-gate _err_record_msg(ef->err, "ef_expand_file: NULL path argument",
3587c478bd9Sstevel@tonic-gate END_ERR_MSG);
3597c478bd9Sstevel@tonic-gate };
3607c478bd9Sstevel@tonic-gate errno = EINVAL;
3617c478bd9Sstevel@tonic-gate return NULL;
3627c478bd9Sstevel@tonic-gate };
3637c478bd9Sstevel@tonic-gate /*
3647c478bd9Sstevel@tonic-gate * If the caller specified that the whole of path[] be matched,
3657c478bd9Sstevel@tonic-gate * work out the corresponding length.
3667c478bd9Sstevel@tonic-gate */
3677c478bd9Sstevel@tonic-gate if(pathlen < 0 || pathlen > strlen(path))
3687c478bd9Sstevel@tonic-gate pathlen = strlen(path);
3697c478bd9Sstevel@tonic-gate /*
3707c478bd9Sstevel@tonic-gate * Discard previous expansion results.
3717c478bd9Sstevel@tonic-gate */
3727c478bd9Sstevel@tonic-gate ef_clear_files(ef);
3737c478bd9Sstevel@tonic-gate /*
3747c478bd9Sstevel@tonic-gate * Preprocess the path, expanding ~/, ~user/ and $envvar references,
3757c478bd9Sstevel@tonic-gate * using ef->path as a work directory and returning a pointer to
3767c478bd9Sstevel@tonic-gate * a copy of the resulting pattern in the cache.
3777c478bd9Sstevel@tonic-gate */
3787c478bd9Sstevel@tonic-gate path = ef_expand_special(ef, path, pathlen);
3797c478bd9Sstevel@tonic-gate if(!path)
3807c478bd9Sstevel@tonic-gate return NULL;
3817c478bd9Sstevel@tonic-gate /*
3827c478bd9Sstevel@tonic-gate * Clear the pathname buffer.
3837c478bd9Sstevel@tonic-gate */
3847c478bd9Sstevel@tonic-gate _pn_clear_path(ef->path);
3857c478bd9Sstevel@tonic-gate /*
3867c478bd9Sstevel@tonic-gate * Does the pathname contain any wildcards?
3877c478bd9Sstevel@tonic-gate */
3887c478bd9Sstevel@tonic-gate for(wild=0,pptr=path; !wild && *pptr; pptr++) {
3897c478bd9Sstevel@tonic-gate switch(*pptr) {
3907c478bd9Sstevel@tonic-gate case '\\': /* Skip escaped characters */
3917c478bd9Sstevel@tonic-gate if(pptr[1])
3927c478bd9Sstevel@tonic-gate pptr++;
393*55fea89dSDan Cross break;
3947c478bd9Sstevel@tonic-gate case '*': case '?': case '[': /* A wildcard character? */
3957c478bd9Sstevel@tonic-gate wild = 1;
3967c478bd9Sstevel@tonic-gate break;
3977c478bd9Sstevel@tonic-gate };
3987c478bd9Sstevel@tonic-gate };
3997c478bd9Sstevel@tonic-gate /*
4007c478bd9Sstevel@tonic-gate * If there are no wildcards to match, copy the current expanded
4017c478bd9Sstevel@tonic-gate * path into the output array, removing backslash escapes while doing so.
4027c478bd9Sstevel@tonic-gate */
4037c478bd9Sstevel@tonic-gate if(!wild) {
4047c478bd9Sstevel@tonic-gate if(ef_record_pathname(ef, path, 1))
4057c478bd9Sstevel@tonic-gate return NULL;
4067c478bd9Sstevel@tonic-gate /*
4077c478bd9Sstevel@tonic-gate * Does the filename exist?
4087c478bd9Sstevel@tonic-gate */
4097c478bd9Sstevel@tonic-gate ef->result.exists = _pu_file_exists(ef->result.files[0]);
4107c478bd9Sstevel@tonic-gate /*
4117c478bd9Sstevel@tonic-gate * Match wildcards against existing files.
4127c478bd9Sstevel@tonic-gate */
4137c478bd9Sstevel@tonic-gate } else {
4147c478bd9Sstevel@tonic-gate /*
4157c478bd9Sstevel@tonic-gate * Only existing files that match the pattern will be returned in the
4167c478bd9Sstevel@tonic-gate * cache.
4177c478bd9Sstevel@tonic-gate */
4187c478bd9Sstevel@tonic-gate ef->result.exists = 1;
4197c478bd9Sstevel@tonic-gate /*
4207c478bd9Sstevel@tonic-gate * Treat matching of the root-directory as a special case since it
4217c478bd9Sstevel@tonic-gate * isn't contained in a directory.
4227c478bd9Sstevel@tonic-gate */
4237c478bd9Sstevel@tonic-gate if(strcmp(path, FS_ROOT_DIR) == 0) {
4247c478bd9Sstevel@tonic-gate if(ef_record_pathname(ef, FS_ROOT_DIR, 0))
4257c478bd9Sstevel@tonic-gate return NULL;
4267c478bd9Sstevel@tonic-gate } else {
4277c478bd9Sstevel@tonic-gate /*
4287c478bd9Sstevel@tonic-gate * What should the top level directory of the search be?
4297c478bd9Sstevel@tonic-gate */
4307c478bd9Sstevel@tonic-gate if(strncmp(path, FS_ROOT_DIR, FS_ROOT_DIR_LEN) == 0) {
4317c478bd9Sstevel@tonic-gate dirname = FS_ROOT_DIR;
4327c478bd9Sstevel@tonic-gate if(!_pn_append_to_path(ef->path, FS_ROOT_DIR, -1, 0)) {
4337c478bd9Sstevel@tonic-gate _err_record_msg(ef->err, "Insufficient memory to record path",
4347c478bd9Sstevel@tonic-gate END_ERR_MSG);
4357c478bd9Sstevel@tonic-gate return NULL;
4367c478bd9Sstevel@tonic-gate };
4377c478bd9Sstevel@tonic-gate path += FS_ROOT_DIR_LEN;
4387c478bd9Sstevel@tonic-gate } else {
4397c478bd9Sstevel@tonic-gate dirname = FS_PWD;
4407c478bd9Sstevel@tonic-gate };
4417c478bd9Sstevel@tonic-gate /*
4427c478bd9Sstevel@tonic-gate * Open the top-level directory of the search.
4437c478bd9Sstevel@tonic-gate */
4447c478bd9Sstevel@tonic-gate dnode = ef_open_dir(ef, dirname);
4457c478bd9Sstevel@tonic-gate if(!dnode)
4467c478bd9Sstevel@tonic-gate return NULL;
4477c478bd9Sstevel@tonic-gate /*
4487c478bd9Sstevel@tonic-gate * Recursively match successive directory components of the path.
4497c478bd9Sstevel@tonic-gate */
4507c478bd9Sstevel@tonic-gate if(ef_match_relative_pathname(ef, dnode->dr, path, 0)) {
4517c478bd9Sstevel@tonic-gate dnode = ef_close_dir(ef, dnode);
4527c478bd9Sstevel@tonic-gate return NULL;
4537c478bd9Sstevel@tonic-gate };
4547c478bd9Sstevel@tonic-gate /*
4557c478bd9Sstevel@tonic-gate * Cleanup.
4567c478bd9Sstevel@tonic-gate */
4577c478bd9Sstevel@tonic-gate dnode = ef_close_dir(ef, dnode);
4587c478bd9Sstevel@tonic-gate };
4597c478bd9Sstevel@tonic-gate /*
4607c478bd9Sstevel@tonic-gate * No files matched?
4617c478bd9Sstevel@tonic-gate */
4627c478bd9Sstevel@tonic-gate if(ef->result.nfile < 1) {
4637c478bd9Sstevel@tonic-gate _err_record_msg(ef->err, "No files match", END_ERR_MSG);
4647c478bd9Sstevel@tonic-gate return NULL;
4657c478bd9Sstevel@tonic-gate };
4667c478bd9Sstevel@tonic-gate /*
4677c478bd9Sstevel@tonic-gate * Sort the pathnames that matched.
4687c478bd9Sstevel@tonic-gate */
4697c478bd9Sstevel@tonic-gate qsort(ef->result.files, ef->result.nfile, sizeof(ef->result.files[0]),
4707c478bd9Sstevel@tonic-gate ef_cmp_strings);
4717c478bd9Sstevel@tonic-gate };
4727c478bd9Sstevel@tonic-gate /*
4737c478bd9Sstevel@tonic-gate * Return the result container.
4747c478bd9Sstevel@tonic-gate */
4757c478bd9Sstevel@tonic-gate return &ef->result;
4767c478bd9Sstevel@tonic-gate }
4777c478bd9Sstevel@tonic-gate
4787c478bd9Sstevel@tonic-gate /*.......................................................................
4797c478bd9Sstevel@tonic-gate * Attempt to recursively match the given pattern with the contents of
4807c478bd9Sstevel@tonic-gate * the current directory, descending sub-directories as needed.
4817c478bd9Sstevel@tonic-gate *
4827c478bd9Sstevel@tonic-gate * Input:
4837c478bd9Sstevel@tonic-gate * ef ExpandFile * The pathname expansion resource object.
4847c478bd9Sstevel@tonic-gate * dr DirReader * The directory reader object of the directory
4857c478bd9Sstevel@tonic-gate * to be searched.
4867c478bd9Sstevel@tonic-gate * pattern const char * The pattern to match with files in the current
4877c478bd9Sstevel@tonic-gate * directory.
4887c478bd9Sstevel@tonic-gate * separate int When appending a filename from the specified
4897c478bd9Sstevel@tonic-gate * directory to ef->pathname, insert a directory
4907c478bd9Sstevel@tonic-gate * separator between the existing pathname and
4917c478bd9Sstevel@tonic-gate * the filename, unless separate is zero.
4927c478bd9Sstevel@tonic-gate * Output:
4937c478bd9Sstevel@tonic-gate * return int 0 - OK.
4947c478bd9Sstevel@tonic-gate * 1 - Error.
4957c478bd9Sstevel@tonic-gate */
ef_match_relative_pathname(ExpandFile * ef,DirReader * dr,const char * pattern,int separate)4967c478bd9Sstevel@tonic-gate static int ef_match_relative_pathname(ExpandFile *ef, DirReader *dr,
4977c478bd9Sstevel@tonic-gate const char *pattern, int separate)
4987c478bd9Sstevel@tonic-gate {
4997c478bd9Sstevel@tonic-gate const char *nextp; /* The pointer to the character that follows the part */
5007c478bd9Sstevel@tonic-gate /* of the pattern that is to be matched with files */
5017c478bd9Sstevel@tonic-gate /* in the current directory. */
5027c478bd9Sstevel@tonic-gate char *file; /* The name of the file being matched */
5037c478bd9Sstevel@tonic-gate int pathlen; /* The length of ef->pathname[] on entry to this */
5047c478bd9Sstevel@tonic-gate /* function */
5057c478bd9Sstevel@tonic-gate /*
5067c478bd9Sstevel@tonic-gate * Record the current length of the pathname string recorded in
5077c478bd9Sstevel@tonic-gate * ef->pathname[].
5087c478bd9Sstevel@tonic-gate */
5097c478bd9Sstevel@tonic-gate pathlen = strlen(ef->path->name);
5107c478bd9Sstevel@tonic-gate /*
5117c478bd9Sstevel@tonic-gate * Get a pointer to the character that follows the end of the part of
5127c478bd9Sstevel@tonic-gate * the pattern that should be matched to files within the current directory.
5137c478bd9Sstevel@tonic-gate * This will either point to a directory separator, or to the '\0' terminator
5147c478bd9Sstevel@tonic-gate * of the pattern string.
5157c478bd9Sstevel@tonic-gate */
5167c478bd9Sstevel@tonic-gate for(nextp=pattern; *nextp && strncmp(nextp, FS_DIR_SEP, FS_DIR_SEP_LEN) != 0;
5177c478bd9Sstevel@tonic-gate nextp++)
5187c478bd9Sstevel@tonic-gate ;
5197c478bd9Sstevel@tonic-gate /*
5207c478bd9Sstevel@tonic-gate * Read each file from the directory, attempting to match it to the
5217c478bd9Sstevel@tonic-gate * current pattern.
5227c478bd9Sstevel@tonic-gate */
5237c478bd9Sstevel@tonic-gate while((file=_dr_next_file(dr)) != NULL) {
5247c478bd9Sstevel@tonic-gate /*
5257c478bd9Sstevel@tonic-gate * Does the latest file match the pattern up to nextp?
5267c478bd9Sstevel@tonic-gate */
5277c478bd9Sstevel@tonic-gate if(ef_string_matches_pattern(file, pattern, file[0]=='.', nextp)) {
5287c478bd9Sstevel@tonic-gate /*
5297c478bd9Sstevel@tonic-gate * Append the new directory entry to the current matching pathname.
5307c478bd9Sstevel@tonic-gate */
5317c478bd9Sstevel@tonic-gate if((separate && _pn_append_to_path(ef->path, FS_DIR_SEP, -1, 0)==NULL) ||
5327c478bd9Sstevel@tonic-gate _pn_append_to_path(ef->path, file, -1, 0)==NULL) {
5337c478bd9Sstevel@tonic-gate _err_record_msg(ef->err, "Insufficient memory to record path",
5347c478bd9Sstevel@tonic-gate END_ERR_MSG);
5357c478bd9Sstevel@tonic-gate return 1;
5367c478bd9Sstevel@tonic-gate };
5377c478bd9Sstevel@tonic-gate /*
5387c478bd9Sstevel@tonic-gate * If we have reached the end of the pattern, record the accumulated
5397c478bd9Sstevel@tonic-gate * pathname in the list of matching files.
5407c478bd9Sstevel@tonic-gate */
5417c478bd9Sstevel@tonic-gate if(*nextp == '\0') {
5427c478bd9Sstevel@tonic-gate if(ef_record_pathname(ef, ef->path->name, 0))
5437c478bd9Sstevel@tonic-gate return 1;
5447c478bd9Sstevel@tonic-gate /*
5457c478bd9Sstevel@tonic-gate * If the matching directory entry is a subdirectory, and the
5467c478bd9Sstevel@tonic-gate * next character of the pattern is a directory separator,
5477c478bd9Sstevel@tonic-gate * recursively call the current function to scan the sub-directory
5487c478bd9Sstevel@tonic-gate * for matches.
5497c478bd9Sstevel@tonic-gate */
5507c478bd9Sstevel@tonic-gate } else if(_pu_path_is_dir(ef->path->name) &&
5517c478bd9Sstevel@tonic-gate strncmp(nextp, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) {
5527c478bd9Sstevel@tonic-gate /*
5537c478bd9Sstevel@tonic-gate * If the pattern finishes with the directory separator, then
5547c478bd9Sstevel@tonic-gate * record the pathame as matching.
5557c478bd9Sstevel@tonic-gate */
5567c478bd9Sstevel@tonic-gate if(nextp[FS_DIR_SEP_LEN] == '\0') {
5577c478bd9Sstevel@tonic-gate if(ef_record_pathname(ef, ef->path->name, 0))
5587c478bd9Sstevel@tonic-gate return 1;
5597c478bd9Sstevel@tonic-gate /*
5607c478bd9Sstevel@tonic-gate * Match files within the directory.
5617c478bd9Sstevel@tonic-gate */
5627c478bd9Sstevel@tonic-gate } else {
5637c478bd9Sstevel@tonic-gate DirNode *subdnode = ef_open_dir(ef, ef->path->name);
5647c478bd9Sstevel@tonic-gate if(subdnode) {
5657c478bd9Sstevel@tonic-gate if(ef_match_relative_pathname(ef, subdnode->dr,
5667c478bd9Sstevel@tonic-gate nextp+FS_DIR_SEP_LEN, 1)) {
5677c478bd9Sstevel@tonic-gate subdnode = ef_close_dir(ef, subdnode);
5687c478bd9Sstevel@tonic-gate return 1;
5697c478bd9Sstevel@tonic-gate };
5707c478bd9Sstevel@tonic-gate subdnode = ef_close_dir(ef, subdnode);
5717c478bd9Sstevel@tonic-gate };
5727c478bd9Sstevel@tonic-gate };
5737c478bd9Sstevel@tonic-gate };
5747c478bd9Sstevel@tonic-gate /*
5757c478bd9Sstevel@tonic-gate * Remove the latest filename from the pathname string, so that
5767c478bd9Sstevel@tonic-gate * another matching file can be appended.
5777c478bd9Sstevel@tonic-gate */
5787c478bd9Sstevel@tonic-gate ef->path->name[pathlen] = '\0';
5797c478bd9Sstevel@tonic-gate };
5807c478bd9Sstevel@tonic-gate };
5817c478bd9Sstevel@tonic-gate return 0;
5827c478bd9Sstevel@tonic-gate }
5837c478bd9Sstevel@tonic-gate
5847c478bd9Sstevel@tonic-gate /*.......................................................................
5857c478bd9Sstevel@tonic-gate * Record a new matching filename.
5867c478bd9Sstevel@tonic-gate *
5877c478bd9Sstevel@tonic-gate * Input:
5887c478bd9Sstevel@tonic-gate * ef ExpandFile * The filename-match resource object.
5897c478bd9Sstevel@tonic-gate * pathname const char * The pathname to record.
5907c478bd9Sstevel@tonic-gate * remove_escapes int If true, remove backslash escapes in the
5917c478bd9Sstevel@tonic-gate * recorded copy of the pathname.
5927c478bd9Sstevel@tonic-gate * Output:
5937c478bd9Sstevel@tonic-gate * return int 0 - OK.
5947c478bd9Sstevel@tonic-gate * 1 - Error (ef->err will contain a
5957c478bd9Sstevel@tonic-gate * description of the error).
5967c478bd9Sstevel@tonic-gate */
ef_record_pathname(ExpandFile * ef,const char * pathname,int remove_escapes)5977c478bd9Sstevel@tonic-gate static int ef_record_pathname(ExpandFile *ef, const char *pathname,
5987c478bd9Sstevel@tonic-gate int remove_escapes)
5997c478bd9Sstevel@tonic-gate {
6007c478bd9Sstevel@tonic-gate char *copy; /* The recorded copy of pathname[] */
6017c478bd9Sstevel@tonic-gate /*
6027c478bd9Sstevel@tonic-gate * Attempt to make a copy of the pathname in the cache.
6037c478bd9Sstevel@tonic-gate */
6047c478bd9Sstevel@tonic-gate copy = ef_cache_pathname(ef, pathname, remove_escapes);
6057c478bd9Sstevel@tonic-gate if(!copy)
6067c478bd9Sstevel@tonic-gate return 1;
6077c478bd9Sstevel@tonic-gate /*
6087c478bd9Sstevel@tonic-gate * If there isn't room to record a pointer to the recorded pathname in the
6097c478bd9Sstevel@tonic-gate * array of files, attempt to extend the array.
6107c478bd9Sstevel@tonic-gate */
6117c478bd9Sstevel@tonic-gate if(ef->result.nfile + 1 > ef->files_dim) {
6127c478bd9Sstevel@tonic-gate int files_dim = ef->files_dim + MATCH_BLK_FACT;
6137c478bd9Sstevel@tonic-gate char **files = (char **) realloc(ef->result.files,
6147c478bd9Sstevel@tonic-gate files_dim * sizeof(files[0]));
6157c478bd9Sstevel@tonic-gate if(!files) {
6167c478bd9Sstevel@tonic-gate _err_record_msg(ef->err,
6177c478bd9Sstevel@tonic-gate "Insufficient memory to record all of the matching filenames",
6187c478bd9Sstevel@tonic-gate END_ERR_MSG);
6197c478bd9Sstevel@tonic-gate errno = ENOMEM;
6207c478bd9Sstevel@tonic-gate return 1;
6217c478bd9Sstevel@tonic-gate };
6227c478bd9Sstevel@tonic-gate ef->result.files = files;
6237c478bd9Sstevel@tonic-gate ef->files_dim = files_dim;
6247c478bd9Sstevel@tonic-gate };
6257c478bd9Sstevel@tonic-gate /*
6267c478bd9Sstevel@tonic-gate * Record a pointer to the new match.
6277c478bd9Sstevel@tonic-gate */
6287c478bd9Sstevel@tonic-gate ef->result.files[ef->result.nfile++] = copy;
6297c478bd9Sstevel@tonic-gate return 0;
6307c478bd9Sstevel@tonic-gate }
6317c478bd9Sstevel@tonic-gate
6327c478bd9Sstevel@tonic-gate /*.......................................................................
6337c478bd9Sstevel@tonic-gate * Record a pathname in the cache.
6347c478bd9Sstevel@tonic-gate *
6357c478bd9Sstevel@tonic-gate * Input:
6367c478bd9Sstevel@tonic-gate * ef ExpandFile * The filename-match resource object.
6377c478bd9Sstevel@tonic-gate * pathname char * The pathname to record.
6387c478bd9Sstevel@tonic-gate * remove_escapes int If true, remove backslash escapes in the
6397c478bd9Sstevel@tonic-gate * copy of the pathname.
6407c478bd9Sstevel@tonic-gate * Output:
6417c478bd9Sstevel@tonic-gate * return char * The pointer to the copy of the pathname.
6427c478bd9Sstevel@tonic-gate * On error NULL is returned and a description
6437c478bd9Sstevel@tonic-gate * of the error is left in ef->err.
6447c478bd9Sstevel@tonic-gate */
ef_cache_pathname(ExpandFile * ef,const char * pathname,int remove_escapes)6457c478bd9Sstevel@tonic-gate static char *ef_cache_pathname(ExpandFile *ef, const char *pathname,
6467c478bd9Sstevel@tonic-gate int remove_escapes)
6477c478bd9Sstevel@tonic-gate {
6487c478bd9Sstevel@tonic-gate char *copy = _sg_store_string(ef->sg, pathname, remove_escapes);
6497c478bd9Sstevel@tonic-gate if(!copy)
6507c478bd9Sstevel@tonic-gate _err_record_msg(ef->err, "Insufficient memory to store pathname",
6517c478bd9Sstevel@tonic-gate END_ERR_MSG);
6527c478bd9Sstevel@tonic-gate return copy;
6537c478bd9Sstevel@tonic-gate }
6547c478bd9Sstevel@tonic-gate
6557c478bd9Sstevel@tonic-gate /*.......................................................................
6567c478bd9Sstevel@tonic-gate * Clear the results of the previous expansion operation, ready for the
6577c478bd9Sstevel@tonic-gate * next.
6587c478bd9Sstevel@tonic-gate *
6597c478bd9Sstevel@tonic-gate * Input:
6607c478bd9Sstevel@tonic-gate * ef ExpandFile * The pathname expansion resource object.
6617c478bd9Sstevel@tonic-gate */
ef_clear_files(ExpandFile * ef)6627c478bd9Sstevel@tonic-gate static void ef_clear_files(ExpandFile *ef)
6637c478bd9Sstevel@tonic-gate {
6647c478bd9Sstevel@tonic-gate _clr_StringGroup(ef->sg);
6657c478bd9Sstevel@tonic-gate _pn_clear_path(ef->path);
6667c478bd9Sstevel@tonic-gate ef->result.exists = 0;
6677c478bd9Sstevel@tonic-gate ef->result.nfile = 0;
6687c478bd9Sstevel@tonic-gate _err_clear_msg(ef->err);
6697c478bd9Sstevel@tonic-gate return;
6707c478bd9Sstevel@tonic-gate }
6717c478bd9Sstevel@tonic-gate
6727c478bd9Sstevel@tonic-gate /*.......................................................................
6737c478bd9Sstevel@tonic-gate * Get a new directory reader object from the cache.
6747c478bd9Sstevel@tonic-gate *
6757c478bd9Sstevel@tonic-gate * Input:
6767c478bd9Sstevel@tonic-gate * ef ExpandFile * The pathname expansion resource object.
6777c478bd9Sstevel@tonic-gate * pathname const char * The pathname of the directory.
6787c478bd9Sstevel@tonic-gate * Output:
6797c478bd9Sstevel@tonic-gate * return DirNode * The cache entry of the new directory reader,
6807c478bd9Sstevel@tonic-gate * or NULL on error. On error, ef->err will
6817c478bd9Sstevel@tonic-gate * contain a description of the error.
6827c478bd9Sstevel@tonic-gate */
ef_open_dir(ExpandFile * ef,const char * pathname)6837c478bd9Sstevel@tonic-gate static DirNode *ef_open_dir(ExpandFile *ef, const char *pathname)
6847c478bd9Sstevel@tonic-gate {
6857c478bd9Sstevel@tonic-gate char *errmsg = NULL; /* An error message from a called function */
6867c478bd9Sstevel@tonic-gate DirNode *node; /* The cache node used */
6877c478bd9Sstevel@tonic-gate /*
6887c478bd9Sstevel@tonic-gate * Get the directory reader cache.
6897c478bd9Sstevel@tonic-gate */
6907c478bd9Sstevel@tonic-gate DirCache *cache = &ef->cache;
6917c478bd9Sstevel@tonic-gate /*
6927c478bd9Sstevel@tonic-gate * Extend the cache if there are no free cache nodes.
6937c478bd9Sstevel@tonic-gate */
6947c478bd9Sstevel@tonic-gate if(!cache->next) {
6957c478bd9Sstevel@tonic-gate node = (DirNode *) _new_FreeListNode(cache->mem);
6967c478bd9Sstevel@tonic-gate if(!node) {
6977c478bd9Sstevel@tonic-gate _err_record_msg(ef->err, "Insufficient memory to open a new directory",
6987c478bd9Sstevel@tonic-gate END_ERR_MSG);
6997c478bd9Sstevel@tonic-gate return NULL;
7007c478bd9Sstevel@tonic-gate };
7017c478bd9Sstevel@tonic-gate /*
7027c478bd9Sstevel@tonic-gate * Initialize the cache node.
7037c478bd9Sstevel@tonic-gate */
7047c478bd9Sstevel@tonic-gate node->next = NULL;
7057c478bd9Sstevel@tonic-gate node->prev = NULL;
7067c478bd9Sstevel@tonic-gate node->dr = NULL;
7077c478bd9Sstevel@tonic-gate /*
7087c478bd9Sstevel@tonic-gate * Allocate a directory reader object.
7097c478bd9Sstevel@tonic-gate */
7107c478bd9Sstevel@tonic-gate node->dr = _new_DirReader();
7117c478bd9Sstevel@tonic-gate if(!node->dr) {
7127c478bd9Sstevel@tonic-gate _err_record_msg(ef->err, "Insufficient memory to open a new directory",
7137c478bd9Sstevel@tonic-gate END_ERR_MSG);
7147c478bd9Sstevel@tonic-gate node = (DirNode *) _del_FreeListNode(cache->mem, node);
7157c478bd9Sstevel@tonic-gate return NULL;
7167c478bd9Sstevel@tonic-gate };
7177c478bd9Sstevel@tonic-gate /*
7187c478bd9Sstevel@tonic-gate * Append the node to the cache list.
7197c478bd9Sstevel@tonic-gate */
7207c478bd9Sstevel@tonic-gate node->prev = cache->tail;
7217c478bd9Sstevel@tonic-gate if(cache->tail)
7227c478bd9Sstevel@tonic-gate cache->tail->next = node;
7237c478bd9Sstevel@tonic-gate else
7247c478bd9Sstevel@tonic-gate cache->head = node;
7257c478bd9Sstevel@tonic-gate cache->next = cache->tail = node;
7267c478bd9Sstevel@tonic-gate };
7277c478bd9Sstevel@tonic-gate /*
7287c478bd9Sstevel@tonic-gate * Get the first unused node, but don't remove it from the list yet.
7297c478bd9Sstevel@tonic-gate */
7307c478bd9Sstevel@tonic-gate node = cache->next;
7317c478bd9Sstevel@tonic-gate /*
7327c478bd9Sstevel@tonic-gate * Attempt to open the specified directory.
7337c478bd9Sstevel@tonic-gate */
7347c478bd9Sstevel@tonic-gate if(_dr_open_dir(node->dr, pathname, &errmsg)) {
7357c478bd9Sstevel@tonic-gate _err_record_msg(ef->err, errmsg, END_ERR_MSG);
7367c478bd9Sstevel@tonic-gate return NULL;
7377c478bd9Sstevel@tonic-gate };
7387c478bd9Sstevel@tonic-gate /*
7397c478bd9Sstevel@tonic-gate * Now that we have successfully opened the specified directory,
7407c478bd9Sstevel@tonic-gate * remove the cache node from the list, and relink the list around it.
7417c478bd9Sstevel@tonic-gate */
7427c478bd9Sstevel@tonic-gate cache->next = node->next;
7437c478bd9Sstevel@tonic-gate if(node->prev)
7447c478bd9Sstevel@tonic-gate node->prev->next = node->next;
7457c478bd9Sstevel@tonic-gate else
7467c478bd9Sstevel@tonic-gate cache->head = node->next;
7477c478bd9Sstevel@tonic-gate if(node->next)
7487c478bd9Sstevel@tonic-gate node->next->prev = node->prev;
7497c478bd9Sstevel@tonic-gate else
7507c478bd9Sstevel@tonic-gate cache->tail = node->prev;
7517c478bd9Sstevel@tonic-gate node->next = node->prev = NULL;
7527c478bd9Sstevel@tonic-gate /*
7537c478bd9Sstevel@tonic-gate * Return the successfully initialized cache node to the caller.
7547c478bd9Sstevel@tonic-gate */
7557c478bd9Sstevel@tonic-gate return node;
7567c478bd9Sstevel@tonic-gate }
7577c478bd9Sstevel@tonic-gate
7587c478bd9Sstevel@tonic-gate /*.......................................................................
7597c478bd9Sstevel@tonic-gate * Return a directory reader object to the cache, after first closing
7607c478bd9Sstevel@tonic-gate * the directory that it was managing.
7617c478bd9Sstevel@tonic-gate *
7627c478bd9Sstevel@tonic-gate * Input:
7637c478bd9Sstevel@tonic-gate * ef ExpandFile * The pathname expansion resource object.
7647c478bd9Sstevel@tonic-gate * node DirNode * The cache entry of the directory reader, as returned
7657c478bd9Sstevel@tonic-gate * by ef_open_dir().
7667c478bd9Sstevel@tonic-gate * Output:
7677c478bd9Sstevel@tonic-gate * return DirNode * The deleted DirNode (ie. allways NULL).
7687c478bd9Sstevel@tonic-gate */
ef_close_dir(ExpandFile * ef,DirNode * node)7697c478bd9Sstevel@tonic-gate static DirNode *ef_close_dir(ExpandFile *ef, DirNode *node)
7707c478bd9Sstevel@tonic-gate {
7717c478bd9Sstevel@tonic-gate /*
7727c478bd9Sstevel@tonic-gate * Get the directory reader cache.
7737c478bd9Sstevel@tonic-gate */
7747c478bd9Sstevel@tonic-gate DirCache *cache = &ef->cache;
7757c478bd9Sstevel@tonic-gate /*
7767c478bd9Sstevel@tonic-gate * Close the directory.
7777c478bd9Sstevel@tonic-gate */
7787c478bd9Sstevel@tonic-gate _dr_close_dir(node->dr);
7797c478bd9Sstevel@tonic-gate /*
7807c478bd9Sstevel@tonic-gate * Return the node to the tail of the cache list.
7817c478bd9Sstevel@tonic-gate */
7827c478bd9Sstevel@tonic-gate node->next = NULL;
7837c478bd9Sstevel@tonic-gate node->prev = cache->tail;
7847c478bd9Sstevel@tonic-gate if(cache->tail)
7857c478bd9Sstevel@tonic-gate cache->tail->next = node;
7867c478bd9Sstevel@tonic-gate else
7877c478bd9Sstevel@tonic-gate cache->head = cache->tail = node;
7887c478bd9Sstevel@tonic-gate if(!cache->next)
7897c478bd9Sstevel@tonic-gate cache->next = node;
7907c478bd9Sstevel@tonic-gate return NULL;
7917c478bd9Sstevel@tonic-gate }
7927c478bd9Sstevel@tonic-gate
7937c478bd9Sstevel@tonic-gate /*.......................................................................
7947c478bd9Sstevel@tonic-gate * Return non-zero if the specified file name matches a given glob
7957c478bd9Sstevel@tonic-gate * pattern.
7967c478bd9Sstevel@tonic-gate *
7977c478bd9Sstevel@tonic-gate * Input:
7987c478bd9Sstevel@tonic-gate * file const char * The file-name component to be matched to the pattern.
7997c478bd9Sstevel@tonic-gate * pattern const char * The start of the pattern to match against file[].
8007c478bd9Sstevel@tonic-gate * xplicit int If non-zero, the first character must be matched
8017c478bd9Sstevel@tonic-gate * explicitly (ie. not with a wildcard).
8027c478bd9Sstevel@tonic-gate * nextp const char * The pointer to the the character following the
8037c478bd9Sstevel@tonic-gate * end of the pattern in pattern[].
8047c478bd9Sstevel@tonic-gate * Output:
8057c478bd9Sstevel@tonic-gate * return int 0 - Doesn't match.
8067c478bd9Sstevel@tonic-gate * 1 - The file-name string matches the pattern.
8077c478bd9Sstevel@tonic-gate */
ef_string_matches_pattern(const char * file,const char * pattern,int xplicit,const char * nextp)8087c478bd9Sstevel@tonic-gate static int ef_string_matches_pattern(const char *file, const char *pattern,
8097c478bd9Sstevel@tonic-gate int xplicit, const char *nextp)
8107c478bd9Sstevel@tonic-gate {
8117c478bd9Sstevel@tonic-gate const char *pptr = pattern; /* The pointer used to scan the pattern */
8127c478bd9Sstevel@tonic-gate const char *fptr = file; /* The pointer used to scan the filename string */
8137c478bd9Sstevel@tonic-gate /*
8147c478bd9Sstevel@tonic-gate * Match each character of the pattern in turn.
8157c478bd9Sstevel@tonic-gate */
8167c478bd9Sstevel@tonic-gate while(pptr < nextp) {
8177c478bd9Sstevel@tonic-gate /*
8187c478bd9Sstevel@tonic-gate * Handle the next character of the pattern.
8197c478bd9Sstevel@tonic-gate */
8207c478bd9Sstevel@tonic-gate switch(*pptr) {
8217c478bd9Sstevel@tonic-gate /*
8227c478bd9Sstevel@tonic-gate * A match zero-or-more characters wildcard operator.
8237c478bd9Sstevel@tonic-gate */
8247c478bd9Sstevel@tonic-gate case '*':
8257c478bd9Sstevel@tonic-gate /*
8267c478bd9Sstevel@tonic-gate * Skip the '*' character in the pattern.
8277c478bd9Sstevel@tonic-gate */
8287c478bd9Sstevel@tonic-gate pptr++;
8297c478bd9Sstevel@tonic-gate /*
8307c478bd9Sstevel@tonic-gate * If wildcards aren't allowed, the pattern doesn't match.
8317c478bd9Sstevel@tonic-gate */
8327c478bd9Sstevel@tonic-gate if(xplicit)
8337c478bd9Sstevel@tonic-gate return 0;
8347c478bd9Sstevel@tonic-gate /*
8357c478bd9Sstevel@tonic-gate * If the pattern ends with a the '*' wildcard, then the
8367c478bd9Sstevel@tonic-gate * rest of the filename matches this.
8377c478bd9Sstevel@tonic-gate */
8387c478bd9Sstevel@tonic-gate if(pptr >= nextp)
8397c478bd9Sstevel@tonic-gate return 1;
8407c478bd9Sstevel@tonic-gate /*
8417c478bd9Sstevel@tonic-gate * Using the wildcard to match successively longer sections of
8427c478bd9Sstevel@tonic-gate * the remaining characters of the filename, attempt to match
8437c478bd9Sstevel@tonic-gate * the tail of the filename against the tail of the pattern.
8447c478bd9Sstevel@tonic-gate */
8457c478bd9Sstevel@tonic-gate for( ; *fptr; fptr++) {
8467c478bd9Sstevel@tonic-gate if(ef_string_matches_pattern(fptr, pptr, 0, nextp))
8477c478bd9Sstevel@tonic-gate return 1;
8487c478bd9Sstevel@tonic-gate };
8497c478bd9Sstevel@tonic-gate return 0; /* The pattern following the '*' didn't match */
8507c478bd9Sstevel@tonic-gate break;
8517c478bd9Sstevel@tonic-gate /*
8527c478bd9Sstevel@tonic-gate * A match-one-character wildcard operator.
8537c478bd9Sstevel@tonic-gate */
8547c478bd9Sstevel@tonic-gate case '?':
8557c478bd9Sstevel@tonic-gate /*
8567c478bd9Sstevel@tonic-gate * If there is a character to be matched, skip it and advance the
8577c478bd9Sstevel@tonic-gate * pattern pointer.
8587c478bd9Sstevel@tonic-gate */
8597c478bd9Sstevel@tonic-gate if(!xplicit && *fptr) {
8607c478bd9Sstevel@tonic-gate fptr++;
8617c478bd9Sstevel@tonic-gate pptr++;
8627c478bd9Sstevel@tonic-gate /*
8637c478bd9Sstevel@tonic-gate * If we hit the end of the filename string, there is no character
8647c478bd9Sstevel@tonic-gate * matching the operator, so the string doesn't match.
8657c478bd9Sstevel@tonic-gate */
8667c478bd9Sstevel@tonic-gate } else {
8677c478bd9Sstevel@tonic-gate return 0;
8687c478bd9Sstevel@tonic-gate };
8697c478bd9Sstevel@tonic-gate break;
8707c478bd9Sstevel@tonic-gate /*
8717c478bd9Sstevel@tonic-gate * A character range operator, with the character ranges enclosed
8727c478bd9Sstevel@tonic-gate * in matching square brackets.
8737c478bd9Sstevel@tonic-gate */
8747c478bd9Sstevel@tonic-gate case '[':
8757c478bd9Sstevel@tonic-gate if(xplicit || !ef_matches_range(*fptr++, ++pptr, &pptr))
8767c478bd9Sstevel@tonic-gate return 0;
8777c478bd9Sstevel@tonic-gate break;
8787c478bd9Sstevel@tonic-gate /*
8797c478bd9Sstevel@tonic-gate * A backslash in the pattern prevents the following character as
8807c478bd9Sstevel@tonic-gate * being seen as a special character.
8817c478bd9Sstevel@tonic-gate */
8827c478bd9Sstevel@tonic-gate case '\\':
8837c478bd9Sstevel@tonic-gate pptr++;
8847c478bd9Sstevel@tonic-gate /* Note fallthrough to default */
8857c478bd9Sstevel@tonic-gate /*
8867c478bd9Sstevel@tonic-gate * A normal character to be matched explicitly.
8877c478bd9Sstevel@tonic-gate */
888cf421650SToomas Soome /* FALLTHROUGH */
8897c478bd9Sstevel@tonic-gate default:
8907c478bd9Sstevel@tonic-gate if(*fptr == *pptr) {
8917c478bd9Sstevel@tonic-gate fptr++;
8927c478bd9Sstevel@tonic-gate pptr++;
8937c478bd9Sstevel@tonic-gate } else {
8947c478bd9Sstevel@tonic-gate return 0;
8957c478bd9Sstevel@tonic-gate };
8967c478bd9Sstevel@tonic-gate break;
8977c478bd9Sstevel@tonic-gate };
8987c478bd9Sstevel@tonic-gate /*
8997c478bd9Sstevel@tonic-gate * After passing the first character, turn off the explicit match
9007c478bd9Sstevel@tonic-gate * requirement.
9017c478bd9Sstevel@tonic-gate */
9027c478bd9Sstevel@tonic-gate xplicit = 0;
9037c478bd9Sstevel@tonic-gate };
9047c478bd9Sstevel@tonic-gate /*
9057c478bd9Sstevel@tonic-gate * To get here the pattern must have been exhausted. If the filename
9067c478bd9Sstevel@tonic-gate * string matched, then the filename string must also have been
9077c478bd9Sstevel@tonic-gate * exhausted.
9087c478bd9Sstevel@tonic-gate */
9097c478bd9Sstevel@tonic-gate return *fptr == '\0';
9107c478bd9Sstevel@tonic-gate }
9117c478bd9Sstevel@tonic-gate
9127c478bd9Sstevel@tonic-gate /*.......................................................................
9137c478bd9Sstevel@tonic-gate * Match a character range expression terminated by an unescaped close
9147c478bd9Sstevel@tonic-gate * square bracket.
9157c478bd9Sstevel@tonic-gate *
9167c478bd9Sstevel@tonic-gate * Input:
9177c478bd9Sstevel@tonic-gate * c int The character to be matched with the range
9187c478bd9Sstevel@tonic-gate * pattern.
9197c478bd9Sstevel@tonic-gate * pattern const char * The range pattern to be matched (ie. after the
9207c478bd9Sstevel@tonic-gate * initiating '[' character).
9217c478bd9Sstevel@tonic-gate * endp const char ** On output a pointer to the character following the
9227c478bd9Sstevel@tonic-gate * range expression will be assigned to *endp.
9237c478bd9Sstevel@tonic-gate * Output:
9247c478bd9Sstevel@tonic-gate * return int 0 - Doesn't match.
9257c478bd9Sstevel@tonic-gate * 1 - The character matched.
9267c478bd9Sstevel@tonic-gate */
ef_matches_range(int c,const char * pattern,const char ** endp)9277c478bd9Sstevel@tonic-gate static int ef_matches_range(int c, const char *pattern, const char **endp)
9287c478bd9Sstevel@tonic-gate {
9297c478bd9Sstevel@tonic-gate const char *pptr = pattern; /* The pointer used to scan the pattern */
9307c478bd9Sstevel@tonic-gate int invert = 0; /* True to invert the sense of the match */
9317c478bd9Sstevel@tonic-gate int matched = 0; /* True if the character matched the pattern */
9327c478bd9Sstevel@tonic-gate /*
9337c478bd9Sstevel@tonic-gate * If the first character is a caret, the sense of the match is
9347c478bd9Sstevel@tonic-gate * inverted and only if the character isn't one of those in the
9357c478bd9Sstevel@tonic-gate * range, do we say that it matches.
9367c478bd9Sstevel@tonic-gate */
9377c478bd9Sstevel@tonic-gate if(*pptr == '^') {
9387c478bd9Sstevel@tonic-gate pptr++;
9397c478bd9Sstevel@tonic-gate invert = 1;
9407c478bd9Sstevel@tonic-gate };
9417c478bd9Sstevel@tonic-gate /*
9427c478bd9Sstevel@tonic-gate * The hyphen is only a special character when it follows the first
9437c478bd9Sstevel@tonic-gate * character of the range (not including the caret).
9447c478bd9Sstevel@tonic-gate */
9457c478bd9Sstevel@tonic-gate if(*pptr == '-') {
9467c478bd9Sstevel@tonic-gate pptr++;
9477c478bd9Sstevel@tonic-gate if(c == '-') {
9487c478bd9Sstevel@tonic-gate *endp = pptr;
9497c478bd9Sstevel@tonic-gate matched = 1;
9507c478bd9Sstevel@tonic-gate };
9517c478bd9Sstevel@tonic-gate /*
9527c478bd9Sstevel@tonic-gate * Skip other leading '-' characters since they make no sense.
9537c478bd9Sstevel@tonic-gate */
9547c478bd9Sstevel@tonic-gate while(*pptr == '-')
9557c478bd9Sstevel@tonic-gate pptr++;
9567c478bd9Sstevel@tonic-gate };
9577c478bd9Sstevel@tonic-gate /*
9587c478bd9Sstevel@tonic-gate * The hyphen is only a special character when it follows the first
9597c478bd9Sstevel@tonic-gate * character of the range (not including the caret or a hyphen).
9607c478bd9Sstevel@tonic-gate */
9617c478bd9Sstevel@tonic-gate if(*pptr == ']') {
9627c478bd9Sstevel@tonic-gate pptr++;
9637c478bd9Sstevel@tonic-gate if(c == ']') {
9647c478bd9Sstevel@tonic-gate *endp = pptr;
9657c478bd9Sstevel@tonic-gate matched = 1;
9667c478bd9Sstevel@tonic-gate };
9677c478bd9Sstevel@tonic-gate };
9687c478bd9Sstevel@tonic-gate /*
9697c478bd9Sstevel@tonic-gate * Having dealt with the characters that have special meanings at
9707c478bd9Sstevel@tonic-gate * the beginning of a character range expression, see if the
9717c478bd9Sstevel@tonic-gate * character matches any of the remaining characters of the range,
9727c478bd9Sstevel@tonic-gate * up until a terminating ']' character is seen.
9737c478bd9Sstevel@tonic-gate */
9747c478bd9Sstevel@tonic-gate while(!matched && *pptr && *pptr != ']') {
9757c478bd9Sstevel@tonic-gate /*
9767c478bd9Sstevel@tonic-gate * Is this a range of characters signaled by the two end characters
9777c478bd9Sstevel@tonic-gate * separated by a hyphen?
9787c478bd9Sstevel@tonic-gate */
9797c478bd9Sstevel@tonic-gate if(*pptr == '-') {
9807c478bd9Sstevel@tonic-gate if(pptr[1] != ']') {
9817c478bd9Sstevel@tonic-gate if(c >= pptr[-1] && c <= pptr[1])
9827c478bd9Sstevel@tonic-gate matched = 1;
9837c478bd9Sstevel@tonic-gate pptr += 2;
9847c478bd9Sstevel@tonic-gate };
9857c478bd9Sstevel@tonic-gate /*
9867c478bd9Sstevel@tonic-gate * A normal character to be compared directly.
9877c478bd9Sstevel@tonic-gate */
9887c478bd9Sstevel@tonic-gate } else if(*pptr++ == c) {
9897c478bd9Sstevel@tonic-gate matched = 1;
9907c478bd9Sstevel@tonic-gate };
9917c478bd9Sstevel@tonic-gate };
9927c478bd9Sstevel@tonic-gate /*
9937c478bd9Sstevel@tonic-gate * Find the terminating ']'.
9947c478bd9Sstevel@tonic-gate */
9957c478bd9Sstevel@tonic-gate while(*pptr && *pptr != ']')
9967c478bd9Sstevel@tonic-gate pptr++;
9977c478bd9Sstevel@tonic-gate /*
9987c478bd9Sstevel@tonic-gate * Did we find a terminating ']'?
9997c478bd9Sstevel@tonic-gate */
10007c478bd9Sstevel@tonic-gate if(*pptr == ']') {
10017c478bd9Sstevel@tonic-gate *endp = pptr + 1;
10027c478bd9Sstevel@tonic-gate return matched ? !invert : invert;
10037c478bd9Sstevel@tonic-gate };
10047c478bd9Sstevel@tonic-gate /*
10057c478bd9Sstevel@tonic-gate * If the pattern didn't end with a ']' then it doesn't match, regardless
10067c478bd9Sstevel@tonic-gate * of the value of the required sense of the match.
10077c478bd9Sstevel@tonic-gate */
10087c478bd9Sstevel@tonic-gate *endp = pptr;
10097c478bd9Sstevel@tonic-gate return 0;
10107c478bd9Sstevel@tonic-gate }
10117c478bd9Sstevel@tonic-gate
10127c478bd9Sstevel@tonic-gate /*.......................................................................
10137c478bd9Sstevel@tonic-gate * This is a qsort() comparison function used to sort strings.
10147c478bd9Sstevel@tonic-gate *
10157c478bd9Sstevel@tonic-gate * Input:
10167c478bd9Sstevel@tonic-gate * v1, v2 void * Pointers to the two strings to be compared.
10177c478bd9Sstevel@tonic-gate * Output:
10187c478bd9Sstevel@tonic-gate * return int -1 -> v1 < v2.
10197c478bd9Sstevel@tonic-gate * 0 -> v1 == v2
10207c478bd9Sstevel@tonic-gate * 1 -> v1 > v2
10217c478bd9Sstevel@tonic-gate */
ef_cmp_strings(const void * v1,const void * v2)10227c478bd9Sstevel@tonic-gate static int ef_cmp_strings(const void *v1, const void *v2)
10237c478bd9Sstevel@tonic-gate {
10247c478bd9Sstevel@tonic-gate char * const *s1 = (char * const *) v1;
10257c478bd9Sstevel@tonic-gate char * const *s2 = (char * const *) v2;
10267c478bd9Sstevel@tonic-gate return strcmp(*s1, *s2);
10277c478bd9Sstevel@tonic-gate }
10287c478bd9Sstevel@tonic-gate
10297c478bd9Sstevel@tonic-gate /*.......................................................................
10307c478bd9Sstevel@tonic-gate * Preprocess a path, expanding ~/, ~user/ and $envvar references, using
10317c478bd9Sstevel@tonic-gate * ef->path as a work buffer, then copy the result into a cache entry,
10327c478bd9Sstevel@tonic-gate * and return a pointer to this copy.
10337c478bd9Sstevel@tonic-gate *
10347c478bd9Sstevel@tonic-gate * Input:
10357c478bd9Sstevel@tonic-gate * ef ExpandFile * The resource object of the file matcher.
10367c478bd9Sstevel@tonic-gate * pathlen int The length of the prefix of path[] to be expanded.
10377c478bd9Sstevel@tonic-gate * Output:
10387c478bd9Sstevel@tonic-gate * return char * A pointer to a copy of the output path in the
10397c478bd9Sstevel@tonic-gate * cache. On error NULL is returned, and a description
10407c478bd9Sstevel@tonic-gate * of the error is left in ef->err.
10417c478bd9Sstevel@tonic-gate */
ef_expand_special(ExpandFile * ef,const char * path,int pathlen)10427c478bd9Sstevel@tonic-gate static char *ef_expand_special(ExpandFile *ef, const char *path, int pathlen)
10437c478bd9Sstevel@tonic-gate {
10447c478bd9Sstevel@tonic-gate int spos; /* The index of the start of the path segment that needs */
10457c478bd9Sstevel@tonic-gate /* to be copied from path[] to the output pathname. */
10467c478bd9Sstevel@tonic-gate int ppos; /* The index of a character in path[] */
10477c478bd9Sstevel@tonic-gate char *pptr; /* A pointer into the output path */
10487c478bd9Sstevel@tonic-gate int escaped; /* True if the previous character was a '\' */
10497c478bd9Sstevel@tonic-gate int i;
10507c478bd9Sstevel@tonic-gate /*
10517c478bd9Sstevel@tonic-gate * Clear the pathname buffer.
10527c478bd9Sstevel@tonic-gate */
10537c478bd9Sstevel@tonic-gate _pn_clear_path(ef->path);
10547c478bd9Sstevel@tonic-gate /*
10557c478bd9Sstevel@tonic-gate * We need to perform two passes, one to expand environment variables
10567c478bd9Sstevel@tonic-gate * and a second to do tilde expansion. This caters for the case
10577c478bd9Sstevel@tonic-gate * where an initial dollar expansion yields a tilde expression.
10587c478bd9Sstevel@tonic-gate */
10597c478bd9Sstevel@tonic-gate escaped = 0;
10607c478bd9Sstevel@tonic-gate for(spos=ppos=0; ppos < pathlen; ppos++) {
10617c478bd9Sstevel@tonic-gate int c = path[ppos];
10627c478bd9Sstevel@tonic-gate if(escaped) {
10637c478bd9Sstevel@tonic-gate escaped = 0;
10647c478bd9Sstevel@tonic-gate } else if(c == '\\') {
10657c478bd9Sstevel@tonic-gate escaped = 1;
10667c478bd9Sstevel@tonic-gate } else if(c == '$') {
10677c478bd9Sstevel@tonic-gate int envlen; /* The length of the environment variable */
10687c478bd9Sstevel@tonic-gate char *value; /* The value of the environment variable */
10697c478bd9Sstevel@tonic-gate /*
10707c478bd9Sstevel@tonic-gate * Record the preceding unrecorded part of the pathname.
10717c478bd9Sstevel@tonic-gate */
10727c478bd9Sstevel@tonic-gate if(spos < ppos && _pn_append_to_path(ef->path, path + spos, ppos-spos, 0)
10737c478bd9Sstevel@tonic-gate == NULL) {
10747c478bd9Sstevel@tonic-gate _err_record_msg(ef->err, "Insufficient memory to expand path",
10757c478bd9Sstevel@tonic-gate END_ERR_MSG);
10767c478bd9Sstevel@tonic-gate return NULL;
10777c478bd9Sstevel@tonic-gate };
10787c478bd9Sstevel@tonic-gate /*
10797c478bd9Sstevel@tonic-gate * Skip the dollar.
10807c478bd9Sstevel@tonic-gate */
10817c478bd9Sstevel@tonic-gate ppos++;
10827c478bd9Sstevel@tonic-gate /*
10837c478bd9Sstevel@tonic-gate * Copy the environment variable name that follows the dollar into
10847c478bd9Sstevel@tonic-gate * ef->envnam[], stopping if a directory separator or end of string
10857c478bd9Sstevel@tonic-gate * is seen.
10867c478bd9Sstevel@tonic-gate */
10877c478bd9Sstevel@tonic-gate for(envlen=0; envlen<ENV_LEN && ppos < pathlen &&
10887c478bd9Sstevel@tonic-gate strncmp(path + ppos, FS_DIR_SEP, FS_DIR_SEP_LEN); envlen++)
10897c478bd9Sstevel@tonic-gate ef->envnam[envlen] = path[ppos++];
10907c478bd9Sstevel@tonic-gate /*
10917c478bd9Sstevel@tonic-gate * If the username overflowed the buffer, treat it as invalid (note that
10927c478bd9Sstevel@tonic-gate * on most unix systems only 8 characters are allowed in a username,
10937c478bd9Sstevel@tonic-gate * whereas our ENV_LEN is much bigger than that.
10947c478bd9Sstevel@tonic-gate */
10957c478bd9Sstevel@tonic-gate if(envlen >= ENV_LEN) {
10967c478bd9Sstevel@tonic-gate _err_record_msg(ef->err, "Environment variable name too long",
10977c478bd9Sstevel@tonic-gate END_ERR_MSG);
10987c478bd9Sstevel@tonic-gate return NULL;
10997c478bd9Sstevel@tonic-gate };
11007c478bd9Sstevel@tonic-gate /*
11017c478bd9Sstevel@tonic-gate * Terminate the environment variable name.
11027c478bd9Sstevel@tonic-gate */
11037c478bd9Sstevel@tonic-gate ef->envnam[envlen] = '\0';
11047c478bd9Sstevel@tonic-gate /*
11057c478bd9Sstevel@tonic-gate * Lookup the value of the environment variable.
11067c478bd9Sstevel@tonic-gate */
11077c478bd9Sstevel@tonic-gate value = getenv(ef->envnam);
11087c478bd9Sstevel@tonic-gate if(!value) {
11097c478bd9Sstevel@tonic-gate _err_record_msg(ef->err, "No expansion found for: $", ef->envnam,
11107c478bd9Sstevel@tonic-gate END_ERR_MSG);
11117c478bd9Sstevel@tonic-gate return NULL;
11127c478bd9Sstevel@tonic-gate };
11137c478bd9Sstevel@tonic-gate /*
11147c478bd9Sstevel@tonic-gate * Copy the value of the environment variable into the output pathname.
11157c478bd9Sstevel@tonic-gate */
11167c478bd9Sstevel@tonic-gate if(_pn_append_to_path(ef->path, value, -1, 0) == NULL) {
11177c478bd9Sstevel@tonic-gate _err_record_msg(ef->err, "Insufficient memory to expand path",
11187c478bd9Sstevel@tonic-gate END_ERR_MSG);
11197c478bd9Sstevel@tonic-gate return NULL;
11207c478bd9Sstevel@tonic-gate };
11217c478bd9Sstevel@tonic-gate /*
11227c478bd9Sstevel@tonic-gate * Record the start of the uncopied tail of the input pathname.
11237c478bd9Sstevel@tonic-gate */
11247c478bd9Sstevel@tonic-gate spos = ppos;
11257c478bd9Sstevel@tonic-gate };
11267c478bd9Sstevel@tonic-gate };
11277c478bd9Sstevel@tonic-gate /*
11287c478bd9Sstevel@tonic-gate * Record the uncopied tail of the pathname.
11297c478bd9Sstevel@tonic-gate */
11307c478bd9Sstevel@tonic-gate if(spos < ppos && _pn_append_to_path(ef->path, path + spos, ppos-spos, 0)
11317c478bd9Sstevel@tonic-gate == NULL) {
11327c478bd9Sstevel@tonic-gate _err_record_msg(ef->err, "Insufficient memory to expand path", END_ERR_MSG);
11337c478bd9Sstevel@tonic-gate return NULL;
11347c478bd9Sstevel@tonic-gate };
11357c478bd9Sstevel@tonic-gate /*
11367c478bd9Sstevel@tonic-gate * If the first character of the resulting pathname is a tilde,
11377c478bd9Sstevel@tonic-gate * then attempt to substitute the home directory of the specified user.
11387c478bd9Sstevel@tonic-gate */
11397c478bd9Sstevel@tonic-gate pptr = ef->path->name;
11407c478bd9Sstevel@tonic-gate if(*pptr == '~' && path[0] != '\\') {
11417c478bd9Sstevel@tonic-gate int usrlen; /* The length of the username following the tilde */
11427c478bd9Sstevel@tonic-gate const char *homedir; /* The home directory of the user */
11437c478bd9Sstevel@tonic-gate int homelen; /* The length of the home directory string */
11447c478bd9Sstevel@tonic-gate int plen; /* The current length of the path */
11457c478bd9Sstevel@tonic-gate int skip=0; /* The number of characters to skip after the ~user */
11467c478bd9Sstevel@tonic-gate /*
11477c478bd9Sstevel@tonic-gate * Get the current length of the output path.
11487c478bd9Sstevel@tonic-gate */
11497c478bd9Sstevel@tonic-gate plen = strlen(ef->path->name);
11507c478bd9Sstevel@tonic-gate /*
11517c478bd9Sstevel@tonic-gate * Skip the tilde.
11527c478bd9Sstevel@tonic-gate */
11537c478bd9Sstevel@tonic-gate pptr++;
11547c478bd9Sstevel@tonic-gate /*
11557c478bd9Sstevel@tonic-gate * Copy the optional username that follows the tilde into ef->usrnam[].
11567c478bd9Sstevel@tonic-gate */
11577c478bd9Sstevel@tonic-gate for(usrlen=0; usrlen<USR_LEN && *pptr &&
11587c478bd9Sstevel@tonic-gate strncmp(pptr, FS_DIR_SEP, FS_DIR_SEP_LEN); usrlen++)
11597c478bd9Sstevel@tonic-gate ef->usrnam[usrlen] = *pptr++;
11607c478bd9Sstevel@tonic-gate /*
11617c478bd9Sstevel@tonic-gate * If the username overflowed the buffer, treat it as invalid (note that
11627c478bd9Sstevel@tonic-gate * on most unix systems only 8 characters are allowed in a username,
11637c478bd9Sstevel@tonic-gate * whereas our USR_LEN is much bigger than that.
11647c478bd9Sstevel@tonic-gate */
11657c478bd9Sstevel@tonic-gate if(usrlen >= USR_LEN) {
11667c478bd9Sstevel@tonic-gate _err_record_msg(ef->err, "Username too long", END_ERR_MSG);
11677c478bd9Sstevel@tonic-gate return NULL;
11687c478bd9Sstevel@tonic-gate };
11697c478bd9Sstevel@tonic-gate /*
11707c478bd9Sstevel@tonic-gate * Terminate the username string.
11717c478bd9Sstevel@tonic-gate */
11727c478bd9Sstevel@tonic-gate ef->usrnam[usrlen] = '\0';
11737c478bd9Sstevel@tonic-gate /*
11747c478bd9Sstevel@tonic-gate * Lookup the home directory of the user.
11757c478bd9Sstevel@tonic-gate */
11767c478bd9Sstevel@tonic-gate homedir = _hd_lookup_home_dir(ef->home, ef->usrnam);
11777c478bd9Sstevel@tonic-gate if(!homedir) {
11787c478bd9Sstevel@tonic-gate _err_record_msg(ef->err, _hd_last_home_dir_error(ef->home), END_ERR_MSG);
11797c478bd9Sstevel@tonic-gate return NULL;
11807c478bd9Sstevel@tonic-gate };
11817c478bd9Sstevel@tonic-gate homelen = strlen(homedir);
11827c478bd9Sstevel@tonic-gate /*
11837c478bd9Sstevel@tonic-gate * ~user and ~ are usually followed by a directory separator to
11847c478bd9Sstevel@tonic-gate * separate them from the file contained in the home directory.
11857c478bd9Sstevel@tonic-gate * If the home directory is the root directory, then we don't want
11867c478bd9Sstevel@tonic-gate * to follow the home directory by a directory separator, so we must
11877c478bd9Sstevel@tonic-gate * erase it.
11887c478bd9Sstevel@tonic-gate */
11897c478bd9Sstevel@tonic-gate if(strcmp(homedir, FS_ROOT_DIR) == 0 &&
11907c478bd9Sstevel@tonic-gate strncmp(pptr, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) {
11917c478bd9Sstevel@tonic-gate skip = FS_DIR_SEP_LEN;
11927c478bd9Sstevel@tonic-gate };
11937c478bd9Sstevel@tonic-gate /*
11947c478bd9Sstevel@tonic-gate * If needed, increase the size of the pathname buffer to allow it
11957c478bd9Sstevel@tonic-gate * to accomodate the home directory instead of the tilde expression.
11967c478bd9Sstevel@tonic-gate * Note that pptr may not be valid after this call.
11977c478bd9Sstevel@tonic-gate */
11987c478bd9Sstevel@tonic-gate if(_pn_resize_path(ef->path, plen - usrlen - 1 - skip + homelen)==NULL) {
11997c478bd9Sstevel@tonic-gate _err_record_msg(ef->err, "Insufficient memory to expand filename",
12007c478bd9Sstevel@tonic-gate END_ERR_MSG);
12017c478bd9Sstevel@tonic-gate return NULL;
12027c478bd9Sstevel@tonic-gate };
12037c478bd9Sstevel@tonic-gate /*
12047c478bd9Sstevel@tonic-gate * Move the part of the pathname that follows the tilde expression to
12057c478bd9Sstevel@tonic-gate * the end of where the home directory will need to be inserted.
12067c478bd9Sstevel@tonic-gate */
12077c478bd9Sstevel@tonic-gate memmove(ef->path->name + homelen,
12087c478bd9Sstevel@tonic-gate ef->path->name + 1 + usrlen + skip, plen - usrlen - 1 - skip+1);
12097c478bd9Sstevel@tonic-gate /*
12107c478bd9Sstevel@tonic-gate * Write the home directory at the beginning of the string.
12117c478bd9Sstevel@tonic-gate */
12127c478bd9Sstevel@tonic-gate for(i=0; i<homelen; i++)
12137c478bd9Sstevel@tonic-gate ef->path->name[i] = homedir[i];
12147c478bd9Sstevel@tonic-gate };
12157c478bd9Sstevel@tonic-gate /*
12167c478bd9Sstevel@tonic-gate * Copy the result into the cache, and return a pointer to the copy.
12177c478bd9Sstevel@tonic-gate */
12187c478bd9Sstevel@tonic-gate return ef_cache_pathname(ef, ef->path->name, 0);
12197c478bd9Sstevel@tonic-gate }
12207c478bd9Sstevel@tonic-gate
12217c478bd9Sstevel@tonic-gate /*.......................................................................
12227c478bd9Sstevel@tonic-gate * Return a description of the last path-expansion error that occurred.
12237c478bd9Sstevel@tonic-gate *
12247c478bd9Sstevel@tonic-gate * Input:
12257c478bd9Sstevel@tonic-gate * ef ExpandFile * The path-expansion resource object.
12267c478bd9Sstevel@tonic-gate * Output:
12277c478bd9Sstevel@tonic-gate * return char * The description of the last error.
12287c478bd9Sstevel@tonic-gate */
ef_last_error(ExpandFile * ef)12297c478bd9Sstevel@tonic-gate const char *ef_last_error(ExpandFile *ef)
12307c478bd9Sstevel@tonic-gate {
12317c478bd9Sstevel@tonic-gate return ef ? _err_get_msg(ef->err) : "NULL ExpandFile argument";
12327c478bd9Sstevel@tonic-gate }
12337c478bd9Sstevel@tonic-gate
12347c478bd9Sstevel@tonic-gate /*.......................................................................
12357c478bd9Sstevel@tonic-gate * Print out an array of matching files.
12367c478bd9Sstevel@tonic-gate *
12377c478bd9Sstevel@tonic-gate * Input:
12387c478bd9Sstevel@tonic-gate * result FileExpansion * The container of the sorted array of
12397c478bd9Sstevel@tonic-gate * expansions.
12407c478bd9Sstevel@tonic-gate * fp FILE * The output stream to write to.
12417c478bd9Sstevel@tonic-gate * term_width int The width of the terminal.
12427c478bd9Sstevel@tonic-gate * Output:
12437c478bd9Sstevel@tonic-gate * return int 0 - OK.
12447c478bd9Sstevel@tonic-gate * 1 - Error.
12457c478bd9Sstevel@tonic-gate */
ef_list_expansions(FileExpansion * result,FILE * fp,int term_width)12467c478bd9Sstevel@tonic-gate int ef_list_expansions(FileExpansion *result, FILE *fp, int term_width)
12477c478bd9Sstevel@tonic-gate {
12487c478bd9Sstevel@tonic-gate return _ef_output_expansions(result, _io_write_stdio, fp, term_width);
12497c478bd9Sstevel@tonic-gate }
12507c478bd9Sstevel@tonic-gate
12517c478bd9Sstevel@tonic-gate /*.......................................................................
12527c478bd9Sstevel@tonic-gate * Print out an array of matching files via a callback.
12537c478bd9Sstevel@tonic-gate *
12547c478bd9Sstevel@tonic-gate * Input:
12557c478bd9Sstevel@tonic-gate * result FileExpansion * The container of the sorted array of
12567c478bd9Sstevel@tonic-gate * expansions.
12577c478bd9Sstevel@tonic-gate * write_fn GlWriteFn * The function to call to write the
12587c478bd9Sstevel@tonic-gate * expansions or 0 to discard the output.
12597c478bd9Sstevel@tonic-gate * data void * Anonymous data to pass to write_fn().
12607c478bd9Sstevel@tonic-gate * term_width int The width of the terminal.
12617c478bd9Sstevel@tonic-gate * Output:
12627c478bd9Sstevel@tonic-gate * return int 0 - OK.
12637c478bd9Sstevel@tonic-gate * 1 - Error.
12647c478bd9Sstevel@tonic-gate */
_ef_output_expansions(FileExpansion * result,GlWriteFn * write_fn,void * data,int term_width)12657c478bd9Sstevel@tonic-gate int _ef_output_expansions(FileExpansion *result, GlWriteFn *write_fn,
12667c478bd9Sstevel@tonic-gate void *data, int term_width)
12677c478bd9Sstevel@tonic-gate {
12687c478bd9Sstevel@tonic-gate EfListFormat fmt; /* List formatting information */
12697c478bd9Sstevel@tonic-gate int lnum; /* The sequential number of the line to print next */
12707c478bd9Sstevel@tonic-gate /*
12717c478bd9Sstevel@tonic-gate * Not enough space to list anything?
12727c478bd9Sstevel@tonic-gate */
12737c478bd9Sstevel@tonic-gate if(term_width < 1)
12747c478bd9Sstevel@tonic-gate return 0;
12757c478bd9Sstevel@tonic-gate /*
12767c478bd9Sstevel@tonic-gate * Do we have a callback to write via, and any expansions to be listed?
12777c478bd9Sstevel@tonic-gate */
12787c478bd9Sstevel@tonic-gate if(write_fn && result && result->nfile>0) {
12797c478bd9Sstevel@tonic-gate /*
12807c478bd9Sstevel@tonic-gate * Work out how to arrange the listing into fixed sized columns.
12817c478bd9Sstevel@tonic-gate */
12827c478bd9Sstevel@tonic-gate ef_plan_listing(result, term_width, &fmt);
12837c478bd9Sstevel@tonic-gate /*
12847c478bd9Sstevel@tonic-gate * Print the listing to the specified stream.
12857c478bd9Sstevel@tonic-gate */
12867c478bd9Sstevel@tonic-gate for(lnum=0; lnum < fmt.nline; lnum++) {
12877c478bd9Sstevel@tonic-gate if(ef_format_line(result, &fmt, lnum, write_fn, data))
12887c478bd9Sstevel@tonic-gate return 1;
12897c478bd9Sstevel@tonic-gate };
12907c478bd9Sstevel@tonic-gate };
12917c478bd9Sstevel@tonic-gate return 0;
12927c478bd9Sstevel@tonic-gate }
12937c478bd9Sstevel@tonic-gate
12947c478bd9Sstevel@tonic-gate /*.......................................................................
12957c478bd9Sstevel@tonic-gate * Work out how to arrange a given array of completions into a listing
12967c478bd9Sstevel@tonic-gate * of one or more fixed size columns.
12977c478bd9Sstevel@tonic-gate *
12987c478bd9Sstevel@tonic-gate * Input:
12997c478bd9Sstevel@tonic-gate * result FileExpansion * The set of completions to be listed.
13007c478bd9Sstevel@tonic-gate * term_width int The width of the terminal. A lower limit of
13017c478bd9Sstevel@tonic-gate * zero is quietly enforced.
13027c478bd9Sstevel@tonic-gate * Input/Output:
13037c478bd9Sstevel@tonic-gate * fmt EfListFormat * The formatting information will be assigned
13047c478bd9Sstevel@tonic-gate * to the members of *fmt.
13057c478bd9Sstevel@tonic-gate */
ef_plan_listing(FileExpansion * result,int term_width,EfListFormat * fmt)13067c478bd9Sstevel@tonic-gate static void ef_plan_listing(FileExpansion *result, int term_width,
13077c478bd9Sstevel@tonic-gate EfListFormat *fmt)
13087c478bd9Sstevel@tonic-gate {
13097c478bd9Sstevel@tonic-gate int maxlen; /* The length of the longest matching string */
13107c478bd9Sstevel@tonic-gate int i;
13117c478bd9Sstevel@tonic-gate /*
13127c478bd9Sstevel@tonic-gate * Ensure that term_width >= 0.
13137c478bd9Sstevel@tonic-gate */
13147c478bd9Sstevel@tonic-gate if(term_width < 0)
13157c478bd9Sstevel@tonic-gate term_width = 0;
13167c478bd9Sstevel@tonic-gate /*
13177c478bd9Sstevel@tonic-gate * Start by assuming the worst case, that either nothing will fit
13187c478bd9Sstevel@tonic-gate * on the screen, or that there are no matches to be listed.
13197c478bd9Sstevel@tonic-gate */
13207c478bd9Sstevel@tonic-gate fmt->term_width = term_width;
13217c478bd9Sstevel@tonic-gate fmt->column_width = 0;
13227c478bd9Sstevel@tonic-gate fmt->nline = fmt->ncol = 0;
13237c478bd9Sstevel@tonic-gate /*
13247c478bd9Sstevel@tonic-gate * Work out the maximum length of the matching strings.
13257c478bd9Sstevel@tonic-gate */
13267c478bd9Sstevel@tonic-gate maxlen = 0;
13277c478bd9Sstevel@tonic-gate for(i=0; i<result->nfile; i++) {
13287c478bd9Sstevel@tonic-gate int len = strlen(result->files[i]);
13297c478bd9Sstevel@tonic-gate if(len > maxlen)
13307c478bd9Sstevel@tonic-gate maxlen = len;
13317c478bd9Sstevel@tonic-gate };
13327c478bd9Sstevel@tonic-gate /*
13337c478bd9Sstevel@tonic-gate * Nothing to list?
13347c478bd9Sstevel@tonic-gate */
13357c478bd9Sstevel@tonic-gate if(maxlen == 0)
13367c478bd9Sstevel@tonic-gate return;
13377c478bd9Sstevel@tonic-gate /*
13387c478bd9Sstevel@tonic-gate * Split the available terminal width into columns of
13397c478bd9Sstevel@tonic-gate * maxlen + EF_COL_SEP characters.
13407c478bd9Sstevel@tonic-gate */
13417c478bd9Sstevel@tonic-gate fmt->column_width = maxlen;
13427c478bd9Sstevel@tonic-gate fmt->ncol = fmt->term_width / (fmt->column_width + EF_COL_SEP);
13437c478bd9Sstevel@tonic-gate /*
13447c478bd9Sstevel@tonic-gate * If the column width is greater than the terminal width, zero columns
13457c478bd9Sstevel@tonic-gate * will have been selected. Set a lower limit of one column. Leave it
13467c478bd9Sstevel@tonic-gate * up to the caller how to deal with completions who's widths exceed
13477c478bd9Sstevel@tonic-gate * the available terminal width.
13487c478bd9Sstevel@tonic-gate */
13497c478bd9Sstevel@tonic-gate if(fmt->ncol < 1)
13507c478bd9Sstevel@tonic-gate fmt->ncol = 1;
13517c478bd9Sstevel@tonic-gate /*
13527c478bd9Sstevel@tonic-gate * How many lines of output will be needed?
13537c478bd9Sstevel@tonic-gate */
13547c478bd9Sstevel@tonic-gate fmt->nline = (result->nfile + fmt->ncol - 1) / fmt->ncol;
13557c478bd9Sstevel@tonic-gate return;
13567c478bd9Sstevel@tonic-gate }
13577c478bd9Sstevel@tonic-gate
13587c478bd9Sstevel@tonic-gate /*.......................................................................
13597c478bd9Sstevel@tonic-gate * Render one line of a multi-column listing of completions, using a
13607c478bd9Sstevel@tonic-gate * callback function to pass the output to an arbitrary destination.
13617c478bd9Sstevel@tonic-gate *
13627c478bd9Sstevel@tonic-gate * Input:
13637c478bd9Sstevel@tonic-gate * result FileExpansion * The container of the sorted array of
13647c478bd9Sstevel@tonic-gate * completions.
13657c478bd9Sstevel@tonic-gate * fmt EfListFormat * Formatting information.
13667c478bd9Sstevel@tonic-gate * lnum int The index of the line to print, starting
13677c478bd9Sstevel@tonic-gate * from 0, and incrementing until the return
13687c478bd9Sstevel@tonic-gate * value indicates that there is nothing more
13697c478bd9Sstevel@tonic-gate * to be printed.
13707c478bd9Sstevel@tonic-gate * write_fn GlWriteFn * The function to call to write the line, or
13717c478bd9Sstevel@tonic-gate * 0 to discard the output.
13727c478bd9Sstevel@tonic-gate * data void * Anonymous data to pass to write_fn().
13737c478bd9Sstevel@tonic-gate * Output:
13747c478bd9Sstevel@tonic-gate * return int 0 - Line printed ok.
13757c478bd9Sstevel@tonic-gate * 1 - Nothing to print.
13767c478bd9Sstevel@tonic-gate */
ef_format_line(FileExpansion * result,EfListFormat * fmt,int lnum,GlWriteFn * write_fn,void * data)13777c478bd9Sstevel@tonic-gate static int ef_format_line(FileExpansion *result, EfListFormat *fmt, int lnum,
13787c478bd9Sstevel@tonic-gate GlWriteFn *write_fn, void *data)
13797c478bd9Sstevel@tonic-gate {
13807c478bd9Sstevel@tonic-gate int col; /* The index of the list column being output */
13817c478bd9Sstevel@tonic-gate /*
13827c478bd9Sstevel@tonic-gate * If the line index is out of bounds, there is nothing to be written.
13837c478bd9Sstevel@tonic-gate */
13847c478bd9Sstevel@tonic-gate if(lnum < 0 || lnum >= fmt->nline)
13857c478bd9Sstevel@tonic-gate return 1;
13867c478bd9Sstevel@tonic-gate /*
13877c478bd9Sstevel@tonic-gate * If no output function has been provided, return as though the line
13887c478bd9Sstevel@tonic-gate * had been printed.
13897c478bd9Sstevel@tonic-gate */
13907c478bd9Sstevel@tonic-gate if(!write_fn)
13917c478bd9Sstevel@tonic-gate return 0;
13927c478bd9Sstevel@tonic-gate /*
13937c478bd9Sstevel@tonic-gate * Print the matches in 'ncol' columns, sorted in line order within each
13947c478bd9Sstevel@tonic-gate * column.
13957c478bd9Sstevel@tonic-gate */
13967c478bd9Sstevel@tonic-gate for(col=0; col < fmt->ncol; col++) {
13977c478bd9Sstevel@tonic-gate int m = col*fmt->nline + lnum;
13987c478bd9Sstevel@tonic-gate /*
13997c478bd9Sstevel@tonic-gate * Is there another match to be written? Note that in general
14007c478bd9Sstevel@tonic-gate * the last line of a listing will have fewer filled columns
14017c478bd9Sstevel@tonic-gate * than the initial lines.
14027c478bd9Sstevel@tonic-gate */
14037c478bd9Sstevel@tonic-gate if(m < result->nfile) {
14047c478bd9Sstevel@tonic-gate char *file = result->files[m];
14057c478bd9Sstevel@tonic-gate /*
14067c478bd9Sstevel@tonic-gate * How long are the completion and type-suffix strings?
14077c478bd9Sstevel@tonic-gate */
14087c478bd9Sstevel@tonic-gate int flen = strlen(file);
14097c478bd9Sstevel@tonic-gate /*
14107c478bd9Sstevel@tonic-gate * Write the completion string.
14117c478bd9Sstevel@tonic-gate */
14127c478bd9Sstevel@tonic-gate if(write_fn(data, file, flen) != flen)
14137c478bd9Sstevel@tonic-gate return 1;
14147c478bd9Sstevel@tonic-gate /*
14157c478bd9Sstevel@tonic-gate * If another column follows the current one, pad to its start with spaces.
14167c478bd9Sstevel@tonic-gate */
14177c478bd9Sstevel@tonic-gate if(col+1 < fmt->ncol) {
14187c478bd9Sstevel@tonic-gate /*
14197c478bd9Sstevel@tonic-gate * The following constant string of spaces is used to pad the output.
14207c478bd9Sstevel@tonic-gate */
14217c478bd9Sstevel@tonic-gate static const char spaces[] = " ";
14227c478bd9Sstevel@tonic-gate static const int nspace = sizeof(spaces) - 1;
14237c478bd9Sstevel@tonic-gate /*
14247c478bd9Sstevel@tonic-gate * Pad to the next column, using as few sub-strings of the spaces[]
14257c478bd9Sstevel@tonic-gate * array as possible.
14267c478bd9Sstevel@tonic-gate */
14277c478bd9Sstevel@tonic-gate int npad = fmt->column_width + EF_COL_SEP - flen;
14287c478bd9Sstevel@tonic-gate while(npad>0) {
14297c478bd9Sstevel@tonic-gate int n = npad > nspace ? nspace : npad;
14307c478bd9Sstevel@tonic-gate if(write_fn(data, spaces + nspace - n, n) != n)
14317c478bd9Sstevel@tonic-gate return 1;
14327c478bd9Sstevel@tonic-gate npad -= n;
14337c478bd9Sstevel@tonic-gate };
14347c478bd9Sstevel@tonic-gate };
14357c478bd9Sstevel@tonic-gate };
14367c478bd9Sstevel@tonic-gate };
14377c478bd9Sstevel@tonic-gate /*
14387c478bd9Sstevel@tonic-gate * Start a new line.
14397c478bd9Sstevel@tonic-gate */
14407c478bd9Sstevel@tonic-gate {
14417c478bd9Sstevel@tonic-gate char s[] = "\r\n";
14427c478bd9Sstevel@tonic-gate int n = strlen(s);
14437c478bd9Sstevel@tonic-gate if(write_fn(data, s, n) != n)
14447c478bd9Sstevel@tonic-gate return 1;
14457c478bd9Sstevel@tonic-gate };
14467c478bd9Sstevel@tonic-gate return 0;
14477c478bd9Sstevel@tonic-gate }
14487c478bd9Sstevel@tonic-gate
14497c478bd9Sstevel@tonic-gate #endif /* ifndef WITHOUT_FILE_SYSTEM */
1450