/* vi:set ts=4 sw=4:
 *
 * VIM - Vi IMproved
 *
 */
/*
 * Author:
 *		K.Tsuchida (ken)	kenichi-tsu@aix.or.jp/gdh01571@niftyserve.or.jp
 */
/*
 * Author:	(base code: winnt.c jelvis version)
 *		T.Watanabe (Tom_W)	tom_w@st.rim.or.jp/GCD02235@niftyserve.or.jp
 */
/* Author: (base code: msdos.c)
 * Code Contributions By:		Bram Moolenaar			mool@oce.nl
 *								Tim Thompson			twitch!tjt
 *								Tony Andrews			onecom!wldrdg!tony
 *								G. R. (Fred) Walter 	watmath!watcgl!grwalter
 */

/*
 * winnt.c
 *
 * WINDOWS-NT system-dependent routines.
 */

#include <windows.h>

#include <io.h>

#include "vim.h"

#include "globals.h"

#include "param.h"

#include "proto.h"

#include <fcntl.h>

#include <process.h>

#ifdef FEPCTRL

#include "fepctrl.h"

#endif


static HANDLE	h_stdin, h_stdout;
static int		sy_fg_color;
static int		sy_bg_color;

#define NM_COLOR	(sy_fg_color|(sy_bg_color<<4))

#define SO_COLOR	(sy_bg_color|(sy_fg_color<<4))

#define gotoxy(x, y) {COORD csr; csr.X = (x-1); csr.Y = (y-1); (void) SetConsoleCursorPosition(h_stdout, csr);}


typedef struct filelist
{
	char	**file;
	int		nfiles;
	int		maxfiles;
} FileList;

static void		addfile __ARGS((FileList *, char *, int));
static int		pstrcmp();	/* __ARGS((char **, char **)); BCC does not like this */
static void		strlowcpy __ARGS((char *, char *));
static int		expandpath __ARGS((FileList *, char *, int, int, int));

static int ctrlc_pressed = FALSE;	/* set when ctrl-C or ctrl-break detected */
static int delayed_redraw = FALSE;	/* set when ctrl-C detected */

#ifdef DEBUG

/*
 * Put two characters in the video buffer without calling BIOS or DOS.
 */
blink(n)
	int n;
{
	/* do nothing */
}
#endif


	void
sleep(time)
unsigned	time;
{
	INPUT_RECORD	ir;
	DWORD			cnt;

	time *= 1000;	/* s -> ms */
	while (time > 0) {
		Sleep(200);
		if (WaitForSingleObject(h_stdin, 0) == WAIT_OBJECT_0) {
			while (PeekConsoleInput(h_stdin, &ir, 1, &cnt) && cnt) {
				if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown) {
					return;
				}
				ReadConsoleInput(h_stdin, &ir, 1, &cnt);
			}
		}
		time -= 200;
	}
}

	void
vim_delay()
{
	sleep(1);
}

/*
 * this version of remove is not scared by a readonly (backup) file
 */
	int
vim_remove(name)
	char *name;
{
	setperm(name, 0);    /* default permissions */
	return unlink(name);
}

	static void
clrscr()
{
	DWORD	cnt, dummy;
	COORD	csr;

	csr.X = csr.Y = 0;
	cnt = Rows * Columns;
	(void) FillConsoleOutputCharacter(h_stdout, ' ', cnt, csr, &dummy);
	(void) FillConsoleOutputAttribute(h_stdout, NM_COLOR, cnt, csr, &dummy);
}

	static void
clreol()
{
	CONSOLE_SCREEN_BUFFER_INFO	pcinf;
	COORD						csr;
	DWORD						cnt, dummy;

	(void) GetConsoleScreenBufferInfo(h_stdout, &pcinf);
    csr.X = pcinf.dwCursorPosition.X;
    csr.Y = pcinf.dwCursorPosition.Y;
    cnt = Columns - pcinf.dwCursorPosition.X;
	(void) FillConsoleOutputCharacter(h_stdout, ' ', cnt, csr, &dummy);
	(void) FillConsoleOutputAttribute(h_stdout, NM_COLOR, cnt, csr, &dummy);
}

	static void
insline()
{
	CONSOLE_SCREEN_BUFFER_INFO	pcinf;
	SMALL_RECT	area, clip;
	COORD		dest;
	CHAR_INFO	fill;

	(void) GetConsoleScreenBufferInfo(h_stdout, &pcinf);
	area.Left = 0;
	area.Right = Columns - 1;
	area.Top = pcinf.dwCursorPosition.Y;
	area.Bottom = Rows - 2;
	clip.Left = 0;
	clip.Right = Columns - 1;
	clip.Top = pcinf.dwCursorPosition.Y;
	clip.Bottom = Rows - 1;
	dest.X = 0;
	dest.Y = pcinf.dwCursorPosition.Y + 1;
	fill.Char.AsciiChar = ' ';
	fill.Attributes = NM_COLOR;
	(void) ScrollConsoleScreenBuffer(h_stdout, &area, &clip, dest, &fill);
}

	static void
delline()
{
	CONSOLE_SCREEN_BUFFER_INFO	pcinf;
	SMALL_RECT	area, clip;
	COORD		dest;
	CHAR_INFO	fill;

	(void) GetConsoleScreenBufferInfo(h_stdout, &pcinf);
	area.Left = 0;
	area.Right = Columns - 1;
	area.Top = pcinf.dwCursorPosition.Y + 1;
	area.Bottom = Rows - 1;
	clip.Left = 0;
	clip.Right = Columns - 1;
	clip.Top = pcinf.dwCursorPosition.Y;
	clip.Bottom = Rows - 1;
	dest.X = 0;
	dest.Y = pcinf.dwCursorPosition.Y;
	fill.Char.AsciiChar = ' ';
	fill.Attributes = NM_COLOR;
	(void) ScrollConsoleScreenBuffer(h_stdout, &area, &clip, dest, &fill);
}

/*
 * mch_write(): write the output buffer to the screen
 */
	void
mch_write(s, len)
	char	*s;
	int		len;
{
	char		*p;
	int			row, col;
	LPDWORD		cnt;
	static char	buffer[IOSIZE];
	int			pos	= 0;
#define SETBUF(_c)	{if (pos >= sizeof(buffer)) {(void)WriteConsole(h_stdout, buffer, (unsigned)pos, &cnt, NULL); pos = 0;} buffer[pos++] = (_c);}

#define PUTBUF()	{if (pos != 0) (void)WriteConsole(h_stdout, buffer, (unsigned)pos, &cnt, NULL); pos = 0;}


	if (term_console) {		/* translate ESC | sequences into bios calls */
		while (len--)
		{
			if (s[0] == '\n')
			{
				PUTBUF();
				(void) WriteConsole(h_stdout, "\r", (unsigned)1, &cnt, NULL);
			}
			else if ((s[0] == '\007') && p_vb)
			{
				HWND		console;
				console = GetForegroundWindow();
				if (IsWindow(console)) {
					FlashWindow(console, TRUE);
					Sleep(80);
					FlashWindow(console, TRUE);
					Sleep(80);
					FlashWindow(console, TRUE);
					Sleep(80);
					FlashWindow(console, TRUE);
				}
				s++;
				continue;
			}
			else if (s[0] == ESC && len > 1 && s[1] == '|')
			{
			switch (s[2])
				{
				case 'J':	PUTBUF();
							clrscr();
							goto got3;

				case 'K':	PUTBUF();
							clreol();
							goto got3;

				case 'L':	PUTBUF();
							insline();
							goto got3;

				case 'M':	PUTBUF();
							delline();
got3:						s += 3;
							len -= 2;
							continue;

				case '0':
				case '1':
				case '2':
				case '3':
				case '4':
				case '5':
				case '6':
				case '7':
				case '8':
				case '9':	p = s + 2;
							row = getdigits(&p);	/* no check for length! */
							if (p > s + len)
								break;
							if (*p == ';')
							{
								++p;
								col = getdigits(&p);/* no check for length! */
								if (p > s + len)
									break;
								if (*p == 'H')
								{
									PUTBUF();
									gotoxy(col, row);
									len -= p - s;
									s = p + 1;
									continue;
								}
							}
							else if (*p == 'm')
							{
								PUTBUF();
								if (row == 0)
									SetConsoleTextAttribute(h_stdout, NM_COLOR);
								else
									SetConsoleTextAttribute(h_stdout, SO_COLOR);
								len -= p - s;
								s = p + 1;
								continue;
							}
				}
			}
			SETBUF(*s++);
		}
		PUTBUF();
	} else
		write(1, s, (unsigned)len);
}

/*
 * GetChars(): low level input funcion.
 * Get a characters from the keyboard.
 * If time == 0 do not wait for characters.
 * If time == n wait a short time for characters.
 * If time == -1 wait forever for characters.
 */
	int
GetChars(buf, maxlen, time)
	char		*buf;
	int 		maxlen;
	int 		time;
{
	CONSOLE_SCREEN_BUFFER_INFO	pcinf;
	INPUT_RECORD				ir;
	DWORD						cnt;
	int							len = 0;
	int							update = FALSE;

	if (delayed_redraw)
	{
		delayed_redraw = FALSE;
		updateScreen(CLEAR);
		setcursor();
	}
	flushbuf();

#ifdef FEPCTRL

	if (p_fc && p_fs)
		fep_disp_sync(h_stdout);
#endif

	if (time >= 0)
	{
		if (time == 0)			/* don't know if time == 0 is allowed */
			time = 1;
	}
	else	/* time == -1 */
	{
		/*
		 * If there is no character available within 2 seconds (default)
		 * write the autoscript file to disk
		 */
		time = p_ut;
		update = TRUE;
	}
	if (WaitForSingleObject(h_stdin, time) != WAIT_OBJECT_0) {
		if (update)
			updatescript(0);
		return 0;			/* no KEY data */
	}
	while (len < maxlen-1) {
		if ((PeekConsoleInput(h_stdin, &ir, 1, &cnt) != TRUE) || (cnt == 0)) {
			return len;
		}
		if ((ReadConsoleInput(h_stdin, &ir, 1, &cnt) != TRUE) || (cnt == 0)) {
			return len;
		}
		if (ir.EventType == KEY_EVENT		/* KEY HIT ? */
				&& ir.Event.KeyEvent.bKeyDown) {
			if (ir.Event.KeyEvent.uChar.AsciiChar) {
				buf[len++] = ir.Event.KeyEvent.uChar.AsciiChar;
#ifdef JP

#define IsSJISkanji(c) ((c)>=0x81 && (c)<=0x9f || (c)>=0xe0 && (c)<=0xfc)

				if (IsSJISkanji((u_char)(buf[len-1]))) {
					/* get 2nd kanji code */
					for (;;) {
						if ((ReadConsoleInput(h_stdin, &ir, 1, &cnt) != TRUE)
															|| (cnt == 0)) {
							continue;
						}
						if (ir.EventType == KEY_EVENT
								&& ir.Event.KeyEvent.bKeyDown
								&& ir.Event.KeyEvent.uChar.AsciiChar) {
							break;	/* for loop */
						}
					}
					buf[len++] = ir.Event.KeyEvent.uChar.AsciiChar;
				}
#endif

				continue;	/* while loop */
			}
			switch (ir.Event.KeyEvent.wVirtualKeyCode) {
			case VK_UP:
				buf[len++] = 0xff; buf[len++] = 'H';
				return len;
			case VK_DOWN:
				buf[len++] = 0xff; buf[len++] = 'P';
				return len;
			case VK_LEFT:
				if (ir.Event.KeyEvent.dwControlKeyState
						& (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) {
					buf[len++] = 0xff; buf[len++] = 's';
				} else {
					buf[len++] = 0xff; buf[len++] = 'K';
				}
				return len;
			case VK_RIGHT:
				if (ir.Event.KeyEvent.dwControlKeyState
						& (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) {
					buf[len++] = 0xff; buf[len++] = 't';
				} else {
					buf[len++] = 0xff; buf[len++] = 'M';
				}
				return len;
			case VK_PRIOR:
				buf[len++] = Ctrl('B');
				return len;
			case VK_NEXT:
				buf[len++] = Ctrl('F');
				return len;
#ifdef	NT106KEY

			case 0xf3:  case 0xf4:		/* ZENKAKU / HANKAKU KEY */
				buf[len++] = '[' & 0x1f;	/* ESC key !! */
				return len;
#endif

			case '6':			/* jkeyb.sys */ /* ken add */
			case 0xde:			/* ^ key */
				if (ir.Event.KeyEvent.dwControlKeyState
						& (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) {
					buf[len++] = '^' & 0x1f;
							/* CTRL+^=switch file */
					return len;
				}
				break;
			}
		}
	}
	return(len);
}

/*
 * We have no job control, fake it by starting a new shell.
 */
	void
mch_suspend()
{
	outstr("new shell started\n");
	call_shell(NULL, 0, TRUE);
}

extern int _fmode;
/*
 * we do not use windows, there is not much to do here
 */
	void
mch_windinit()
{
	CONSOLE_SCREEN_BUFFER_INFO	pcinf;

	_fmode = O_BINARY;		/* we do our own CR-LF translation */
	if ((h_stdin = CreateFile("CONIN$", GENERIC_READ|GENERIC_WRITE,
			FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) {
		FatalAppExit(0, "stdin handle get error\n");
		exit(99);
	}
	if ((h_stdout = GetStdHandle(STD_OUTPUT_HANDLE)) == INVALID_HANDLE_VALUE) {
		FatalAppExit(0, "stdout handle get error\n");
		exit(99);
	}
	term_console = 1;
	flushbuf();
	mch_get_winsize();
#ifdef FEPCTRL

	if (p_fc)
		fep_init();
#endif

}

	void
check_win(argc, argv)
	int		argc;
	char	**argv;
{
	if (!isatty(0) || !isatty(1))
	{
		fprintf(stderr, "VIM: no controlling terminal\n");
		exit(2);
	}
}

/*
 * fname_case(): Set the case of the filename, if it already exists.
 *				 msdos filesystem is far to primitive for that. do nothing.
 */
	void
fname_case(name)
	char *name;
{
}

/*
 * settitle(): set titlebar of our window.
 * Dos console has no title.
 */
	void
settitle(str)
	char *str;
{
	if (*str)
		SetConsoleTitle(str);
	else
		SetConsoleTitle("-JVim-");
}

	void
resettitle()
{
	SetConsoleTitle("");
}

/*
 * Get name of current directory into buffer 'buf' of length 'len' bytes.
 * Return non-zero for success.
 */
	int
dirname(buf, len)
	char	*buf;
	int		len;
{
	return (getcwd(buf, len) != NULL);
}

/*
 * get absolute filename into buffer 'buf' of length 'len' bytes
 */
	int
FullName(fname, buf, len)
	char	*fname, *buf;
	int		len;
{
	if (fname == NULL)	/* always fail */
		return 0;

#ifdef __BORLANDC__		/* the old Turbo C does not have this */

	if (_fullpath(buf, fname, len) == NULL)
	{
		strncpy(buf, fname, len);	/* failed, use the relative path name */
		return 0;
	}
	return 1;
#else					/* almost the same as FullName in unix.c */

	{
		int		l;
		char	olddir[MAXPATHL];
		char	*p, *q;
		int		c;
		int		retval = 1;

		*buf = 0;
		/*
		 * change to the directory for a moment,
		 * and then do the getwd() (and get back to where we were).
		 * This will get the correct path name with "../" things.
		 */
		p = strrchr(fname, '/');
		q = strrchr(fname, '\\');
		if (q && (p == NULL || q > p))
			p = q;
		q = strrchr(fname, ':');
		if (q && (p == NULL || q > p))
			p = q;
		if (p != NULL)
		{
			if (getcwd(olddir, MAXPATHL) == NULL)
			{
				p = NULL;		/* can't get current dir: don't chdir */
				retval = 0;
			}
			else
			{
				if (*p == ':' || (p > fname && p[-1] == ':'))
					q = p + 1;
				else
					q = p;
				c = *q;
				*q = NUL;
				if (chdir(fname))
					retval = 0;
				else
					fname = p + 1;
				*q = c;
			}
		}
		if (getcwd(buf, len) == NULL)
		{
			retval = 0;
			*buf = NUL;
		}
		l = strlen(buf);
		if (l && buf[l - 1] != '/' && buf[l - 1] != '\\')
			strcat(buf, "\\");
		if (p)
			chdir(olddir);
		strcat(buf, fname);
		return retval;
	}
#endif
}

/*
 * get file permissions for 'name'
 * -1 : error
 * else FA_attributes defined in dos.h
 */
	long
getperm(name)
	char *name;
{
	return(_chmod(name, 0, 0));		 /* get file mode */
}

/*
 * set file permission for 'name' to 'perm'
 */
	int
setperm(name, perm)
	char	*name;
	long	perm;
{
	perm &= ~FA_ARCH;
	return _chmod(name, 1, (int)perm);
}

/*
 * check if "name" is a directory
 */
	int
isdir(name)
	char *name;
{
	int f;

	f = _chmod(name, 0, 0);
	if (f == -1)
		return -1;					/* file does not exist at all */
	if ((f & FA_DIREC) == 0)
		return 0;					/* not a directory */
	return 1;
}

/*
 * Careful: mch_windexit() may be called before mch_windinit()!
 */
	void
mch_windexit(r)
	int r;
{
#ifdef FEPCTRL
	if (p_fc)
		fep_term();
#endif
	settmode(0);
	stoptermcap();
	flushbuf();
	stopscript(); 				/* remove autoscript file */
	exit(r);
}

/*
 *
 */
	BOOL
cbrk_handler(type)
DWORD	type;
{
	switch (type) {
	case CTRL_BREAK_EVENT:
		delayed_redraw = TRUE;
		return(TRUE);
	case CTRL_C_EVENT:
		/*
		FlushConsoleInputBuffer(h_stdin);
		 */
		ctrlc_pressed  = TRUE;
		return(TRUE);
	case CTRL_CLOSE_EVENT:
	case CTRL_LOGOFF_EVENT:
	case CTRL_SHUTDOWN_EVENT:
		if (p_wa && !p_ro) {
			extern int global_busy;
			global_busy = 1;
			p_bk = TRUE;
			if (Filename == NULL)
				setfname("-JVim-", NULL);
			docmdline(":x!");
		} else {
			docmdline(":q!");
		}
		getout(1);
		return(TRUE);
	}
	return(FALSE);
}

	void
mch_settmode(raw)
	int  raw;
{
	DWORD	bits;

	GetConsoleMode(h_stdin, &bits);
	if (raw)
	{
		SetConsoleCtrlHandler((PHANDLER_ROUTINE)cbrk_handler, TRUE);
		bits &= ~(ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT|ENABLE_ECHO_INPUT);
	}
	else
	{
		SetConsoleTextAttribute(h_stdout, NM_COLOR);
		bits |=  (ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT|ENABLE_ECHO_INPUT);
	}
	SetConsoleMode(h_stdin, bits);
}

/*
 * Structure used by Turbo-C/Borland-C to store video parameters.
 */
	int
mch_get_winsize()
{
	int i;
	CONSOLE_SCREEN_BUFFER_INFO	pcinf;
	SMALL_RECT					rect;

	if (GetConsoleScreenBufferInfo(h_stdout, &pcinf)) {
		Columns = pcinf.dwSize.X;
		Rows    = pcinf.dwSize.Y;
		sy_fg_color = pcinf.wAttributes & 0x0f;
		sy_bg_color = (pcinf.wAttributes & 0xf0) >> 4;
		rect.Left = rect.Top = 0;
		rect.Right	= Columns - 1;
		rect.Bottom = Rows - 1;
		SetConsoleWindowInfo(h_stdout, TRUE, &rect);
		/*
		COORD						coord;
		SetConsoleScreenBufferSize(h_stdout, coord);
		 */
	} else {
		Columns = 25;
		Rows    = 80;
		sy_fg_color = FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE;
		sy_bg_color = 0;
	}
	set_window();

	if (Columns < 5 || Columns > MAX_COLUMNS ||
					Rows < 2 || Rows > MAX_COLUMNS)
	{
		/* these values are overwritten by termcap size or default */
		Columns = 80;
		Rows = 25;
		return 1;
	}
	Rows_max = Rows;				/* remember physical max height */

	check_winsize();
	script_winsize();

	return 0;
}

/*
 * Set the active window for delline/insline.
 */
	void
set_window()
{
	/* nothing */
}

	void
mch_set_winsize()
{
	/* should try to set the window size to Rows and Columns */
	/* may involve switching display mode.... */
}

	int
call_shell(cmd, filter, cooked)
	char	*cmd;
	int 	filter; 		/* if != 0: called by dofilter() */
	int		cooked;
{
	int		x;
	char	newcmd[200];

	flushbuf();

#ifdef FEPCTRL
	if (p_fc)
		fep_term();
#endif

	if (cooked)
		settmode(0);		/* set to cooked mode */

	if (cmd == NULL)
		x = spawnlp(P_WAIT, p_sh, p_sh, NULL);
	else
	{ 					/* we use "command" to start the shell, slow but easy */
		sprintf(newcmd, "%cc", get_switchar());
		x = spawnlp(P_WAIT, p_sh, p_sh, newcmd, cmd, NULL);
	}
	outchar('\n');
	if (cooked)
		settmode(1);		/* set to raw mode */

	if (x)
	{
		smsg("%d returned", x);
		outchar('\n');
	}

#ifdef FEPCTRL
	if (p_fc)
		fep_init();
#endif

	return x;
}

/*
 * check for an "interrupt signal": CTRL-break or CTRL-C
 */
	void
breakcheck()
{
	if (ctrlc_pressed)
	{
		ctrlc_pressed = FALSE;
		got_int = TRUE;
	}
}

#define FL_CHUNK 512

	static void
addfile(fl, f, isdir)
	FileList	*fl;
	char		*f;
	int			isdir;
{
	char		*p;

	if (!fl->file)
	{
		fl->file = (char **)alloc(sizeof(char *) * FL_CHUNK);
		if (!fl->file)
			return;
		fl->nfiles = 0;
		fl->maxfiles = FL_CHUNK;
	}
	if (fl->nfiles >= fl->maxfiles)
	{
		char	**t;
		int		i;

		t = (char **)lalloc(sizeof(char *) * (fl->maxfiles + FL_CHUNK), TRUE);
		if (!t)
			return;
		for (i = fl->nfiles - 1; i >= 0; i--)
			t[i] = fl->file[i];
		free(fl->file);
		fl->file = t;
		fl->maxfiles += FL_CHUNK;
	}
	p = alloc((unsigned)(strlen(f) + 1 + isdir));
	if (p)
	{
		strcpy(p, f);
		if (isdir)
			strcat(p, "\\");
	}
	fl->file[fl->nfiles++] = p;
}

	static int
pstrcmp(a, b)
	char **a, **b;
{
	return (strcmp(*a, *b));
}

	int
has_wildcard(s)
	char *s;
{
	if (s)
		for ( ; *s; ++s)
			if (*s == '?' || *s == '*')
				return 1;
	return 0;
}

	static void
strlowcpy(d, s)
	char *d, *s;
{
	while (*s)
		*d++ = tolower(*s++);
	*d = '\0';
}

	static int
expandpath(fl, path, fonly, donly, notf)
	FileList	*fl;
	char		*path;
	int			fonly, donly, notf;
{
	char	buf[MAXPATH];
	char	*p, *s, *e;
	int		lastn, c, r;
	struct	ffblk fb;

	lastn = fl->nfiles;

/*
 * Find the first part in the path name that contains a wildcard.
 * Copy it into buf, including the preceding characters.
 */
	p = buf;
	s = NULL;
	e = NULL;
	while (*path)
	{
		if (*path == '\\' || *path == ':' || *path == '/')
		{
			if (e)
				break;
			else
				s = p;
		}
		if (*path == '*' || *path == '?')
			e = p;
		*p++ = *path++;
	}
	e = p;
	if (s)
		s++;
	else
		s = buf;

	/* now we have one wildcard component between s and e */
	*e = '\0';
	r = 0;
	/* If we are expanding wildcards we try both files and directories */
	if ((c = findfirst(buf, &fb, (*path || !notf) ? FA_DIREC : 0)) != 0)
	{
		/* not found */
		strcpy(e, path);
		if (notf)
			addfile(fl, buf, FALSE);
		return 1; /* unexpanded or empty */
	}
	while (!c)
	{
		strlowcpy(s, fb.ff_name);
		if (*s != '.' || (s[1] != '\0' && (s[1] != '.' || s[2] != '\0')))
		{
			strcat(buf, path);
			if (!has_wildcard(path))
				addfile(fl, buf, (isdir(buf) > 0));
			else
				r |= expandpath(fl, buf, fonly, donly, notf);
		}
		c = findnext(&fb);
	}
	qsort(fl->file + lastn, fl->nfiles - lastn, sizeof(char *), pstrcmp);
	return r;
}

/*
 * MSDOS rebuilt of Scott Ballantynes ExpandWildCard for amiga/arp.
 * jw
 */

	int
ExpandWildCards(num_pat, pat, num_file, file, files_only, list_notfound)
	int 	num_pat;
	char	**pat;
	int 	*num_file;
	char	***file;
	int 	files_only, list_notfound;
{
	int			i, r = 0;
	FileList	f;

	f.file = NULL;
	f.nfiles = 0;
	for (i = 0; i < num_pat; i++)
	{
		if (!has_wildcard(pat[i]))
			addfile(&f, pat[i], files_only ? FALSE : (isdir(pat[i]) > 0));
		else
			r |= expandpath(&f, pat[i], files_only, 0, list_notfound);
	}
	if (r == 0)
	{
		*num_file = f.nfiles;
		*file = f.file;
	}
	else
	{
		*num_file = 0;
		*file = NULL;
	}
	return r;
}

	void
FreeWild(num, file)
	int		num;
	char	**file;
{
	if (file == NULL || num <= 0)
		return;
	while (num--)
		free(file[num]);
	free(file);
}

/*
 * The normal chdir() does not change the default drive.
 * This one does.
 */
#undef chdir

	int
vim_chdir(path)
	char *path;
{
	if (path[0] == NUL)				/* just checking... */
		return 0;
	if (path[1] == ':')				/* has a drive name */
	{
		if (_chdrive(toupper(path[0]) - 'A' + 1))
			return -1;				/* invalid drive name */
		path += 2;
	}
	if (*path == NUL)				/* drive name only */
		return 0;
	return chdir(path);				/* let the normal chdir() do the rest */
}

	int
get_switchar()
{
	return '/';
}


syntax highlighted by Code2HTML, v. 0.9.1