| /* |
| * 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 */ |