diff options
author | Eric Andersen <andersen@codepoet.org> | 1999-10-05 20:24:54 +0400 |
---|---|---|
committer | Eric Andersen <andersen@codepoet.org> | 1999-10-05 20:24:54 +0400 |
commit | cc8ed39b240180b58810784f844e253263594ac3 (patch) | |
tree | 15feebbb4be9a9168209609f48f0b100f9364420 /utility.c |
Initial revision0_29alpha2
Diffstat (limited to 'utility.c')
-rw-r--r-- | utility.c | 1181 |
1 files changed, 1181 insertions, 0 deletions
diff --git a/utility.c b/utility.c new file mode 100644 index 000000000..68259710b --- /dev/null +++ b/utility.c @@ -0,0 +1,1181 @@ +/* + * Utility routines. + * + * Copyright (C) 1998 by Erik Andersen <andersee@debian.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Based in part on code from sash, Copyright (c) 1999 by David I. Bell + * Permission has been granted to redistribute this code under the GPL. + * + */ + +#include "utility.h" + +#if 0 + +extern char * +join_paths(char * buffer, const char * a, const char * b) +{ + int length = 0; + + if ( a && *a ) { + length = strlen(a); + memcpy(buffer, a, length); + } + if ( b && *b ) { + if ( length > 0 && buffer[length - 1] != '/' ) + buffer[length++] = '/'; + if ( *b == '/' ) + b++; + strcpy(&buffer[length], b); + } + return buffer; +} + +#endif + + + + + + +static CHUNK * chunkList; + +extern void +name_and_error(const char * name) +{ + fprintf(stderr, "%s: %s\n", name, strerror(errno)); +} + + + +/* + * Return the standard ls-like mode string from a file mode. + * This is static and so is overwritten on each call. + */ +const char * +modeString(int mode) +{ + static char buf[12]; + + strcpy(buf, "----------"); + + /* + * Fill in the file type. + */ + if (S_ISDIR(mode)) + buf[0] = 'd'; + if (S_ISCHR(mode)) + buf[0] = 'c'; + if (S_ISBLK(mode)) + buf[0] = 'b'; + if (S_ISFIFO(mode)) + buf[0] = 'p'; +#ifdef S_ISLNK + if (S_ISLNK(mode)) + buf[0] = 'l'; +#endif +#ifdef S_ISSOCK + if (S_ISSOCK(mode)) + buf[0] = 's'; +#endif + + /* + * Now fill in the normal file permissions. + */ + if (mode & S_IRUSR) + buf[1] = 'r'; + if (mode & S_IWUSR) + buf[2] = 'w'; + if (mode & S_IXUSR) + buf[3] = 'x'; + if (mode & S_IRGRP) + buf[4] = 'r'; + if (mode & S_IWGRP) + buf[5] = 'w'; + if (mode & S_IXGRP) + buf[6] = 'x'; + if (mode & S_IROTH) + buf[7] = 'r'; + if (mode & S_IWOTH) + buf[8] = 'w'; + if (mode & S_IXOTH) + buf[9] = 'x'; + + /* + * Finally fill in magic stuff like suid and sticky text. + */ + if (mode & S_ISUID) + buf[3] = ((mode & S_IXUSR) ? 's' : 'S'); + if (mode & S_ISGID) + buf[6] = ((mode & S_IXGRP) ? 's' : 'S'); + if (mode & S_ISVTX) + buf[9] = ((mode & S_IXOTH) ? 't' : 'T'); + + return buf; +} + + +/* + * Get the time string to be used for a file. + * This is down to the minute for new files, but only the date for old files. + * The string is returned from a static buffer, and so is overwritten for + * each call. + */ +const char * +timeString(time_t timeVal) +{ + time_t now; + char * str; + static char buf[26]; + + time(&now); + + str = ctime(&timeVal); + + strcpy(buf, &str[4]); + buf[12] = '\0'; + + if ((timeVal > now) || (timeVal < now - 365*24*60*60L)) + { + strcpy(&buf[7], &str[20]); + buf[11] = '\0'; + } + + return buf; +} + + +/* + * Return TRUE if a fileName is a directory. + * Nonexistant files return FALSE. + */ +BOOL +isDirectory(const char * name) +{ + struct stat statBuf; + + if (stat(name, &statBuf) < 0) + return FALSE; + + return S_ISDIR(statBuf.st_mode); +} + + +/* + * Return TRUE if a filename is a block or character device. + * Nonexistant files return FALSE. + */ +BOOL +isDevice(const char * name) +{ + struct stat statBuf; + + if (stat(name, &statBuf) < 0) + return FALSE; + + return S_ISBLK(statBuf.st_mode) || S_ISCHR(statBuf.st_mode); +} + + +/* + * Copy one file to another, while possibly preserving its modes, times, + * and modes. Returns TRUE if successful, or FALSE on a failure with an + * error message output. (Failure is not indicted if the attributes cannot + * be set.) + */ +BOOL +copyFile( + const char * srcName, + const char * destName, + BOOL setModes +) +{ + int rfd; + int wfd; + int rcc; + char buf[BUF_SIZE]; + struct stat statBuf1; + struct stat statBuf2; + struct utimbuf times; + + if (stat(srcName, &statBuf1) < 0) + { + perror(srcName); + + return FALSE; + } + + if (stat(destName, &statBuf2) < 0) + { + statBuf2.st_ino = -1; + statBuf2.st_dev = -1; + } + + if ((statBuf1.st_dev == statBuf2.st_dev) && + (statBuf1.st_ino == statBuf2.st_ino)) + { + fprintf(stderr, "Copying file \"%s\" to itself\n", srcName); + + return FALSE; + } + + rfd = open(srcName, O_RDONLY); + + if (rfd < 0) + { + perror(srcName); + + return FALSE; + } + + wfd = creat(destName, statBuf1.st_mode); + + if (wfd < 0) + { + perror(destName); + close(rfd); + + return FALSE; + } + + while ((rcc = read(rfd, buf, sizeof(buf))) > 0) + { + if (fullWrite(wfd, buf, rcc) < 0) + goto error_exit; + } + + if (rcc < 0) + { + perror(srcName); + goto error_exit; + } + + (void) close(rfd); + + if (close(wfd) < 0) + { + perror(destName); + + return FALSE; + } + + if (setModes) + { + (void) chmod(destName, statBuf1.st_mode); + + (void) chown(destName, statBuf1.st_uid, statBuf1.st_gid); + + times.actime = statBuf1.st_atime; + times.modtime = statBuf1.st_mtime; + + (void) utime(destName, ×); + } + + return TRUE; + + +error_exit: + close(rfd); + close(wfd); + + return FALSE; +} + + +/* + * Build a path name from the specified directory name and file name. + * If the directory name is NULL, then the original fileName is returned. + * The built path is in a static area, and is overwritten for each call. + */ +const char * +buildName(const char * dirName, const char * fileName) +{ + const char * cp; + static char buf[PATH_LEN]; + + if ((dirName == NULL) || (*dirName == '\0')) + return fileName; + + cp = strrchr(fileName, '/'); + + if (cp) + fileName = cp + 1; + + strcpy(buf, dirName); + strcat(buf, "/"); + strcat(buf, fileName); + + return buf; +} + + + +/* + * 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 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; + + /* + * 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) + { + fprintf(stderr, "No memory for file name\n"); + 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) + { + fprintf(stderr, "No matches\n"); + + 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; +} + + +/* + * Sort routine for list of fileNames. + */ +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. + */ +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; +} + + +/* + * Take a command string and break it up into an argc, argv list while + * handling quoting and wildcards. The returned argument list and + * strings are in static memory, and so are overwritten on each call. + * The argument list is ended with a NULL pointer for convenience. + * Returns TRUE if successful, or FALSE on an error with a message + * already output. + */ +BOOL +makeArgs(const char * cmd, int * retArgc, const char *** retArgv) +{ + const char * argument; + char * cp; + char * cpOut; + char * newStrings; + const char ** fileTable; + const char ** newArgTable; + int newArgTableSize; + int fileCount; + int len; + int ch; + int quote; + BOOL quotedWildCards; + BOOL unquotedWildCards; + + static int stringsLength; + static char * strings; + static int argCount; + static int argTableSize; + static const char ** argTable; + + /* + * Clear the returned values until we know them. + */ + argCount = 0; + *retArgc = 0; + *retArgv = NULL; + + /* + * Copy the command string into a buffer that we can modify, + * reallocating it if necessary. + */ + len = strlen(cmd) + 1; + + if (len > stringsLength) + { + newStrings = realloc(strings, len); + + if (newStrings == NULL) + { + fprintf(stderr, "Cannot allocate string\n"); + + return FALSE; + } + + strings = newStrings; + stringsLength = len; + } + + memcpy(strings, cmd, len); + cp = strings; + + /* + * Keep parsing the command string as long as there are any + * arguments left. + */ + while (*cp) + { + /* + * Save the beginning of this argument. + */ + argument = cp; + cpOut = cp; + + /* + * Reset quoting and wildcarding for this argument. + */ + quote = '\0'; + quotedWildCards = FALSE; + unquotedWildCards = FALSE; + + /* + * Loop over the string collecting the next argument while + * looking for quoted strings or quoted characters, and + * remembering whether there are any wildcard characters + * in the argument. + */ + while (*cp) + { + ch = *cp++; + + /* + * If we are not in a quote and we see a blank then + * this argument is done. + */ + if (isBlank(ch) && (quote == '\0')) + break; + + /* + * If we see a backslash then accept the next + * character no matter what it is. + */ + if (ch == '\\') + { + ch = *cp++; + + /* + * Make sure there is a next character. + */ + if (ch == '\0') + { + fprintf(stderr, + "Bad quoted character\n"); + + return FALSE; + } + + /* + * Remember whether the quoted character + * is a wildcard. + */ + if (isWildCard(ch)) + quotedWildCards = TRUE; + + *cpOut++ = ch; + + continue; + } + + /* + * If we see one of the wildcard characters then + * remember whether it was seen inside or outside + * of quotes. + */ + if (isWildCard(ch)) + { + if (quote) + quotedWildCards = TRUE; + else + unquotedWildCards = TRUE; + } + + /* + * If we were in a quote and we saw the same quote + * character again then the quote is done. + */ + if (ch == quote) + { + quote = '\0'; + + continue; + } + + /* + * If we weren't in a quote and we see either type + * of quote character, then remember that we are + * now inside of a quote. + */ + if ((quote == '\0') && ((ch == '\'') || (ch == '"'))) + { + quote = ch; + + continue; + } + + /* + * Store the character. + */ + *cpOut++ = ch; + } + + /* + * Make sure that quoting is terminated properly. + */ + if (quote) + { + fprintf(stderr, "Unmatched quote character\n"); + + return FALSE; + } + + /* + * Null terminate the argument if it had shrunk, and then + * skip over all blanks to the next argument, nulling them + * out too. + */ + if (cp != cpOut) + *cpOut = '\0'; + + while (isBlank(*cp)) + *cp++ = '\0'; + + /* + * If both quoted and unquoted wildcards were used then + * complain since we don't handle them properly. + */ + if (quotedWildCards && unquotedWildCards) + { + fprintf(stderr, + "Cannot use quoted and unquoted wildcards\n"); + + return FALSE; + } + + /* + * Expand the argument into the matching filenames or accept + * it as is depending on whether there were any unquoted + * wildcard characters in it. + */ + if (unquotedWildCards) + { + /* + * Expand the argument into the matching filenames. + */ + fileCount = expandWildCards(argument, &fileTable); + + /* + * Return an error if the wildcards failed to match. + */ + if (fileCount < 0) + return FALSE; + + if (fileCount == 0) + { + fprintf(stderr, "Wildcard expansion error\n"); + + return FALSE; + } + } + else + { + /* + * Set up to only store the argument itself. + */ + fileTable = &argument; + fileCount = 1; + } + + /* + * Now reallocate the argument table to hold the file name. + */ + if (argCount + fileCount >= argTableSize) + { + newArgTableSize = argCount + fileCount + 1; + + newArgTable = (const char **) realloc(argTable, + (sizeof(const char *) * newArgTableSize)); + + if (newArgTable == NULL) + { + fprintf(stderr, "No memory for arg list\n"); + + return FALSE; + } + + argTable = newArgTable; + argTableSize = newArgTableSize; + } + + /* + * Copy the new arguments to the end of the old ones. + */ + memcpy((void *) &argTable[argCount], (const void *) fileTable, + (sizeof(const char **) * fileCount)); + + /* + * Add to the argument count. + */ + argCount += fileCount; + } + + /* + * Null terminate the argument list and return it. + */ + argTable[argCount] = NULL; + + *retArgc = argCount; + *retArgv = argTable; + + return TRUE; +} + + +/* + * Make a NULL-terminated string out of an argc, argv pair. + * Returns TRUE if successful, or FALSE if the string is too long, + * with an error message given. This does not handle spaces within + * arguments correctly. + */ +BOOL +makeString( + int argc, + const char ** argv, + char * buf, + int bufLen +) +{ + int len; + + while (argc-- > 0) + { + len = strlen(*argv); + + if (len >= bufLen) + { + fprintf(stderr, "Argument string too long\n"); + + return FALSE; + } + + strcpy(buf, *argv++); + + buf += len; + bufLen -= len; + + if (argc) + *buf++ = ' '; + + bufLen--; + } + + *buf = '\0'; + + return TRUE; +} + + +/* + * 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. + */ +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; +} + + +/* + * Duplicate a string value using the chunk allocator. + * The returned string cannot be individually freed, but can only be freed + * with other strings when freeChunks is called. Returns NULL on failure. + */ +char * +chunkstrdup(const char * str) +{ + int len; + char * newStr; + + len = strlen(str) + 1; + newStr = getChunk(len); + + if (newStr) + memcpy(newStr, str, len); + + return newStr; +} + + +/* + * Free all chunks of memory that had been allocated since the last + * call to this routine. + */ +void +freeChunks(void) +{ + CHUNK * chunk; + + while (chunkList) + { + chunk = chunkList; + chunkList = chunk->next; + free((char *) chunk); + } +} + + +/* + * Write all of the supplied buffer out to a file. + * This does multiple writes as necessary. + * Returns the amount written, or -1 on an error. + */ +int +fullWrite(int fd, const char * buf, int len) +{ + int cc; + int total; + + total = 0; + + while (len > 0) + { + cc = write(fd, buf, len); + + if (cc < 0) + return -1; + + buf += cc; + total+= cc; + len -= cc; + } + + return total; +} + + +/* + * Read all of the supplied buffer from a file. + * This does multiple reads as necessary. + * Returns the amount read, or -1 on an error. + * A short read is returned on an end of file. + */ +int +fullRead(int fd, char * buf, int len) +{ + int cc; + int total; + + total = 0; + + while (len > 0) + { + cc = read(fd, buf, len); + + if (cc < 0) + return -1; + + if (cc == 0) + break; + + buf += cc; + total+= cc; + len -= cc; + } + + return total; +} + + +/* + * Read all of the supplied buffer from a file. + * This does multiple reads as necessary. + * Returns the amount read, or -1 on an error. + * A short read is returned on an end of file. + */ +int +recursive( const char *fileName, BOOL followLinks, const char* pattern, + int (*fileAction)(const char* fileName, const struct stat* statbuf), + int (*dirAction)(const char* fileName, const struct stat* statbuf)) +{ + int status; + struct stat statbuf; + struct dirent* next; + + if (followLinks) + status = stat(fileName, &statbuf); + else + status = lstat(fileName, &statbuf); + + if (status < 0) { + perror(fileName); + return( -1); + } + + if (S_ISREG(statbuf.st_mode)) { + if (match(fileName, pattern)) { + if (fileAction==NULL) + fprintf( stdout, "%s\n", fileName); + else + return(fileAction(fileName, &statbuf)); + } + } + else if (S_ISDIR(statbuf.st_mode)) { + if (dirAction==NULL) { + DIR *dir; + if (! match(fileName, pattern)) + return 1; + dir = opendir(fileName); + if (!dir) { + perror(fileName); + return( -1); + } + while ((next = readdir (dir)) != NULL) { + status = recursive(fileName, followLinks, pattern, fileAction, dirAction); + if (status < 0) { + closedir(dir); + return(status); + } + } + status = closedir (dir); + if (status < 0) { + perror(fileName); + return( -1); + } + } + else + return(dirAction(fileName, &statbuf)); + } + return( 1); + +} + + + +/* END CODE */ |