Initial revision
diff --git a/utility.c b/utility.c
new file mode 100644
index 0000000..6825971
--- /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, &times);
+	}
+
+	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 */