From f3f66b77d5e2c564dd5ee8ded6cae62bc5b62ddb Mon Sep 17 00:00:00 2001
From: rswindell <>
Date: Sat, 6 Apr 2002 00:40:31 +0000
Subject: [PATCH] New directory for cross-platform development files (wrappers,
 etc).

---
 src/xpdev/conwrap.c    | 120 +++++++++++
 src/xpdev/conwrap.h    |  52 +++++
 src/xpdev/dirwrap.c    | 469 +++++++++++++++++++++++++++++++++++++++++
 src/xpdev/dirwrap.h    | 212 +++++++++++++++++++
 src/xpdev/filewrap.c   | 179 ++++++++++++++++
 src/xpdev/filewrap.h   | 128 +++++++++++
 src/xpdev/gen_defs.h   | 180 ++++++++++++++++
 src/xpdev/genwrap.c    | 118 +++++++++++
 src/xpdev/genwrap.h    | 180 ++++++++++++++++
 src/xpdev/sockwrap.h   | 127 +++++++++++
 src/xpdev/threadwrap.c |  91 ++++++++
 src/xpdev/threadwrap.h | 105 +++++++++
 src/xpdev/wraptest.c   |  65 ++++++
 13 files changed, 2026 insertions(+)
 create mode 100644 src/xpdev/conwrap.c
 create mode 100644 src/xpdev/conwrap.h
 create mode 100644 src/xpdev/dirwrap.c
 create mode 100644 src/xpdev/dirwrap.h
 create mode 100644 src/xpdev/filewrap.c
 create mode 100644 src/xpdev/filewrap.h
 create mode 100644 src/xpdev/gen_defs.h
 create mode 100644 src/xpdev/genwrap.c
 create mode 100644 src/xpdev/genwrap.h
 create mode 100644 src/xpdev/sockwrap.h
 create mode 100644 src/xpdev/threadwrap.c
 create mode 100644 src/xpdev/threadwrap.h
 create mode 100644 src/xpdev/wraptest.c

diff --git a/src/xpdev/conwrap.c b/src/xpdev/conwrap.c
new file mode 100644
index 0000000000..bfaeea3121
--- /dev/null
+++ b/src/xpdev/conwrap.c
@@ -0,0 +1,120 @@
+/*
+	conwrap.c -- To give DOS's getch() function to Linux for use in Synchronet
+    Casey Martin 2000
+*/
+
+/* $Id$ */
+
+/* @format.tab-size 4, @format.use-tabs true */
+
+#ifdef __unix__
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <termios.h>
+
+/* Do the correct thing under BSD */
+#ifndef __FreeBSD__
+#include <sys/kd.h>
+#endif
+#ifdef __FreeBSD__
+#include <sys/kbio.h>
+#endif
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <signal.h>
+
+#include "conwrap.h"	// Verify prototypes
+
+struct termios current;            // our current term settings
+struct termios original;           // old termios settings
+struct timeval timeout = {0, 0};   // passed in select() call
+fd_set inp;                        // ditto
+int beensetup = 0;                 // has _termios_setup() been called?
+
+/*
+	I'm using a variable function here simply for the sake of speed.  The
+    termios functions must be called before a kbhit() can be successful, so
+    on the first call, we just set up the terminal, point to variable function
+    to kbhit_norm(), and then call the new function.  Otherwise, testing would
+	be required on every call to determine if termios has already been setup.
+
+    Maybe I'm being way too anal, though.
+*/
+
+/* Resets the termios to its previous state */
+void _termios_reset(void)
+{
+	tcsetattr(0, TCSANOW, &original);
+}
+
+/************************************************
+  This pair of functions handles Ctrl-Z presses
+************************************************/
+
+void _sighandler_stop(int sig)
+{
+    // clean up the terminal
+    _termios_reset();
+
+    // ... and stop
+	kill(getpid(), SIGSTOP);
+}
+
+void _sighandler_cont(int sig)
+{
+    // restore terminal
+	tcsetattr(0, TCSANOW, &current);
+}
+
+
+/* Prepares termios for non-blocking action */
+void _termios_setup(void)
+{
+	beensetup = 1;
+    
+	tcgetattr(0, &original);
+  
+	memcpy(&current, &original, sizeof(struct termios));
+	current.c_cc[VMIN] = 1;           // read() will return with one char
+	current.c_cc[VTIME] = 0;          // read() blocks forever
+	current.c_lflag &= ~ICANON;       // character mode
+    current.c_lflag &= ~ECHO;         // turn off echoing
+	tcsetattr(0, TCSANOW, &current);
+
+    // Let's install an exit function, also.  This way, we can reset
+    // the termios silently
+    atexit(_termios_reset);
+
+    // install the Ctrl-Z handler
+    signal(SIGSTOP, _sighandler_stop);
+    signal(SIGCONT, _sighandler_cont);
+}
+
+
+int kbhit(void)
+{
+	// set up select() args
+	FD_ZERO(&inp);
+	FD_SET(0, &inp);
+
+	return select(1, &inp, NULL, NULL, &timeout);
+}
+
+int getch(void)
+{
+	char c;
+
+    if (!beensetup)
+    	// I hate to test for this every time, but this shouldn't be
+        // called that often anyway...
+    	_termios_setup();
+
+    // get a char out of stdin
+    read(0, &c, 1);
+
+    return c;
+}
+
+#endif // __unix__
diff --git a/src/xpdev/conwrap.h b/src/xpdev/conwrap.h
new file mode 100644
index 0000000000..e1153fb7d2
--- /dev/null
+++ b/src/xpdev/conwrap.h
@@ -0,0 +1,52 @@
+/* conwrap.h */
+
+/* Synchronet local console I/O wrapppers */
+
+/* $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 _CONWRAP_H
+#define _CONWRAP_H
+
+#ifdef __unix__
+
+	int kbhit(void);
+	int getch(void);
+
+#else	/* DOS-Based */
+
+	#include <conio.h>
+
+#endif
+
+#endif	/* _CONWRAP_H */
diff --git a/src/xpdev/dirwrap.c b/src/xpdev/dirwrap.c
new file mode 100644
index 0000000000..4d7d0402e3
--- /dev/null
+++ b/src/xpdev/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/xpdev/dirwrap.h b/src/xpdev/dirwrap.h
new file mode 100644
index 0000000000..5d382e4435
--- /dev/null
+++ b/src/xpdev/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 */
diff --git a/src/xpdev/filewrap.c b/src/xpdev/filewrap.c
new file mode 100644
index 0000000000..8b0b5ad6aa
--- /dev/null
+++ b/src/xpdev/filewrap.c
@@ -0,0 +1,179 @@
+/* filewrap.c */
+
+/* File-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.	*
+ ****************************************************************************/
+
+/* OS-specific */
+#if defined(__unix__)
+
+#include <glob.h>       /* glob() wildcard matching */
+#include <string.h>     /* strlen() */
+#include <unistd.h>     /* getpid() */
+#include <fcntl.h>      /* fcntl() file/record locking */
+
+#endif
+
+/* ANSI */
+#include <sys/types.h>	/* _dev_t */
+#include <sys/stat.h>	/* struct stat */
+
+#include "filewrap.h"	/* Verify prototypes */
+
+/****************************************************************************/
+/* Returns the modification time of the file in 'fd'						*/
+/****************************************************************************/
+time_t DLLCALL filetime(int fd)
+{
+	struct stat st;
+
+	if(fstat(fd, &st)!=0)
+		return(-1);
+
+	return(st.st_mtime);
+}
+
+#if defined(__unix__)
+
+/****************************************************************************/
+/* Returns the length of the file in 'fd'									*/
+/****************************************************************************/
+long DLLCALL filelength(int fd)
+{
+	struct stat st;
+
+	if(fstat(fd, &st)!=0)
+		return(-1L);
+
+	return(st.st_size);
+}
+
+/* Sets a lock on a portion of a file */
+int DLLCALL lock(int fd, long pos, int len)
+{
+	int	flags;
+ 	struct flock alock;
+
+	if((flags=fcntl(fd,F_GETFL))<0)
+		return -1;
+
+	if(flags==O_RDONLY)
+		alock.l_type = F_RDLCK; // set read lock to prevent writes
+	else
+		alock.l_type = F_WRLCK; // set write lock to prevent all access
+	alock.l_whence = L_SET;	  // SEEK_SET
+	alock.l_start = pos;
+	alock.l_len = len;
+
+	return fcntl(fd, F_SETLK, &alock);
+}
+
+/* Removes a lock from a file record */
+int DLLCALL unlock(int fd, long pos, int len)
+{
+	struct flock alock;
+
+	alock.l_type = F_UNLCK;   // remove the lock
+	alock.l_whence = L_SET;
+	alock.l_start = pos;
+	alock.l_len = len;
+	return fcntl(fd, F_SETLK, &alock);
+}
+
+/* Opens a file in specified sharing (file-locking) mode */
+int DLLCALL sopen(char *fn, int access, int share)
+{
+	int fd;
+	struct flock alock;
+
+	if ((fd = open(fn, access, S_IREAD|S_IWRITE)) < 0)
+		return -1;
+
+	if (share == SH_DENYNO)
+		// no lock needed
+		return fd;
+
+	alock.l_type = share;
+	alock.l_whence = L_SET;
+	alock.l_start = 0;
+	alock.l_len = 0;       // lock to EOF
+
+	if (fcntl(fd, F_SETLK, &alock) < 0) {
+		close(fd);
+		return -1;
+	}
+
+	return fd;
+}
+
+#elif defined _MSC_VER || defined __MINGW32__
+
+#include <io.h>				/* tell */
+#include <stdio.h>			/* SEEK_SET */
+#include <sys/locking.h>	/* _locking */
+
+/* Fix MinGW locking.h typo */
+#if defined LK_UNLOCK && !defined LK_UNLCK
+	#define LK_UNLCK LK_UNLOCK
+#endif
+
+int DLLCALL lock(int file, long offset, int size) 
+{
+	int	i;
+	long	pos;
+   
+	pos=tell(file);
+	if(offset!=pos)
+		lseek(file, offset, SEEK_SET);
+	i=_locking(file,LK_NBLCK,size);
+	if(offset!=pos)
+		lseek(file, pos, SEEK_SET);
+	return(i);
+}
+
+int DLLCALL unlock(int file, long offset, int size)
+{
+	int	i;
+	long	pos;
+   
+	pos=tell(file);
+	if(offset!=pos)
+		lseek(file, offset, SEEK_SET);
+	i=_locking(file,LK_UNLCK,size);
+	if(offset!=pos)
+		lseek(file, pos, SEEK_SET);
+	return(i);
+}
+
+#endif	/* !Unix && (MSVC || MinGW) */
diff --git a/src/xpdev/filewrap.h b/src/xpdev/filewrap.h
new file mode 100644
index 0000000000..4e8088f561
--- /dev/null
+++ b/src/xpdev/filewrap.h
@@ -0,0 +1,128 @@
+/* filewrap.h */
+
+/* File 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 _FILEWRAP_H
+#define _FILEWRAP_H
+
+/**********/
+/* Macros */
+/**********/
+
+#if defined(_WIN32)
+
+	#include <io.h>				/* _sopen */
+	#include <sys/stat.h>		/* S_IREAD */
+	#include <fcntl.h>			/* O_BINARY */
+	#include <windows.h>		/* OF_SHARE_ */
+
+	#define sopen(f,o,s)		_sopen(f,o,s,S_IREAD|S_IWRITE)
+	#define close(f)			_close(f)
+							
+	#ifndef SH_DENYNO
+	#define SH_DENYNO			OF_SHARE_DENY_NONE
+	#define SH_DENYWR			OF_SHARE_DENY_WRITE
+	#define SH_DENYRW			OF_SHARE_EXCLUSIVE
+	#endif
+	#ifndef O_DENYNONE
+	#define O_DENYNONE			SH_DENYNO
+	#endif
+
+#elif defined(__unix__)
+
+	#include <fcntl.h>
+
+	#define O_TEXT		0		/* all files in binary mode on Unix */
+	#define O_BINARY	0		/* all files in binary mode on Unix */
+	#define O_DENYNONE  (1<<31)	/* req'd for Baja/nopen compatibility */
+
+	#define SH_DENYNO	2          // no locks
+	#define SH_DENYRW	F_WRLCK	   // exclusive lock
+	#define SH_DENYWR   F_RDLCK    // shareable lock
+	
+	#define chsize(fd,size)		ftruncate(fd,size)
+	#define tell(fd)			lseek(fd,0,SEEK_CUR)
+
+#endif
+
+/**************/
+/* Prototypes */
+/**************/
+
+#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
+
+#if !defined(__BORLANDC__)
+	DLLEXPORT int	DLLCALL	lock(int fd, long pos, int len);
+	DLLEXPORT int	DLLCALL unlock(int fd, long pos, int len);
+#endif
+
+#if defined(__unix__)
+	DLLEXPORT int	DLLCALL sopen(char *fn, int access, int share);
+	DLLEXPORT long	DLLCALL filelength(int fd);
+#endif
+
+DLLEXPORT time_t	DLLCALL filetime(int fd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* Don't add anything after this line */
diff --git a/src/xpdev/gen_defs.h b/src/xpdev/gen_defs.h
new file mode 100644
index 0000000000..2d110968e4
--- /dev/null
+++ b/src/xpdev/gen_defs.h
@@ -0,0 +1,180 @@
+/* gen_defs.h */
+
+/* General(ly useful) constant, macro, and type definitions */
+
+/* $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 _GEN_DEFS_H
+#define _GEN_DEFS_H
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN	/* Don't bring in excess baggage */
+#include <windows.h>
+#endif
+
+#include <sys/types.h>
+
+									/* Control characters */
+#define STX 	0x02				/* Start of text			^B	*/
+#define ETX 	0x03				/* End of text				^C	*/
+#define BEL		0x07				/* Bell/beep				^G	*/
+#define FF		0x0c				/* Form feed				^L	*/
+#define ESC 	0x1b				/* Escape					^[	*/
+#define DEL		0x7f				/* Delete					^BS	*/
+#define BS		'\b'				/* Back space				^H	*/
+#define TAB 	'\t'				/* Horizontal tabulation	^I	*/
+#define LF		'\n'				/* Line feed				^J	*/
+#define CR		'\r'				/* Carriage return			^M	*/
+#define SP		' '					/* Space						*/
+
+enum {
+	 CTRL_A=1
+	,CTRL_B
+	,CTRL_C
+	,CTRL_D	
+	,CTRL_E
+	,CTRL_F
+	,CTRL_G
+	,CTRL_H
+	,CTRL_I
+	,CTRL_J
+	,CTRL_K
+	,CTRL_L
+	,CTRL_M
+	,CTRL_N
+	,CTRL_O
+	,CTRL_P
+	,CTRL_Q
+	,CTRL_R
+	,CTRL_S
+	,CTRL_T
+	,CTRL_U
+	,CTRL_V
+	,CTRL_W
+	,CTRL_X
+	,CTRL_Y
+	,CTRL_Z
+};
+
+#ifndef MAX_PATH
+	#ifdef MAXPATHLEN
+		#define MAX_PATH MAXPATHLEN	/* clib.h */
+	#elif defined PATH_MAX
+		#define MAX_PATH PATH_MAX
+	#elif defined _MAX_PATH
+		#define MAX_PATH _MAX_PATH
+	#else
+		#define MAX_PATH 260		
+	#endif
+#endif
+
+/* Unsigned type short-hands	*/
+#ifndef uchar
+	#define uchar	unsigned char
+#endif
+#ifndef __GLIBC__
+	#ifndef ushort
+	#define ushort  unsigned short
+	#define uint    unsigned int
+	#define ulong   unsigned long
+	#endif
+#endif
+
+/* Windows Types */
+#ifndef BYTE
+#define BYTE	uchar
+#endif
+#ifndef WORD
+#define WORD	ushort
+#endif
+#ifndef DWORD
+#define DWORD	ulong
+#endif
+#ifndef BOOL
+#define BOOL	int
+#endif
+#ifndef TRUE
+#define TRUE	1
+#define FALSE	0
+#endif
+#ifndef HANDLE
+#define HANDLE	void*
+#endif
+
+#define SAFECOPY(dst,src)	sprintf(dst,"%.*s",sizeof(dst)-1,src)
+
+/****************************************************************************/
+/* MALLOC/FREE Macros for various compilers and environments				*/
+/* MALLOC is used for allocations of 64k or less							*/
+/* FREE is used to free buffers allocated with MALLOC						*/
+/* LMALLOC is used for allocations of possibly larger than 64k				*/
+/* LFREE is used to free buffers allocated with LMALLOC 					*/
+/* REALLOC is used to re-size a previously MALLOCed or LMALLOCed buffer 	*/
+/* FAR16 is used to create a far (32-bit) pointer in 16-bit compilers		*/
+/* HUGE16 is used to create a huge (32-bit) pointer in 16-bit compilers 	*/
+/****************************************************************************/
+#if defined(__COMPACT__) || defined(__LARGE__) || defined(__HUGE__)
+	#define HUGE16 huge
+	#define FAR16 far
+	#if defined(__TURBOC__)
+		#define REALLOC(x,y) farrealloc(x,y)
+		#define LMALLOC(x) farmalloc(x)
+		#define MALLOC(x) farmalloc(x)
+		#define LFREE(x) farfree(x)
+		#define FREE(x) farfree(x)
+	#elif defined(__WATCOMC__)
+		#define REALLOC realloc
+		#define LMALLOC(x) halloc(x,1)	/* far heap, but slow */
+		#define MALLOC malloc			/* far heap, but 64k max */
+		#define LFREE hfree
+		#define FREE free
+	#else	/* Other 16-bit Compiler */
+		#define REALLOC realloc
+		#define LMALLOC malloc
+		#define MALLOC malloc
+		#define LFREE free
+		#define FREE free
+	#endif
+#else		/* 32-bit Compiler or Small Memory Model */
+	#define HUGE16
+	#define FAR16
+	#define REALLOC realloc
+	#define LMALLOC malloc
+	#define MALLOC malloc
+	#define LFREE free
+	#define FREE free
+#endif
+
+
+#endif /* Don't add anything after this #endif statement */
diff --git a/src/xpdev/genwrap.c b/src/xpdev/genwrap.c
new file mode 100644
index 0000000000..3679ae6f49
--- /dev/null
+++ b/src/xpdev/genwrap.c
@@ -0,0 +1,118 @@
+/* genwrap.c */
+
+/* General cross-platform development 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.	*
+ ****************************************************************************/
+
+#include <string.h>     /* strlen() */
+
+#include "genwrap.h"	/* Verify prototypes */
+
+/****************************************************************************/
+/* Convert ASCIIZ string to upper case										*/
+/****************************************************************************/
+#ifdef __unix__
+char* SMBCALL strupr(char* str)
+{
+	char*	p=str;
+
+	while(*p) {
+		*p=toupper(*p);
+		p++;
+	}
+	return(str);
+}
+/****************************************************************************/
+/* Convert ASCIIZ string to lower case										*/
+/****************************************************************************/
+char* SMBCALL strlwr(char* str)
+{
+	char*	p=str;
+
+	while(*p) {
+		*p=tolower(*p);
+		p++;
+	}
+	return(str);
+}
+/****************************************************************************/
+/* Reverse characters of a string (provided by amcleod)						*/
+/****************************************************************************/
+char* strrev(char* str)
+{
+    char t, *i=str, *j=str+strlen(str);
+
+    while (i<j) {
+        t=*i; *(i++)=*(--j); *j=t;
+    }
+    return str;
+}
+#endif
+
+/****************************************************************************/
+/* Generate a tone at specified frequency for specified milliseconds		*/
+/* Thanks to Casey Martin for this code										*/
+/****************************************************************************/
+#ifdef __unix__
+void DLLCALL xp_beep(int freq, int dur)
+{
+	static int console_fd=-1;
+
+	if(console_fd == -1) 
+  		console_fd = open("/dev/console", O_NOCTTY);
+	
+	if(console_fd != -1) {
+		ioctl(console_fd, KIOCSOUND, (int) (1193180 / freq));
+		mswait(dur);
+		ioctl(console_fd, KIOCSOUND, 0);	/* turn off tone */
+	}
+}
+#endif
+
+/****************************************************************************/
+/* Return random number between 0 and n-1									*/
+/****************************************************************************/
+#ifndef __BORLANDC__
+int DLLCALL xp_random(int n)
+{
+	float f;
+
+	if(n<2)
+		return(0);
+	f=(float)rand()/(float)RAND_MAX;
+
+	return((int)(n*f));
+}
+#endif
+
diff --git a/src/xpdev/genwrap.h b/src/xpdev/genwrap.h
new file mode 100644
index 0000000000..759b526c5d
--- /dev/null
+++ b/src/xpdev/genwrap.h
@@ -0,0 +1,180 @@
+/* genwrap.h */
+
+/* General cross-platform development 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 _GENWRAP_H
+#define _GENWRAP_H
+
+#include <stdio.h>		/* sprintf */
+#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
+
+/*********************/
+/* Compiler-specific */
+/*********************/
+
+/* Compiler Description */
+#if defined(__BORLANDC__)
+
+	#define COMPILER_DESC(str) sprintf(str,"BCC %X.%02X" \
+		,__BORLANDC__>>8,__BORLANDC__&0xff);	
+
+#elif defined(_MSC_VER)
+
+	#define COMPILER_DESC(str) sprintf(str,"MSC %u", _MSC_VER);
+
+/***
+#elif defined(__GNUC__) && defined(__GLIBC__)
+
+	#define COMPILER_DESC(str) sprintf(str,"GCC %u.%02u (GLIBC %u.%u)" \
+		,__GNUC__,__GNUC_MINOR__,__GLIBC__,__GLIBC_MINOR__);
+***/
+
+#elif defined(__GNUC__)
+
+	#define COMPILER_DESC(str) sprintf(str,"GCC %u.%02u" \
+		,__GNUC__,__GNUC_MINOR__);
+
+#else /* Unknown compiler */
+
+	#define COMPILER_DESC(str) strcpy(str,"UNKNOWN COMPILER");
+
+#endif
+
+/**********/
+/* Macros */
+/**********/
+
+/* Target Platform Description */
+#if defined(_WIN64)
+	#define PLATFORM_DESC	"Win64"
+#elif defined(_WIN32)
+	#define PLATFORM_DESC	"Win32"
+#elif defined(__OS2__)
+	#define PLATFORM_DESC	"OS/2"
+#elif defined(__MSDOS__)
+	#define PLATFORM_DESC	"DOS"
+#elif defined(__linux__)
+	#define PLATFORM_DESC	"Linux"
+#elif defined(__FreeBSD__)
+	#define PLATFORM_DESC	"FreeBSD"
+#elif defined(BSD)
+	#define PLATFORM_DESC	"BSD"
+#elif defined(__unix__)
+	#define PLATFORM_DESC	"Unix"
+#else
+	#warning "Need to describe target platform"
+	#define PLATFORM_DESC	"UNKNOWN"
+#endif
+
+/*********************/
+/* String Functionss */
+/*********************/
+
+#if defined(_MSC_VER) || defined(__MINGW32__)
+	#define snprintf		_snprintf
+#endif
+
+#if !defined(_MSC_VER) && !defined(__BORLANDC__)
+	DLLEXPORT char* DLLCALL ultoa(ulong, char*, int radix);
+#endif
+
+#if defined(__unix__)
+	DLLEXPORT char*	DLLCALL strupr(char* str);
+	DLLEXPORT char*	DLLCALL strlwr(char* str);
+	DLLEXPORT char* DLLCALL strrev(char* str);
+	#if !defined(stricmp)
+		#define stricmp(x,y)		strcasecmp(x,y)
+		#define strnicmp(x,y,z)		strncasecmp(x,y,z)
+	#endif
+#endif
+
+/****************************/
+/* Common Utility Functions */
+/****************************/
+
+#if defined(_WIN32)
+
+	#define xp_beep(freq,dur)	Beep(freq,dur)
+	#define mswait(x)			Sleep(x)
+
+#elif defined(__OS2__)
+
+	#define xp_beep(freq,dur)	DosBeep(freq,dur)
+	#define mswait(x)			DosSleep(x)
+
+#elif defined(__unix__)
+
+	#define mswait(x)			usleep(x*1000)
+	DLLEXPORT void	DLLCALL		xp_beep(int freq, int dur);
+
+#else	/* Unsupported OS */
+
+	#warning "Unsupported Target: Need some macros or function prototypes here."
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* Don't add anything after this line */
diff --git a/src/xpdev/sockwrap.h b/src/xpdev/sockwrap.h
new file mode 100644
index 0000000000..84e0132a53
--- /dev/null
+++ b/src/xpdev/sockwrap.h
@@ -0,0 +1,127 @@
+/* sockwrap.h */
+
+/* Berkley/WinSock socket API 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 _SOCKWRAP_H
+#define _SOCKWRAP_H
+
+/***************/
+/* OS-specific */
+/***************/
+#if defined _WIN32	|| defined __OS2__	/* Use WinSock */
+
+#include <winsock.h>	/* socket/bind/etc. */
+
+/* Let's agree on a standard WinSock symbol here, people */
+#ifndef _WINSOCKAPI_
+#define _WINSOCKAPI_	
+#endif
+
+#elif defined __unix__	/* Unix-variant */
+
+#include <netdb.h>		/* gethostbyname */
+#include <sys/types.h>  /* For u_int32_t on FreeBSD */
+#include <netinet/in.h>	/* IPPROTO_IP */
+#include <sys/socket.h>	/* socket/bind/etc. */
+#include <sys/ioctl.h>	/* FIONBIO */
+#include <sys/time.h>	/* struct timeval */
+#include <arpa/inet.h>	/* inet_ntoa */
+#include <unistd.h>		/* close */
+
+#endif
+
+#include <errno.h>		/* errno */
+
+/**********************************/
+/* Socket Implementation-specific */
+/**********************************/
+#ifdef _WINSOCKAPI_
+
+#undef  EINTR
+#define EINTR			(WSAEINTR-WSABASEERR)
+#undef  ENOTSOCK
+#define ENOTSOCK		(WSAENOTSOCK-WSABASEERR)
+#undef  EWOULDBLOCK
+#define EWOULDBLOCK		(WSAEWOULDBLOCK-WSABASEERR)
+#undef  ECONNRESET
+#define ECONNRESET		(WSAECONNRESET-WSABASEERR)
+#undef  ESHUTDOWN
+#define ESHUTDOWN		(WSAESHUTDOWN-WSABASEERR)
+#undef  ECONNABORTED
+#define ECONNABORTED	(WSAECONNABORTED-WSABASEERR)
+
+#define s_addr			S_un.S_addr
+
+#define socklen_t		int
+
+static  wsa_error;
+#define ERROR_VALUE		((wsa_error=WSAGetLastError())>0 ? wsa_error-WSABASEERR : wsa_error)
+#define sendsocket(s,b,l)	send(s,b,l,0)
+
+#else	/* BSD sockets */
+
+/* WinSock-isms */
+#define HOSTENT			struct hostent
+#define SOCKADDR_IN		struct sockaddr_in
+#define LINGER			struct linger
+#define SOCKET			int
+#define SOCKET_ERROR	-1
+#define INVALID_SOCKET  (SOCKET)(~0)
+#define closesocket		close
+#define ioctlsocket		ioctl
+#define ERROR_VALUE		errno
+#define sendsocket		write		// FreeBSD send() is broken
+
+#endif	/* __unix__ */
+
+#ifndef SHUT_RDWR
+#define SHUT_RDWR		2	/* for shutdown() */
+#endif
+
+#ifndef IPPORT_FTP
+#define IPPORT_FTP		21
+#endif
+#ifndef IPPORT_TELNET
+#define IPPORT_TELNET	23
+#endif
+#ifndef IPPORT_SMTP
+#define IPPORT_SMTP		25
+#endif
+#ifndef IPPORT_POP3
+#define IPPORT_POP3		110
+#endif
+
+#endif	/* Don't add anything after this line */
diff --git a/src/xpdev/threadwrap.c b/src/xpdev/threadwrap.c
new file mode 100644
index 0000000000..097f947d95
--- /dev/null
+++ b/src/xpdev/threadwrap.c
@@ -0,0 +1,91 @@
+/* threadwrap.c */
+
+/* Thread-related cross-platform development 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.	*
+ ****************************************************************************/
+
+#include "threadwrap.h"	/* DLLCALL */
+
+/****************************************************************************/
+/* Wrapper for Win32 create/begin thread function							*/
+/* Uses POSIX threads														*/
+/****************************************************************************/
+#if defined(__unix__) && defined(SBBS)
+#if defined(_POSIX_THREADS)
+ulong _beginthread(void( *start_address )( void * )
+		,unsigned stack_size, void *arglist)
+{
+	pthread_t	thread;
+	pthread_attr_t attr;
+
+	pthread_attr_init(&attr);     /* initialize attribute structure */
+
+	/* set thread attributes to PTHREAD_CREATE_DETACHED which will ensure
+	   that thread resources are freed on exit() */
+	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+	if(pthread_create(&thread
+		,&attr	/* default attributes */
+		/* POSIX defines this arg as "void *(*start_address)" */
+		,(void *) start_address
+		,arglist)==0)
+		return((int) thread /* thread handle */);
+
+	return(-1);	/* error */
+}
+#else
+
+#error "Need _beginthread implementation for non-POSIX thread library."
+
+#endif
+
+#endif	/* __unix__ */
+
+/****************************************************************************/
+/* Win32 implementation of POSIX sem_getvalue() function					*/
+/****************************************************************************/
+#ifdef _WIN32
+int DLLCALL sem_getvalue(sem_t* psem, int* val)
+{
+	if(psem==NULL || val==NULL)
+		return(-1);
+
+	if(WaitForSingleObject(*(psem),0)==WAIT_OBJECT_0)
+		*val=1;
+	else
+		*val=0;
+
+	return(0);
+}
+#endif
diff --git a/src/xpdev/threadwrap.h b/src/xpdev/threadwrap.h
new file mode 100644
index 0000000000..24c9fbe18d
--- /dev/null
+++ b/src/xpdev/threadwrap.h
@@ -0,0 +1,105 @@
+/* threadwrap.h */
+
+/* Thread-related cross-platform development 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 _THREADWRAP_H
+#define _THREADWRAP_H
+
+#include "gen_defs.h"	/* HANDLE */
+
+#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 __unix__
+
+	#include <pthread.h>	/* POSIX threads and mutexes */
+	#include <semaphore.h>	/* POSIX semaphores */
+	ulong _beginthread(void( *start_address )( void * )
+		,unsigned stack_size, void *arglist);
+
+#elif defined(_WIN32)	
+
+	/* POSIX semaphores */
+	typedef HANDLE sem_t;
+	#define sem_init(psem,ps,v)			ResetEvent(*(psem))
+	#define sem_wait(psem)				WaitForSingleObject(*(psem),INFINITE)
+	#define sem_post(psem)				SetEvent(*(psem))
+	#define sem_destroy(psem)			CloseHandle(*(psem))
+	DLLEXPORT int DLLCALL sem_getvalue(sem_t*, int* val);
+
+	/* POSIX mutexes */
+	typedef HANDLE pthread_mutex_t;
+	#define PTHREAD_MUTEX_INITIALIZER	CreateMutex(NULL,FALSE,NULL)
+	#define pthread_mutex_init(pmtx,v)	*(pmtx)=CreateMutex(NULL,FALSE,NULL)
+	#define pthread_mutex_lock(pmtx)	WaitForSingleObject(*(pmtx),INFINITE)
+	#define pthread_mutex_unlock(pmtx)	ReleaseMutex(*(pmtx))
+	#define	pthread_mutex_destroy(pmtx)	CloseHandle(*(pmtx))
+
+#else
+
+	#warning "Need semaphore wrappers."
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* Don't add anything after this line */
diff --git a/src/xpdev/wraptest.c b/src/xpdev/wraptest.c
new file mode 100644
index 0000000000..290ef6dd97
--- /dev/null
+++ b/src/xpdev/wraptest.c
@@ -0,0 +1,65 @@
+/* wraptest.c */
+
+#include <time.h>	/* ctime */
+
+#include "genwrap.h"
+#include "conwrap.h"
+#include "dirwrap.h"
+#include "filewrap.h"
+#include "sockwrap.h"
+#include "threadwrap.h"
+
+int main()
+{
+	char	compiler[128];
+	char	fname[MAX_PATH+1];
+	char*	glob_pattern = "*wrap*";
+	int		i;
+	uint	u;
+	time_t	t;
+	glob_t	g;
+	DIR*	dir;
+	DIRENT*	dirent;
+
+	COMPILER_DESC(compiler);
+	printf("Platform: %s\n",PLATFORM_DESC);
+	printf("Compiler: %s\n",compiler);
+
+	xp_beep(500,500);
+
+	/* GLOB TEST */
+	printf("glob() test\n");
+	i=glob(glob_pattern,GLOB_MARK,NULL,&g);
+	if(i==0) {
+		for(u=0;u<g.gl_pathc;u++)
+			printf("%s\n",g.gl_pathv[u]);
+		globfree(&g);
+	} else
+		printf("glob(%s) returned %d\n",glob_pattern,i);
+
+	printf("Hit any key...");
+	printf("\ngetch() returned %d\n",getch());
+
+	/* OPENDIR TEST */
+	printf("opendir() test\n");
+	dir=opendir(".");
+	while(dir!=NULL && (dirent=readdir(dir))!=NULL) {
+		t=fdate(dirent->d_name);
+		sprintf(fname,"%.*s%c"
+			,sizeof(fname)-2
+			,dirent->d_name
+			,isdir(dirent->d_name) ? '/':0);
+		printf("%-25s %10lu %.24s\n"
+			,fname
+			,flength(dirent->d_name)
+			,ctime(&t)
+			);
+	}
+	if(dir!=NULL)
+		closedir(dir);
+
+	printf("Hit any key...");
+	printf("\ngetch() returned %d\n",getch());
+
+	return 0;
+}
\ No newline at end of file
-- 
GitLab