diff --git a/src/sbbs3/dirwrap.c b/src/sbbs3/dirwrap.c
new file mode 100644
index 0000000000000000000000000000000000000000..4d7d0402e3888d39240cf7dc839ccda199d34d05
--- /dev/null
+++ b/src/sbbs3/dirwrap.c
@@ -0,0 +1,469 @@
+/* dirwrap.c */
+
+/* Directory-related system-call wrappers */
+
+/* $Id$ */
+
+/****************************************************************************
+ * @format.tab-size 4		(Plain Text/Source Code File Header)			*
+ * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
+ *																			*
+ * Copyright 2000 Rob Swindell - http://www.synchro.net/copyright.html		*
+ *																			*
+ * 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.					*
+ * See the GNU General Public License for more details: gpl.txt or			*
+ * http://www.fsf.org/copyleft/gpl.html										*
+ *																			*
+ * Anonymous FTP access to the most recent released source is available at	*
+ * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
+ *																			*
+ * Anonymous CVS access to the development source and modification history	*
+ * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
+ * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
+ *     (just hit return, no password is necessary)							*
+ * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
+ *																			*
+ * For Synchronet coding style and modification guidelines, see				*
+ * http://www.synchro.net/source.html										*
+ *																			*
+ * You are encouraged to submit any modifications (preferably in Unix diff	*
+ * format) via e-mail to mods@synchro.net									*
+ *																			*
+ * Note: If this box doesn't appear square, then you need to fix your tabs.	*
+ ****************************************************************************/
+
+#ifdef _WIN32
+
+#include <windows.h>	/* WINAPI, etc */
+#include <io.h>			/* _findfirst */
+
+#elif defined __unix__
+
+#include <unistd.h>		/* usleep */
+#include <fcntl.h>		/* O_NOCCTY */
+#include <ctype.h>		/* toupper */
+
+#ifdef __FreeBSD__
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/kbio.h>
+#endif
+
+#include <sys/ioctl.h>	/* ioctl */
+
+#ifdef __GLIBC__		/* actually, BSD, but will work for now */
+#include <sys/vfs.h>    /* statfs() */
+#endif
+
+#endif
+
+#include <sys/types.h>	/* _dev_t */
+#include <sys/stat.h>	/* struct stat */
+
+#include <stdio.h>		/* sprintf */
+#include <stdlib.h>		/* rand */
+#include <errno.h>		/* ENOENT definitions */
+
+#include "dirwrap.h"	/* DLLCALL */
+
+/****************************************************************************/
+/* Return the filename portion of a full pathname							*/
+/****************************************************************************/
+char* DLLCALL getfname(char* path)
+{
+	char *fname;
+
+	fname=strrchr(path,'/');
+	if(fname==NULL) 
+		fname=strrchr(path,'\\');
+	if(fname!=NULL) 
+		fname++;
+	else 
+		fname=path;
+	return(fname);
+}
+
+#if !defined(__unix__)
+/****************************************************************************/
+/* Win32 (minimal) implementation of POSIX.2 glob() function				*/
+/* This code _may_ work on other DOS-based platforms (e.g. OS/2)			*/
+/****************************************************************************/
+static int glob_compare( const void *arg1, const void *arg2 )
+{
+   /* Compare all of both strings: */
+   return stricmp( * ( char** ) arg1, * ( char** ) arg2 );
+}
+
+#ifdef __BORLANDC__
+	#pragma argsused
+#endif
+
+int	DLLCALL	glob(const char *pattern, int flags, void* unused, glob_t* glob)
+{
+    struct	_finddata_t ff;
+	long	ff_handle;
+	size_t	found=0;
+	char	path[MAX_PATH+1];
+	char*	p;
+	char**	new_pathv;
+
+	if(!(flags&GLOB_APPEND)) {
+		glob->gl_pathc=0;
+		glob->gl_pathv=NULL;
+	}
+
+	ff_handle=_findfirst((char*)pattern,&ff);
+	while(ff_handle!=-1) {
+		if(!(flags&GLOB_ONLYDIR) || ff.attrib&_A_SUBDIR) {
+			if((new_pathv=realloc(glob->gl_pathv
+				,(glob->gl_pathc+1)*sizeof(char*)))==NULL) {
+				globfree(glob);
+				return(GLOB_NOSPACE);
+			}
+			glob->gl_pathv=new_pathv;
+
+			/* build the full pathname */
+			strcpy(path,pattern);
+			p=getfname(path);
+			*p=0;
+			strcat(path,ff.name);
+
+			if((glob->gl_pathv[glob->gl_pathc]=malloc(strlen(path)+2))==NULL) {
+				globfree(glob);
+				return(GLOB_NOSPACE);
+			}
+			strcpy(glob->gl_pathv[glob->gl_pathc],path);
+			if(flags&GLOB_MARK && ff.attrib&_A_SUBDIR)
+				strcat(glob->gl_pathv[glob->gl_pathc],"/");
+
+			glob->gl_pathc++;
+			found++;
+		}
+		if(_findnext(ff_handle, &ff)!=0) {
+			_findclose(ff_handle);
+			ff_handle=-1; 
+		} 
+	}
+
+	if(found==0)
+		return(GLOB_NOMATCH);
+
+	if(!(flags&GLOB_NOSORT)) {
+		qsort(glob->gl_pathv,found,sizeof(char*),glob_compare);
+	}
+
+	return(0);	/* success */
+}
+
+void DLLCALL globfree(glob_t* glob)
+{
+	size_t i;
+
+	if(glob==NULL)
+		return;
+
+	if(glob->gl_pathv!=NULL) {
+		for(i=0;i<glob->gl_pathc;i++)
+			if(glob->gl_pathv[i]!=NULL)
+				free(glob->gl_pathv[i]);
+
+		free(glob->gl_pathv);
+		glob->gl_pathv=NULL;
+	}
+	glob->gl_pathc=0;
+}
+
+#endif /* !defined(__unix__) */
+
+/****************************************************************************/
+/* POSIX directory operations using Microsoft _findfirst/next API.			*/
+/****************************************************************************/
+#if defined(_MSC_VER)
+DIR* opendir(const char* dirname)
+{
+	DIR*	dir;
+
+	if((dir=(DIR*)calloc(1,sizeof(DIR)))==NULL) {
+		errno=ENOMEM;
+		return(NULL);
+	}
+	sprintf(dir->filespec,"%.*s",sizeof(dir->filespec)-5,dirname);
+	if(*dir->filespec && dir->filespec[strlen(dir->filespec)-1]!='\\')
+		strcat(dir->filespec,"\\");
+	strcat(dir->filespec,"*.*");
+	dir->handle=_findfirst(dir->filespec,&dir->finddata);
+	if(dir->handle==-1) {
+		errno=ENOENT;
+		free(dir);
+		return(NULL);
+	}
+	return(dir);
+}
+struct dirent* readdir(DIR* dir)
+{
+	if(dir==NULL)
+		return(NULL);
+	if(dir->end==TRUE)
+		return(NULL);
+	if(dir->handle==-1)
+		return(NULL);
+	sprintf(dir->dirent.d_name,"%.*s",sizeof(struct dirent)-1,dir->finddata.name);
+	if(_findnext(dir->handle,&dir->finddata)!=0)
+		dir->end=TRUE;
+	return(&dir->dirent);
+}
+int closedir (DIR* dir)
+{
+	if(dir==NULL)
+		return(-1);
+	_findclose(dir->handle);
+	free(dir);
+	return(0);
+}
+void rewinddir(DIR* dir)
+{
+	if(dir==NULL)
+		return;
+	_findclose(dir->handle);
+	dir->end=FALSE;
+	dir->handle=_findfirst(dir->filespec,&dir->finddata);
+}
+#endif /* defined(_MSC_VER) */
+
+/****************************************************************************/
+/* Returns the time/date of the file in 'filename' in time_t (unix) format  */
+/****************************************************************************/
+time_t DLLCALL fdate(char *filename)
+{
+	struct stat st;
+
+	if(access(filename,0)==-1)
+		return(-1);
+
+	if(stat(filename, &st)!=0)
+		return(-1);
+
+	return(st.st_mtime);
+}
+
+/****************************************************************************/
+/* Returns the length of the file in 'filename'                             */
+/****************************************************************************/
+long DLLCALL flength(char *filename)
+{
+#ifdef __BORLANDC__	/* stat() doesn't work right */
+
+	long	handle;
+	struct _finddata_t f;
+
+	if(access(filename,0)==-1)
+		return(-1L);
+
+	if((handle=_findfirst(filename,&f))==-1)
+		return(-1);
+
+ 	_findclose(handle);
+
+	return(f.size);
+
+#else 
+
+	struct stat st;
+
+	if(access(filename,0)==-1)
+		return(-1L);
+
+	if(stat(filename, &st)!=0)
+		return(-1L);
+
+	return(st.st_size);
+
+#endif
+}
+
+/****************************************************************************/
+/* Checks the file system for the existence of one or more files.			*/
+/* Returns TRUE if it exists, FALSE if it doesn't.                          */
+/* 'filespec' may contain wildcards!										*/
+/****************************************************************************/
+BOOL DLLCALL fexist(char *filespec)
+{
+#ifdef _WIN32
+
+	long	handle;
+	struct _finddata_t f;
+
+	if(access(filespec,0)==-1 && !strchr(filespec,'*') && !strchr(filespec,'?'))
+		return(FALSE);
+
+	if((handle=_findfirst(filespec,&f))==-1)
+		return(FALSE);
+
+ 	_findclose(handle);
+
+ 	if(f.attrib&_A_SUBDIR)
+		return(FALSE);
+
+	return(TRUE);
+
+#elif defined(__unix__)	/* portion by cmartin */
+
+	glob_t g;
+    int c;
+    int l;
+
+	if(access(filespec,0)==-1 && !strchr(filespec,'*') && !strchr(filespec,'?'))
+		return(FALSE);
+
+    // start the search
+    glob(filespec, GLOB_MARK | GLOB_NOSORT, NULL, &g);
+
+    if (!g.gl_pathc) {
+	    // no results
+    	globfree(&g);
+    	return FALSE;
+    }
+
+    // make sure it's not a directory
+	c = g.gl_pathc;
+    while (c--) {
+    	l = strlen(g.gl_pathv[c]);
+    	if (l && g.gl_pathv[c][l-1] != '/') {
+        	globfree(&g);
+            return TRUE;
+        }
+    }
+        
+    globfree(&g);
+    return FALSE;
+
+#else
+
+#warning "fexist() port needs to support wildcards!"
+
+	return(FALSE);
+
+#endif
+}
+
+/****************************************************************************/
+/* Returns TRUE if the filename specified is a directory					*/
+/****************************************************************************/
+BOOL DLLCALL isdir(char *filename)
+{
+	struct stat st;
+
+	if(stat(filename, &st)!=0)
+		return(FALSE);
+
+	return((st.st_mode&S_IFDIR) ? TRUE : FALSE);
+}
+
+/****************************************************************************/
+/* Returns the attributes (mode) for specified 'filename'					*/
+/****************************************************************************/
+int DLLCALL getfattr(char* filename)
+{
+#ifdef _WIN32
+	long handle;
+	struct _finddata_t	finddata;
+
+	if((handle=_findfirst(filename,&finddata))==-1) {
+		errno=ENOENT;
+		return(-1);
+	}
+	_findclose(handle);
+	return(finddata.attrib);
+#else
+	struct stat st;
+
+	if(stat(filename, &st)!=0) {
+		errno=ENOENT;
+		return(-1L);
+	}
+
+	return(st.st_mode);
+#endif
+}
+
+/****************************************************************************/
+/* Return free disk space in bytes (up to a maximum of 4GB)					*/
+/****************************************************************************/
+#ifdef _WIN32
+	typedef BOOL(WINAPI * GetDiskFreeSpaceEx_t)
+		(LPCTSTR,PULARGE_INTEGER,PULARGE_INTEGER,PULARGE_INTEGER); 
+#endif
+
+ulong DLLCALL getfreediskspace(char* path)
+{
+#ifdef _WIN32
+	char			root[16];
+	DWORD			TotalNumberOfClusters;
+	DWORD			NumberOfFreeClusters;
+	DWORD			BytesPerSector;
+	DWORD			SectorsPerCluster;
+	ULARGE_INTEGER	avail;
+	ULARGE_INTEGER	size;
+	static HINSTANCE hK32;
+	GetDiskFreeSpaceEx_t GetDiskFreeSpaceEx;
+
+	if(hK32 == NULL)
+		hK32 = LoadLibrary("KERNEL32");
+	GetDiskFreeSpaceEx 
+		= (GetDiskFreeSpaceEx_t)GetProcAddress(hK32,"GetDiskFreeSpaceExA");
+ 
+	if (GetDiskFreeSpaceEx!=NULL) {	/* Windows 95-OSR2 or later */
+		if(!GetDiskFreeSpaceEx(
+			path,		// pointer to the directory name
+			&avail,		// receives the number of bytes on disk avail to the caller
+			&size,		// receives the number of bytes on disk
+			NULL))		// receives the free bytes on disk
+			return(0);
+#ifdef _ANONYMOUS_STRUCT
+		if(avail.HighPart)
+#else
+		if(avail.u.HighPart)
+#endif
+			return(0xffffffff);	/* 4GB max */
+
+#ifdef _ANONYMOUS_STRUCT
+		return(avail.LowPart);
+#else
+		return(avail.u.LowPart);
+#endif
+	}
+
+	/* Windows 95 (old way), limited to 2GB */
+	sprintf(root,"%.3s",path);
+	if(!GetDiskFreeSpace(
+		root,					// pointer to root path
+		&SectorsPerCluster,		// pointer to sectors per cluster
+		&BytesPerSector,		// pointer to bytes per sector
+		&NumberOfFreeClusters,	// pointer to number of free clusters
+		&TotalNumberOfClusters  // pointer to total number of clusters
+		))
+		return(0);
+
+	return(NumberOfFreeClusters*SectorsPerCluster*BytesPerSector);
+
+
+/* statfs is also used under FreeBSD */
+#elif defined(__GLIBC__) || defined(__FreeBSD__)
+
+	struct statfs fs;
+
+    if (statfs(path, &fs) < 0)
+    	return 0;
+
+    return fs.f_bsize * fs.f_bavail;
+    
+#else
+
+	#warning OS-specific code needed here
+	return(0);
+
+#endif
+}
diff --git a/src/sbbs3/dirwrap.h b/src/sbbs3/dirwrap.h
new file mode 100644
index 0000000000000000000000000000000000000000..5d382e44357484d9eb5c7df92feeba824a06d609
--- /dev/null
+++ b/src/sbbs3/dirwrap.h
@@ -0,0 +1,212 @@
+/* dirwrap.h */
+
+/* Directory system-call wrappers */
+
+/* $Id$ */
+
+/****************************************************************************
+ * @format.tab-size 4		(Plain Text/Source Code File Header)			*
+ * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
+ *																			*
+ * Copyright 2000 Rob Swindell - http://www.synchro.net/copyright.html		*
+ *																			*
+ * 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.					*
+ * See the GNU General Public License for more details: gpl.txt or			*
+ * http://www.fsf.org/copyleft/gpl.html										*
+ *																			*
+ * Anonymous FTP access to the most recent released source is available at	*
+ * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
+ *																			*
+ * Anonymous CVS access to the development source and modification history	*
+ * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
+ * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
+ *     (just hit return, no password is necessary)							*
+ * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
+ *																			*
+ * For Synchronet coding style and modification guidelines, see				*
+ * http://www.synchro.net/source.html										*
+ *																			*
+ * You are encouraged to submit any modifications (preferably in Unix diff	*
+ * format) via e-mail to mods@synchro.net									*
+ *																			*
+ * Note: If this box doesn't appear square, then you need to fix your tabs.	*
+ ****************************************************************************/
+
+#ifndef _DIRWRAP_H
+#define _DIRWRAP_H
+
+#include "gen_defs.h"	/* ulong */
+
+#ifdef DLLEXPORT
+#undef DLLEXPORT
+#endif
+#ifdef DLLCALL
+#undef DLLCALL
+#endif
+
+#ifdef _WIN32
+	#ifdef WRAPPER_DLL
+		#define DLLEXPORT	__declspec(dllexport)
+	#else
+		#define DLLEXPORT	__declspec(dllimport)
+	#endif
+	#ifdef __BORLANDC__
+		#define DLLCALL __stdcall
+	#else
+		#define DLLCALL
+	#endif
+#else	/* !_WIN32 */
+	#define DLLEXPORT
+	#define DLLCALL
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _MSC_VER
+	#include "msdirent.h"
+#else
+	#include <dirent.h>	/* POSIX directory functions */
+#endif
+
+/****************/
+/* RTL-specific */
+/****************/
+
+#ifdef __unix__
+
+	#define ALLFILES "*"	/* matches all files in a directory */
+	#define MKDIR(dir)		_mkdir(dir)
+	#define RMDIR(dir)		_rmdir(dir)
+	#define FULLPATH(a,r,l)	_fullpath(a,r,l)
+	#include <glob.h>		/* POSIX.2 directory pattern matching function */
+
+#else	
+
+	#define ALLFILES "*.*"	/* matches all files in a directory */
+	#define MKDIR(dir)		mkdir(dir,0777)
+	#define RMDIR(dir)		rmdir(dir)
+	#define FULLPATH(a,r,l)	realpath(r,a)
+	
+	/* glob-compatible findfirst/findnext wrapper */
+
+	typedef struct 
+	{
+			size_t gl_pathc;			/* Count of paths matched so far */
+			char **gl_pathv;			/* List of matched pathnames. */
+			size_t gl_offs;				/* Slots to reserve in 'gl_pathv'. */
+	} glob_t;
+
+	/* Bits set in the FLAGS argument to `glob'.  */
+	#define GLOB_ERR        (1 << 0)	/* Return on read errors.  */
+	#define GLOB_MARK       (1 << 1)	/* Append a slash to each name.  */
+	#define GLOB_NOSORT     (1 << 2)	/* Don't sort the names.  */
+	#define GLOB_DOOFFS     (1 << 3)	/* Insert PGLOB->gl_offs NULLs.  */
+	#define GLOB_NOCHECK    (1 << 4)	/* If nothing matches, return the pattern.  */
+	#define GLOB_APPEND     (1 << 5)	/* Append to results of a previous call.  */
+	#define GLOB_NOESCAPE   (1 << 6)	/* Backslashes don't quote metacharacters.  */
+	#define GLOB_PERIOD     (1 << 7)	/* Leading `.' can be matched by metachars.  */
+	#define GLOB_MAGCHAR    (1 << 8)	/* Set in gl_flags if any metachars seen.  */
+	#define GLOB_ALTDIRFUNC (1 << 9)	/* Use gl_opendir et al functions.  */
+	#define GLOB_BRACE      (1 << 10)	/* Expand "{a,b}" to "a" "b".  */
+	#define GLOB_NOMAGIC    (1 << 11)	/* If no magic chars, return the pattern.  */
+	#define GLOB_TILDE      (1 << 12)	/* Expand ~user and ~ to home directories. */
+	#define GLOB_ONLYDIR    (1 << 13)	/* Match only directories.  */
+	#define GLOB_TILDE_CHECK (1 << 14)	/* Like GLOB_TILDE but return an error
+										  if the user name is not available.  */
+	/* Error returns from `glob'.  */
+	#define GLOB_NOSPACE    1       /* Ran out of memory.  */
+	#define GLOB_ABORTED    2       /* Read error.  */
+	#define GLOB_NOMATCH    3       /* No matches found.  */
+	#define GLOB_NOSYS      4       /* Not implemented.  */
+
+	DLLEXPORT int	DLLCALL	glob(const char *pattern, int flags, void* unused, glob_t*);
+	DLLEXPORT void	DLLCALL globfree(glob_t*);
+
+#endif
+
+/*****************************/
+/* POSIX Directory Functions */
+/*****************************/
+#if defined(_MSC_VER)
+	/* dirent structure returned by readdir().
+	 */
+	struct dirent
+	{	
+		char        d_name[260];	/* filename */
+	};
+
+	/* DIR type returned by opendir().  The members of this structure
+	 * must not be accessed by application programs.
+	 */
+	typedef struct
+	{
+		char				filespec[260];
+		struct dirent		dirent;
+		long				handle;
+		struct _finddata_t	finddata;
+		BOOL				end;		/* End of directory flag */
+	} DIR;
+
+
+	/* Prototypes.
+	 */
+	DIR            *	opendir  (const char *__dirname);
+	struct dirent  *	readdir  (DIR *__dir);
+	int                 closedir (DIR *__dir);
+	void                rewinddir(DIR *__dir);
+#endif
+
+/**********/
+/* Macros */
+/**********/
+
+/* POSIX readdir convenience macro */
+#ifndef DIRENT
+#define DIRENT struct dirent	
+#endif
+
+#if defined(__unix__)
+#define BACKSLASH	'/'
+#else /* MS-DOS based OS */
+#define BACKSLASH	'\\'
+#endif
+
+#if defined(_MSC_VER) || defined(__MINGW32__)
+
+#define CHMOD(s,m)		_chmod(s,m)
+#define PUTENV  		_putenv
+#define GETCWD  		_getcwd
+
+#elif defined(__BORLANDC__)
+
+#define CHMOD(p,a)		_rtl_chmod(p,1,a) 	/* _chmod obsolete in 4.x */
+#define PUTENV  		putenv
+#define GETCWD  		getcwd
+
+#else	/* ??? */
+
+#define CHMOD(s,m)		chmod(s,m)
+#define PUTENV  		putenv
+#define GETCWD  		getcwd
+
+#endif
+
+/* General file system wrappers for all platforms and compilers */
+DLLEXPORT BOOL		DLLCALL fexist(char *filespec);
+DLLEXPORT long		DLLCALL flength(char *filename);
+DLLEXPORT time_t	DLLCALL fdate(char *filename);
+DLLEXPORT BOOL		DLLCALL	isdir(char *filename);
+DLLEXPORT char*		DLLCALL getfname(char* path);
+DLLEXPORT int		DLLCALL getfattr(char* filename);
+DLLEXPORT ulong		DLLCALL getfreediskspace(char* path);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* Don't add anything after this line */