/* This file is extracted from sash-3.4/util.c */ /* * Copyright (c) 1999 by David I. Bell * Permission is granted to use, distribute, or modify this source, * provided that this copyright notice remains intact. * */ #include #include #include #include #include #include #include typedef int BOOL; #define FALSE ((BOOL) 0) #define TRUE ((BOOL) 1) #define EXPAND_ALLOC 1024 #define PATH_LEN 1024 /* * A chunk of data. * Chunks contain data which is allocated as needed, but which is * not freed until all of the data needs freeing, such as at * the beginning of the next command. */ typedef struct chunk CHUNK; #define CHUNK_INIT_SIZE 4 struct chunk { CHUNK * next; char data[CHUNK_INIT_SIZE]; /* actually of varying length */ }; static CHUNK * chunkList; /* * Allocate a chunk of memory (like malloc). * The difference, though, is that the memory allocated is put on a * list of chunks which can be freed all at one time. You CAN NOT free * an individual chunk. */ static char *getChunk(int size) { CHUNK * chunk; if (size < CHUNK_INIT_SIZE) size = CHUNK_INIT_SIZE; chunk = (CHUNK *) malloc(size + sizeof(CHUNK) - CHUNK_INIT_SIZE); if (chunk == NULL) return NULL; chunk->next = chunkList; chunkList = chunk; return chunk->data; } /* * Sort routine for list of fileNames. */ static int nameSort(const void * p1, const void * p2) { const char ** s1; const char ** s2; s1 = (const char **) p1; s2 = (const char **) p2; return strcmp(*s1, *s2); } /* * Routine to see if a text string is matched by a wildcard pattern. * Returns TRUE if the text is matched, or FALSE if it is not matched * or if the pattern is invalid. * * matches zero or more characters * ? matches a single character * [abc] matches 'a', 'b' or 'c' * \c quotes character c * Adapted from code written by Ingo Wilken. */ static BOOL match(const char * text, const char * pattern) { const char * retryPat; const char * retryText; int ch; BOOL found; retryPat = NULL; retryText = NULL; while (*text || *pattern) { ch = *pattern++; switch (ch) { case '*': retryPat = pattern; retryText = text; break; case '[': found = FALSE; while ((ch = *pattern++) != ']') { if (ch == '\\') ch = *pattern++; if (ch == '\0') return FALSE; if (*text == ch) found = TRUE; } if (!found) { pattern = retryPat; text = ++retryText; } /* fall into next case */ case '?': if (*text++ == '\0') return FALSE; break; case '\\': ch = *pattern++; if (ch == '\0') return FALSE; /* fall into next case */ default: if (*text == ch) { if (*text) text++; break; } if (*text) { pattern = retryPat; text = ++retryText; break; } return FALSE; } if (pattern == NULL) return FALSE; } return TRUE; } /* * Utility routines. * * Expand the wildcards in a fileName wildcard pattern, if any. * Returns an argument list with matching fileNames in sorted order. * The expanded names are stored in memory chunks which can later all * be freed at once. The returned list is only valid until the next * call or until the next command. Returns zero if the name is not a * wildcard, or returns the count of matched files if the name is a * wildcard and there was at least one match, or returns -1 if either * no fileNames matched or there was an allocation error. */ int expandWildCards(const char * fileNamePattern, const char *** retFileTable) { const char * last; const char * cp1; const char * cp2; const char * cp3; char * str; DIR * dirp; struct dirent * dp; int dirLen; int newFileTableSize; char ** newFileTable; char dirName[PATH_LEN]; static int fileCount; static int fileTableSize; static char ** fileTable; /* * Clear the return values until we know their final values. */ fileCount = 0; *retFileTable = NULL; /* * Scan the file name pattern for any wildcard characters. */ cp1 = strchr(fileNamePattern, '*'); cp2 = strchr(fileNamePattern, '?'); cp3 = strchr(fileNamePattern, '['); #if 0 /* * If there are no wildcard characters then return zero to * indicate that there was actually no wildcard pattern. */ if ((cp1 == NULL) && (cp2 == NULL) && (cp3 == NULL)) return 0; #endif /* * There are wildcards in the specified filename. * Get the last component of the file name. */ last = strrchr(fileNamePattern, '/'); if (last) last++; else last = fileNamePattern; /* * If any wildcards were found before the last filename component * then return an error. */ if ((cp1 && (cp1 < last)) || (cp2 && (cp2 < last)) || (cp3 && (cp3 < last))) { fprintf(stderr, "Wildcards only implemented for last file name component\n"); return -1; } /* * Assume at first that we are scanning the current directory. */ dirName[0] = '.'; dirName[1] = '\0'; /* * If there was a directory given as part of the file name then * copy it and null terminate it. */ if (last != fileNamePattern) { memcpy(dirName, fileNamePattern, last - fileNamePattern); dirName[last - fileNamePattern - 1] = '\0'; if (dirName[0] == '\0') { dirName[0] = '/'; dirName[1] = '\0'; } } /* * Open the directory containing the files to be checked. */ dirp = opendir(dirName); if (dirp == NULL) { perror(dirName); return -1; } /* * Prepare the directory name for use in making full path names. */ dirLen = strlen(dirName); if (last == fileNamePattern) { dirLen = 0; dirName[0] = '\0'; } else if (dirName[dirLen - 1] != '/') { dirName[dirLen++] = '/'; dirName[dirLen] = '\0'; } /* * Find all of the files in the directory and check them against * the wildcard pattern. */ while ((dp = readdir(dirp)) != NULL) { /* * Skip the current and parent directories. */ if ((strcmp(dp->d_name, ".") == 0) || (strcmp(dp->d_name, "..") == 0)) { continue; } /* * If the file name doesn't match the pattern then skip it. */ if (!match(dp->d_name, last)) continue; /* * This file name is selected. * See if we need to reallocate the file name table. */ if (fileCount >= fileTableSize) { /* * Increment the file table size and reallocate it. */ newFileTableSize = fileTableSize + EXPAND_ALLOC; newFileTable = (char **) realloc((char *) fileTable, (newFileTableSize * sizeof(char *))); if (newFileTable == NULL) { fprintf(stderr, "Cannot allocate file list\n"); closedir(dirp); return -1; } fileTable = newFileTable; fileTableSize = newFileTableSize; } /* * Allocate space for storing the file name in a chunk. */ str = getChunk(dirLen + strlen(dp->d_name) + 1); if (str == NULL) { #if 0 fprintf(stderr, "No memory for file name\n"); #endif closedir(dirp); return -1; } /* * Save the file name in the chunk. */ if (dirLen) memcpy(str, dirName, dirLen); strcpy(str + dirLen, dp->d_name); /* * Save the allocated file name into the file table. */ fileTable[fileCount++] = str; } /* * Close the directory and check for any matches. */ closedir(dirp); if (fileCount == 0) { #if 0 fprintf(stderr, "No matches\n"); #endif return -1; } /* * Sort the list of file names. */ qsort((void *) fileTable, fileCount, sizeof(char *), nameSort); /* * Return the file list and count. */ *retFileTable = (const char **) fileTable; return fileCount; } #ifdef TEST int main(int argc, char *argv[]) { const char **fileTable; int fileCount, i, j; for (j=1; j