www.pudn.com > vim53src.zip > os_msdos.c


/* vi:set ts=8 sts=4 sw=4: 
 * 
 * VIM - Vi IMproved	by Bram Moolenaar 
 * 
 * Do ":help uganda"  in Vim to read copying and usage conditions. 
 * Do ":help credits" in Vim to see a list of people who contributed. 
 */ 
 
/* 
 * os_msdos.c 
 * 
 * MSDOS system-dependent routines. 
 * A cheap plastic imitation of the amiga dependent code. 
 * A lot in this file was made by Juergen Weigert (jw). 
 * 
 * DJGPP changes by Gert van Antwerpen 
 * Faster text screens by John Lange (jlange@zilker.net) 
 * 
 */ 
 
#include  
#include "vim.h" 
 
#include  
#ifdef HAVE_FCNTL_H 
# include  
#endif 
#include  
#ifdef DJGPP 
# include  
# include  
# include  
#else 
# include  
#endif 
 
#if defined(DJGPP) || defined(PROTO) 
# define _cdecl	    /* DJGPP doesn't have this */ 
#endif 
 
static int cbrk_pressed = FALSE;    /* set by ctrl-break interrupt */ 
static int ctrlc_pressed = FALSE;   /* set when ctrl-C or ctrl-break detected */ 
static int delayed_redraw = FALSE;  /* set when ctrl-C detected */ 
 
#ifdef USE_MOUSE 
static int mouse_avail = FALSE;		/* mouse present */ 
static int mouse_active;		/* mouse enabled */ 
static int mouse_hidden;		/* mouse not shown */ 
static int mouse_click = -1;		/* mouse status */ 
static int mouse_last_click = -1;	/* previous status at click */ 
static int mouse_x = -1;		/* mouse x coodinate */ 
static int mouse_y = -1;		/* mouse y coodinate */ 
static long mouse_click_time = 0;	/* biostime() of last click */ 
static int mouse_click_count = 0;	/* count for multi-clicks */ 
static int mouse_click_x = 0;		/* x of previous mouse click */ 
static int mouse_click_y = 0;		/* y of previous mouse click */ 
static linenr_t mouse_topline = 0;	/* topline at previous mouse click */ 
static int mouse_x_div = 8;		/* column = x coord / mouse_x_div */ 
static int mouse_y_div = 8;		/* line   = y coord / mouse_y_div */ 
#endif 
 
#define BIOSTICK    55		    /* biostime() increases one tick about 
					every 55 msec */ 
 
#ifdef DJGPP 
/* 
 * For DJGPP, use our own functions for fast text screens.  JML 1/18/98 
 */ 
 
unsigned long	S_ulScreenBase = 0xb8000; 
unsigned short	S_uiAttribute = 7 << 8; 
int		S_iCurrentRow = 0;	/* These are 0 offset */ 
int		S_iCurrentColumn = 0; 
int		S_iLeft = 0;	/* These are 1 offset */ 
int		S_iTop = 0; 
int		S_iRight = 0; 
int		S_iBottom = 0; 
short		S_selVideo;	/* Selector for DJGPP direct video transfers */ 
 
    static void 
mygotoxy(int x, int y) 
{ 
    S_iCurrentRow = y - 1; 
    S_iCurrentColumn = x - 1; 
    gotoxy(x,y);		/* set cursor position */ 
} 
 
    static void 
myscroll(void) 
{ 
    short iRow, iColumn; 
    unsigned short uiValue; 
 
    /* Copy the screen */ 
    for (iRow = S_iTop; iRow < S_iBottom; iRow++) 
	movedata(S_selVideo, (iRow * Columns) << 1, 
		S_selVideo, ((iRow - 1) * Columns) << 1, 
		(S_iRight - S_iLeft + 1) << 1); 
 
    /* Clear the bottom row */ 
    uiValue = S_uiAttribute | ' '; 
    for (iColumn = S_iLeft - 1; iColumn < S_iRight; iColumn++) 
	_dosmemputw(&uiValue, 1, S_ulScreenBase 
			 + (S_iBottom - 1) * (Columns << 1) + (iColumn << 1)); 
} 
 
    static int 
myputch(int iChar) 
{ 
    unsigned int *puiLocation; 
    unsigned short uiValue; 
 
    if (iChar == '\n') 
    { 
	if (S_iCurrentRow >= S_iBottom - S_iTop) 
	    myscroll(); 
	else 
	    mygotoxy(S_iLeft, S_iCurrentRow + 2); 
    } 
    else if (iChar == '\r') 
	mygotoxy(S_iLeft, S_iCurrentRow + 1); 
    else if (iChar == '\b') 
	mygotoxy(S_iCurrentColumn, S_iCurrentRow + 1); 
    else if (iChar == 7) 
    { 
	sound(440);	/* short beep */ 
	delay(200); 
	nosound(); 
    } 
    else 
    { 
	uiValue = S_uiAttribute | (unsigned char)iChar; 
 
	_dosmemputw(&uiValue, 1, S_ulScreenBase 
		  + S_iCurrentRow * (Columns << 1) + (S_iCurrentColumn << 1)); 
 
	S_iCurrentColumn++; 
	if (S_iCurrentColumn >= S_iRight && S_iCurrentRow >= S_iBottom - S_iTop) 
	{ 
	    myscroll(); 
	    mygotoxy(S_iLeft, S_iCurrentRow + 2); 
	} 
	else 
	    mygotoxy(S_iCurrentColumn + 1, S_iCurrentRow + 1); 
    } 
 
    return 0; 
} 
 
    static void 
mywindow(int iLeft, int iTop, int iRight, int iBottom) 
{ 
    S_iLeft = iLeft; 
    S_iTop = iTop; 
    S_iRight = iRight; 
    S_iBottom = iBottom; 
    window(iLeft, iTop, iRight, iBottom); 
} 
 
    static void 
mytextinit(struct text_info *pTextinfo) 
{ 
    S_selVideo = __dpmi_segment_to_descriptor(S_ulScreenBase >> 4); 
    S_uiAttribute = pTextinfo->normattr << 8; 
    mywindow(1, 1, Columns, Rows); 
} 
 
    static void 
get_screenbase(void) 
{ 
    static union REGS	    regs; 
 
    /* old Hercules grafic card has different base address (Macewicz) */ 
    regs.h.ah = 0x0f; 
    (void)int86(0x10, ®s, ®s);	/* int 10 0f */ 
    if (regs.h.al == 0x07)		/* video mode 7 -- hercules mono */ 
	S_ulScreenBase = 0xb0000; 
    else 
	S_ulScreenBase = 0xb8000; 
} 
 
    static void 
mynormvideo(void) 
{ 
    S_uiAttribute = 0x700; 
    textbackground(0);			/*for delline() etc*/ 
} 
 
    static void 
mytextattr(int iAttribute) 
{ 
    S_uiAttribute = (unsigned short)iAttribute << 8; 
    iAttribute >>= 4; 
    if (iAttribute < 8) 
	textbackground(iAttribute);	/*for delline() etc*/ 
    else 
	textbackground(0); 
} 
 
    static void 
mytextcolor(int iTextColor) 
{ 
    S_uiAttribute = (unsigned short)((S_uiAttribute & 0xf000) 
					   | (unsigned short)iTextColor << 8); 
    textcolor(iTextColor);		/*for delline() etc*/ 
} 
 
    static void 
mytextbackground(int iBkgColor) 
{ 
    S_uiAttribute = (unsigned short)((S_uiAttribute & 0x0f00) 
					 | (unsigned short)(iBkgColor << 12)); 
    if (iBkgColor < 8) 
	textbackground(iBkgColor);	/*for delline() etc*/ 
    else 
	textbackground(0); 
} 
 
#else 
# define mygotoxy gotoxy 
# define myputch putch 
# define myscroll scroll 
# define mywindow window 
# define mynormvideo normvideo 
# define mytextattr textattr 
# define mytextcolor textcolor 
# define mytextbackground textbackground 
#endif 
 
/* 
 * Save/restore the shape of the cursor. 
 * call with FALSE to save, TRUE to restore 
 */ 
    static void 
mch_restore_cursor_shape(int restore) 
{ 
    static union REGS	    regs; 
    static int		    saved = FALSE; 
 
    if (restore) 
    { 
	if (saved) 
	    regs.h.ah = 0x01;	    /*Set Cursor*/ 
	else 
	    return; 
    } 
    else 
    { 
	regs.h.ah = 0x03;	    /*Get Cursor*/ 
	regs.h.bh = 0x00;	    /*Page */ 
	saved = TRUE; 
    } 
 
    (void)int86(0x10, ®s, ®s); 
} 
 
/* 
 * Set the shape of the cursor. 
 * 'thickness' can be from 0 (thin) to 7 (block) 
 */ 
    static void 
mch_set_cursor_shape(int thickness) 
{ 
    union REGS	    regs; 
 
    regs.h.ch = 7 - thickness;	    /*Starting Line*/ 
    regs.h.cl = 7;		    /*Ending Line*/ 
    regs.h.ah = 0x01;		    /*Set Cursor*/ 
    (void)int86(0x10, ®s, ®s); 
} 
 
    void 
mch_update_cursor(void) 
{ 
    int		idx; 
    int		thickness; 
 
    /* 
     * How the cursor is drawn depends on the current mode. 
     */ 
    idx = get_cursor_idx(); 
 
    if (cursor_table[idx].shape == SHAPE_BLOCK) 
	thickness = 7; 
    else 
	thickness = (7 * cursor_table[idx].percentage + 90) / 100; 
    mch_set_cursor_shape(thickness); 
} 
 
    long_u 
mch_avail_mem(int special) 
{ 
#ifdef DJGPP 
    return _go32_dpmi_remaining_virtual_memory(); 
#else 
    return coreleft(); 
#endif 
} 
 
#ifdef USE_MOUSE 
 
/* 
 * Set area where mouse can be moved to: The whole screen. 
 * Rows must be valid when calling! 
 */ 
    static void 
mouse_area(void) 
{ 
    union REGS	    regs; 
    int		    mouse_y_max;	    /* maximum mouse y coord */ 
 
    if (mouse_avail) 
    { 
	mouse_y_max = Rows * mouse_y_div - 1; 
	if (mouse_y_max < 199)	    /* is this needed? */ 
	    mouse_y_max = 199; 
	regs.x.cx = 0;	/* mouse visible between cx and dx */ 
	regs.x.dx = 639; 
	regs.x.ax = 7; 
	(void)int86(0x33, ®s, ®s); 
	regs.x.cx = 0;	/* mouse visible between cx and dx */ 
	regs.x.dx = mouse_y_max; 
	regs.x.ax = 8; 
	(void)int86(0x33, ®s, ®s); 
    } 
} 
 
    static void 
show_mouse(int on) 
{ 
    static int	    was_on = FALSE; 
    union REGS	    regs; 
 
    if (mouse_avail) 
    { 
	if (!mouse_active || mouse_hidden) 
	    on = FALSE; 
	/* 
	 * Careful: Each switch on must be compensated by exactly one switch 
	 * off 
	 */ 
	if (on && !was_on || !on && was_on) 
	{ 
	    was_on = on; 
	    regs.x.ax = on ? 1 : 2; 
	    int86(0x33, ®s, ®s);	/* show mouse */ 
	    if (on) 
		mouse_area(); 
	} 
    } 
} 
 
#endif 
 
#ifdef DJGPP 
/* 
 * DJGPP provides a kbhit() function that goes to the BIOS instead of DOS. 
 * This doesn't work for terminals connected to a serial port. 
 * Redefine kbhit() here to make it work. 
 */ 
    static int 
vim_kbhit(void) 
{ 
    union REGS regs; 
 
    regs.h.ah = 0x0b; 
    (void)intdos(®s, ®s); 
    return regs.h.al; 
} 
 
#ifdef kbhit 
# undef kbhit	    /* might have been defined in conio.h */ 
#endif 
 
#define kbhit()	vim_kbhit() 
#endif 
 
/* 
 * Simulate WaitForChar() by slowly polling with bioskey(1) or kbhit(). 
 * 
 * If Vim should work over the serial line after a 'ctty com1' we must use 
 * kbhit() and getch(). (jw) 
 * Usually kbhit() is not used, because then CTRL-C and CTRL-P 
 * will be catched by DOS (mool). 
 * 
 * return TRUE if a character is available, FALSE otherwise 
 */ 
 
#define FOREVER 1999999999L 
 
    static  int 
WaitForChar(long msec) 
{ 
    union REGS	regs; 
    long	starttime; 
    int		x, y; 
 
    starttime = biostime(0, 0L); 
 
    for (;;) 
    { 
#ifdef USE_MOUSE 
	long		clicktime; 
	static int	last_status = 0; 
 
	if (mouse_avail && mouse_active && mouse_click < 0) 
	{ 
	    regs.x.ax = 3; 
	    int86(0x33, ®s, ®s);	    /* check mouse status */ 
		/* only recognize button-down and button-up event */ 
	    x = regs.x.cx / mouse_x_div; 
	    y = regs.x.dx / mouse_y_div; 
	    if ((last_status == 0) != (regs.x.bx == 0)) 
	    { 
		if (last_status)	/* button up */ 
		    mouse_click = MOUSE_RELEASE; 
		else			/* button down */ 
		{ 
		    /* 
		     * Translate MSDOS mouse events to Vim mouse events. 
		     * TODO: should handle middle mouse button, by pressing 
		     * left and right at the same time. 
		     */ 
		    if (regs.x.bx & MSDOS_MOUSE_LEFT) 
			mouse_click = MOUSE_LEFT; 
		    else if (regs.x.bx & MSDOS_MOUSE_RIGHT) 
			mouse_click = MOUSE_RIGHT; 
		    else if (regs.x.bx & MSDOS_MOUSE_MIDDLE) 
			mouse_click = MOUSE_MIDDLE; 
 
		    /* 
		     * Find out if this is a multi-click 
		     */ 
		    clicktime = biostime(0, 0L); 
		    if (mouse_click_x == x && mouse_click_y == y && 
			    mouse_topline == curwin->w_topline && 
			    mouse_click_count != 4 && 
			    mouse_click == mouse_last_click && 
			    clicktime < mouse_click_time + p_mouset / BIOSTICK) 
			++mouse_click_count; 
		    else 
			mouse_click_count = 1; 
		    mouse_click_time = clicktime; 
		    mouse_last_click = mouse_click; 
		    mouse_click_x = x; 
		    mouse_click_y = y; 
		    mouse_topline = curwin->w_topline; 
		    SET_NUM_MOUSE_CLICKS(mouse_click, mouse_click_count); 
		} 
	    } 
	    else if (last_status && (x != mouse_x || y != mouse_y)) 
		mouse_click = MOUSE_DRAG; 
	    last_status = regs.x.bx; 
	    if (mouse_hidden && mouse_x >= 0 && (mouse_x != x || mouse_y != y)) 
	    { 
		mouse_hidden = FALSE; 
		show_mouse(TRUE); 
	    } 
	    mouse_x = x; 
	    mouse_y = y; 
	} 
#endif 
 
	if ((p_biosk ? bioskey(1) : kbhit()) || cbrk_pressed 
#ifdef USE_MOUSE 
						    || mouse_click >= 0 
#endif 
		) 
	    return TRUE; 
	/* 
	 * Use biostime() to wait until our time is done. 
	 * We busy-wait here.  Unfortunately, delay() and usleep() have been 
	 * reported to give problems with the original Windows 95.  This is 
	 * fixed in service pack 1, but not everybody installed that. 
	 */ 
	if (msec != FOREVER && biostime(0, 0L) > starttime + msec / BIOSTICK) 
	    break; 
    } 
    return FALSE; 
} 
 
/* 
 * don't do anything for about "msec" msec 
 */ 
    void 
mch_delay( 
    long	msec, 
    int		ignoreinput) 
{ 
    long	starttime; 
 
    if (ignoreinput) 
    { 
	/* 
	 * We busy-wait here.  Unfortunately, delay() and usleep() have been 
	 * reported to give problems with the original Windows 95.  This is 
	 * fixed in service pack 1, but not everybody installed that. 
	 */ 
	starttime = biostime(0, 0L); 
	while (biostime(0, 0L) < starttime + msec / BIOSTICK) 
	    ; 
    } 
    else 
	WaitForChar(msec); 
} 
 
/* 
 * this version of remove is not scared by a readonly (backup) file 
 * 
 * returns -1 on error, 0 otherwise (just like remove()) 
 */ 
    int 
mch_remove(char_u *name) 
{ 
    (void)mch_setperm(name, 0);    /* default permissions */ 
    return unlink((char *)name); 
} 
 
/* 
 * mch_write(): write the output buffer to the screen 
 */ 
    void 
mch_write( 
    char_u	*s, 
    int		len) 
{ 
    char_u	*p; 
    int		row, col; 
 
    if (term_console)	    /* translate ESC | sequences into bios calls */ 
	while (len--) 
	{ 
	    if (p_wd)	    /* testing: wait a bit for each char */ 
		WaitForChar(p_wd); 
 
	    if (s[0] == '\n') 
		myputch('\r'); 
	    else if (s[0] == ESC && len > 1 && s[1] == '|') 
	    { 
		switch (s[2]) 
		{ 
#ifdef DJGPP 
		case 'B':   ScreenVisualBell(); 
			    goto got3; 
#endif 
		case 'J':   clrscr(); 
			    goto got3; 
 
		case 'K':   clreol(); 
			    goto got3; 
 
		case 'L':   insline(); 
			    goto got3; 
 
		case 'M':   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' || *p == 'r') 
				{ 
				    if (*p == 'H')  /* set cursor position */ 
					mygotoxy(col, row); 
				    else	    /* set scroll region  */ 
					mywindow(1, row, Columns, col); 
				    len -= p - s; 
				    s = p + 1; 
				    continue; 
				} 
			    } 
			    else if (*p == 'm' || *p == 'f' || *p == 'b') 
			    { 
				if (*p == 'm')	    /* set color */ 
				{ 
				    if (row == 0) 
					mynormvideo();/* reset color */ 
				    else 
					mytextattr(row); 
				} 
				else if (*p == 'f') /* set foreground color */ 
				    mytextcolor(row); 
				else		    /* set background color */ 
				    mytextbackground(row); 
 
				len -= p - s; 
				s = p + 1; 
				continue; 
			    } 
		} 
	    } 
	    myputch(*s++); 
	} 
    else 
	write(1, s, (unsigned)len); 
} 
 
/* 
 * mch_inchar(): 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. 
 * 
 * return the number of characters obtained 
 */ 
    int 
mch_inchar( 
    char_u	*buf, 
    int		maxlen, 
    long	time) 
{ 
    int		len = 0; 
    int		c; 
    static int	nextchar = 0;	    /* may keep character when maxlen == 1 */ 
 
/* 
 * if we got a ctrl-C when we were busy, there will be a "^C" somewhere 
 * on the sceen, so we need to redisplay it. 
 */ 
    if (delayed_redraw) 
    { 
	delayed_redraw = FALSE; 
	update_screen(CLEAR); 
	setcursor(); 
	out_flush(); 
    } 
 
    /* return remaining character from last call */ 
    if (nextchar) 
    { 
	*buf = nextchar; 
	nextchar = 0; 
	return 1; 
    } 
 
#ifdef USE_MOUSE 
    if (time != 0) 
	show_mouse(TRUE); 
#endif 
    if (time >= 0) 
    { 
	if (WaitForChar(time) == 0)	/* no character available */ 
	{ 
#ifdef USE_MOUSE 
	    show_mouse(FALSE); 
#endif 
	    return 0; 
	} 
    } 
    else    /* time == -1 */ 
    { 
    /* 
     * If there is no character available within 2 seconds (default) 
     * write the autoscript file to disk 
     */ 
	if (WaitForChar(p_ut) == 0) 
	    updatescript(0); 
    } 
    WaitForChar(FOREVER);	/* wait for key or mouse click */ 
 
/* 
 * Try to read as many characters as there are, until the buffer is full. 
 */ 
    /* 
     * we will get at least one key. Get more if they are available 
     * After a ctrl-break we have to read a 0 (!) from the buffer. 
     * bioskey(1) will return 0 if no key is available and when a 
     * ctrl-break was typed. When ctrl-break is hit, this does not always 
     * implies a key hit. 
     */ 
    cbrk_pressed = FALSE; 
#ifdef USE_MOUSE 
    if (mouse_click >= 0 && maxlen >= 5) 
    { 
	len = 5; 
	*buf++ = ESC + 128; 
	*buf++ = 'M'; 
	*buf++ = mouse_click; 
	*buf++ = mouse_x + '!'; 
	*buf++ = mouse_y + '!'; 
	mouse_click = -1; 
    } 
    else 
#endif 
    { 
#ifdef USE_MOUSE 
	mouse_hidden = TRUE; 
#endif 
	if (p_biosk) 
	{ 
	    while ((len == 0 || bioskey(1)) && len < maxlen) 
	    { 
		c = bioskey(0);		/* get the key */ 
		/* 
		 * translate a few things for inchar(): 
		 * 0x0000 == CTRL-break		-> 3	(CTRL-C) 
		 * 0x0300 == CTRL-@		-> NUL 
		 * 0xnn00 == extended key code	-> K_NUL, nn 
		 * K_NUL			-> K_NUL, 3 
		 */ 
		if (c == 0) 
		    c = 3; 
		else if (c == 0x0300) 
		    c = NUL; 
		else if ((c & 0xff) == 0 || c == K_NUL) 
		{ 
		    if (c == K_NUL) 
			c = 3; 
		    else 
			c >>= 8; 
		    *buf++ = K_NUL; 
		    ++len; 
		} 
 
		if (len < maxlen) 
		{ 
		    *buf++ = c; 
		    len++; 
		} 
		else 
		    nextchar = c; 
	    } 
	} 
	else 
	{ 
	    while ((len == 0 || kbhit()) && len < maxlen) 
	    { 
		switch (c = getch()) 
		{ 
		    case 0: 
			/* NUL means that there is another character. 
			 * Get it immediately, because kbhit() doesn't always 
			 * return TRUE for the second character. 
			 */ 
			*buf++ = K_NUL; 
			++len; 
			c = getch(); 
			break; 
		    case K_NUL: 
			*buf++ = K_NUL; 
			++len; 
			c = 3; 
			break; 
		    case 3: 
			cbrk_pressed = TRUE; 
			/*FALLTHROUGH*/ 
		    default: 
			break; 
		} 
		if (len < maxlen) 
		{ 
		    *buf++ = c; 
		    ++len; 
		} 
		else 
		    nextchar = c; 
	    } 
	} 
    } 
#ifdef USE_MOUSE 
    show_mouse(FALSE); 
#endif 
 
    beep_count = 0;	    /* may beep again now that we got some chars */ 
    return len; 
} 
 
/* 
 * return non-zero if a character is available 
 */ 
    int 
mch_char_avail(void) 
{ 
    return WaitForChar(0L); 
} 
 
#ifdef DJGPP 
# define INT_ARG    int 
#else 
# define INT_ARG 
#endif 
 
/* 
 * function for ctrl-break interrupt 
 */ 
    static void interrupt 
#ifdef DJGPP 
catch_cbrk(int a) 
#else 
catch_cbrk(void) 
#endif 
{ 
    cbrk_pressed = TRUE; 
    ctrlc_pressed = TRUE; 
} 
 
/* 
 * ctrl-break handler for DOS. Never called when a ctrl-break is typed, because 
 * we catch interrupt 1b. If you type ctrl-C while Vim is waiting for a 
 * character this function is not called. When a ctrl-C is typed while Vim is 
 * busy this function may be called. By that time a ^C has been displayed on 
 * the screen, so we have to redisplay the screen. We can't do that here, 
 * because we may be called by DOS. The redraw is in mch_inchar(). 
 */ 
    static int _cdecl 
cbrk_handler(void) 
{ 
    delayed_redraw = TRUE; 
    return 1;		    /* resume operation after ctrl-break */ 
} 
 
/* 
 * function for critical error interrupt 
 * For DOS 1 and 2 return 0 (Ignore). 
 * For DOS 3 and later return 3 (Fail) 
 */ 
    static void interrupt 
catch_cint(bp, di, si, ds, es, dx, cx, bx, ax) 
    unsigned bp, di, si, ds, es, dx, cx, bx, ax; 
{ 
    ax = (ax & 0xff00);	    /* set AL to 0 */ 
    if (_osmajor >= 3) 
	ax |= 3;	    /* set AL to 3 */ 
} 
 
/* 
 * Set the interrupt vectors for use with Vim on or off. 
 * on == TRUE means as used within Vim 
 */ 
    static void 
set_interrupts(int on) 
{ 
    static int saved_cbrk; 
#ifndef DJGPP 
    static void interrupt (*old_cint)(); 
#endif 
    static void interrupt (*old_cbrk)(INT_ARG); 
 
    if (on) 
    { 
	saved_cbrk = getcbrk();		/* save old ctrl-break setting */ 
	setcbrk(0);			/* do not check for ctrl-break */ 
#ifdef DJGPP 
	old_cbrk = signal(SIGINT, catch_cbrk);	/* critical error interrupt */ 
#else 
	old_cint = getvect(0x24);	/* save old critical error interrupt */ 
	setvect(0x24, catch_cint);	/* install our critical error interrupt */ 
	old_cbrk = getvect(0x1B);	/* save old ctrl-break interrupt */ 
	setvect(0x1B, catch_cbrk);	/* install our ctrl-break interrupt */ 
	ctrlbrk(cbrk_handler);		/* vim's ctrl-break handler */ 
#endif 
	if (term_console) 
	    out_str(T_ME);		/* set colors */ 
    } 
    else 
    { 
	setcbrk(saved_cbrk);		/* restore ctrl-break setting */ 
#ifdef DJGPP 
	signal(SIGINT,old_cbrk);	/* critical error interrupt */ 
#else 
	setvect(0x24, old_cint);	/* restore critical error interrupt */ 
	setvect(0x1B, old_cbrk);	/* restore ctrl-break interrupt */ 
#endif 
	/* restore ctrl-break handler, how ??? */ 
	if (term_console) 
	    mynormvideo();		/* restore screen colors */ 
    } 
} 
 
/* 
 * We have no job control, fake it by starting a new shell. 
 */ 
    void 
mch_suspend(void) 
{ 
    suspend_shell(); 
} 
 
extern int _fmode; 
 
/* 
 * Prepare window for use by Vim. 
 * we do not use windows, there is not much to do here 
 */ 
    void 
mch_windinit(void) 
{ 
    union REGS regs; 
 
    term_console = TRUE;    /* assume using the console for the things here */ 
    _fmode = O_BINARY;	    /* we do our own CR-LF translation */ 
    out_flush(); 
    set_interrupts(TRUE);   /* catch interrupts */ 
 
#ifdef DJGPP 
    /* 
     * Use Long File Names by default, if $LFN not set. 
     */ 
    if (getenv("LFN") == NULL) 
	putenv("LFN=y"); 
 
    get_screenbase(); 
#endif 
 
#ifdef USE_MOUSE 
/* find out if a MS compatible mouse is available */ 
    regs.x.ax = 0; 
    (void)int86(0x33, ®s, ®s); 
    mouse_avail = regs.x.ax; 
    /* best guess for mouse coordinate computations */ 
    mch_get_winsize(); 
    if (Columns <= 40) 
	mouse_x_div = 16; 
    if (Rows == 30) 
	mouse_y_div = 16; 
#endif 
 
    /* 
     * Try switching to 16 colors for background, instead of 8 colors and 
     * blinking.  Does this always work?  Can the old value be restored? 
     */ 
    regs.x.ax = 0x1003; 
    regs.h.bl = 0x00; 
    regs.h.bh = 0x00; 
    int86(0x10, ®s, ®s); 
 
    /* Save the old cursor shape */ 
    mch_restore_cursor_shape(FALSE); 
    /* Initialise the cursor shape */ 
    mch_update_cursor(); 
} 
 
    int 
mch_check_win( 
    int		argc, 
    char	**argv) 
{ 
    /* store argv[0], may be used for $VIM */ 
    if (*argv[0] != NUL) 
	exe_name = FullName_save((char_u *)argv[0], FALSE); 
 
    if (isatty(1)) 
	return OK; 
    return FAIL; 
} 
 
/* 
 * Return TRUE if the input comes from a terminal, FALSE otherwise. 
 */ 
    int 
mch_input_isatty(void) 
{ 
    if (isatty(read_cmd_fd)) 
	return TRUE; 
    return FALSE; 
} 
 
#ifdef USE_FNAME_CASE 
/* 
 * fname_case(): Set the case of the file name, if it already exists. 
 */ 
    void 
fname_case(char_u *name) 
{ 
    char_u	    *tail; 
    struct ffblk    fb; 
 
    slash_adjust(name); 
    if (findfirst(name, &fb, 0) == 0) 
    { 
	tail = gettail(name); 
	if (STRLEN(tail) == STRLEN(fb.ff_name)) 
	    STRCPY(tail, fb.ff_name); 
    } 
} 
#endif 
 
/* 
 * mch_settitle(): set titlebar of our window. 
 * Dos console has no title. 
 */ 
    void 
mch_settitle( 
    char_u *title, 
    char_u *icon) 
{ 
} 
 
/* 
 * Restore the window/icon title. (which we don't have) 
 */ 
    void 
mch_restore_title(int which) 
{ 
} 
 
    int 
mch_can_restore_title(void) 
{ 
    return FALSE; 
} 
 
    int 
mch_can_restore_icon(void) 
{ 
    return FALSE; 
} 
 
/* 
 * Insert user name in s[len]. 
 */ 
    int 
mch_get_user_name( 
    char_u	*s, 
    int		len) 
{ 
    *s = NUL; 
    return FAIL; 
} 
 
/* 
 * Insert host name is s[len]. 
 */ 
    void 
mch_get_host_name( 
    char_u	*s, 
    int		len) 
{ 
#ifdef DJGPP 
    STRNCPY(s, "PC (32 bits Vim)", len); 
#else 
    STRNCPY(s, "PC (16 bits Vim)", len); 
#endif 
} 
 
/* 
 * return process ID 
 */ 
    long 
mch_get_pid(void) 
{ 
    return (long)0; 
} 
 
/* 
 * Get name of current directory into buffer 'buf' of length 'len' bytes. 
 * Return OK for success, FAIL for failure. 
 */ 
    int 
mch_dirname( 
    char_u	*buf, 
    int		len) 
{ 
#ifdef DJGPP 
    if (getcwd((char *)buf, len) == NULL) 
	return FAIL; 
    /* turn the '/'s returned by DJGPP into '\'s */ 
    slash_adjust(buf); 
    return OK; 
#else 
    return (getcwd((char *)buf, len) != NULL ? OK : FAIL); 
#endif 
} 
 
/* 
 * Change default drive (just like _chdrive of Borland C 3.1) 
 */ 
    static int 
change_drive(int drive) 
{ 
    union REGS regs; 
 
    regs.h.ah = 0x0e; 
    regs.h.dl = drive - 1; 
    intdos(®s, ®s);   /* set default drive */ 
    regs.h.ah = 0x19; 
    intdos(®s, ®s);   /* get default drive */ 
    if (regs.h.al == drive - 1) 
	return 0; 
    else 
	return -1; 
} 
 
/* 
 * Get absolute file name into buffer 'buf' of length 'len' bytes. 
 * All slashes are replaced with backslashes, to avoid trouble when comparing 
 * file names. 
 * 
 * return FAIL for failure, OK otherwise 
 */ 
    int 
mch_FullName( 
    char_u	*fname, 
    char_u	*buf, 
    int		len, 
    int		force) 
{ 
    if (fname == NULL)	/* always fail */ 
    { 
	*buf = NUL; 
	return FAIL; 
    } 
 
    if (!force && mch_isFullName(fname))	/* allready expanded */ 
    { 
	STRNCPY(buf, fname, len); 
	slash_adjust(buf); 
	return OK; 
    } 
 
#ifdef __BORLANDC__	/* the old Turbo C does not have this */ 
    if (_fullpath((char *)buf, (char *)fname, len - 1) == NULL) 
    { 
	STRNCPY(buf, fname, len);   /* failed, use the relative path name */ 
	slash_adjust(buf); 
	return FAIL; 
    } 
    if (mch_isdir(fname)) 
	STRCAT(buf, "\\"); 
    slash_adjust(buf); 
    return OK; 
#else			/* almost the same as mch_FullName in os_unix.c */ 
    { 
	int	l; 
	char_u	olddir[MAXPATHL]; 
	char_u	*p, *q; 
	int	c; 
	int	retval = OK; 
 
	*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 = vim_strrchr(fname, '/'); 
	q = vim_strrchr(fname, '\\'); 
	if (q != NULL && (p == NULL || q > p)) 
	    p = q; 
	q = vim_strrchr(fname, ':'); 
	if (q != NULL && (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 = FAIL; 
	    } 
	    else 
	    { 
		if ((q + 1) == p)		/* ... c:\foo	    */ 
		    q = p + 1;			/* -> c:\	    */ 
		else				/* but c:\foo\bar   */ 
		    q = p;			/* -> c:\foo	    */ 
 
		c = *q;			/* truncate at start of fname */ 
		*q = NUL; 
#ifdef DJGPP 
		STRCPY(buf, fname); 
		slash_adjust(buf);	/* needed when fname starts with \ */ 
		if (mch_chdir(buf))	/* change to the directory */ 
#else 
		if (mch_chdir(fname))	/* change to the directory */ 
#endif 
		    retval = FAIL; 
		else 
		{ 
		    fname = q; 
		    if (c == '\\')	    /* if we cut the name at a */ 
			fname++;	    /* '\', don't add it again */ 
		} 
		*q = c; 
	    } 
	} 
	if (getcwd(buf, len) == NULL) 
	{ 
	    retval = FAIL; 
	    *buf = NUL; 
	} 
	/* 
	 * Concatenate the file name to the path. 
	 */ 
	l = STRLEN(buf); 
	if (l && buf[l - 1] != '/' && buf[l - 1] != '\\') 
	    strcat(buf, "/"); 
	if (p) 
	    mch_chdir(olddir); 
	strcat(buf, fname); 
	slash_adjust(buf); 
	return retval; 
    } 
#endif 
} 
 
/* 
 * Replace all slashes by backslashes. 
 * This used to be the other way around, but MS-DOS sometimes has problems 
 * with slashes (e.g. in a command name).  We can't have mixed slashes and 
 * backslashes, because comparing file names will not work correctly.  The 
 * commands that use a file name should try to avoid the need to type a 
 * backslash twice. 
 */ 
    void 
slash_adjust(char_u *p) 
{ 
#ifdef OLD_DJGPP    /* this seems to have been fixed in DJGPP 2.01 */ 
    /* DJGPP can't handle a file name that starts with a backslash, and when it 
     * starts with a slash there should be no backslashes */ 
    if (*p == '\\' || *p == '/') 
	while (*p) 
	{ 
	    if (*p == '\\') 
		*p = '/'; 
	    ++p; 
	} 
    else 
#endif 
    while (*p) 
    { 
	if (*p == '/') 
	    *p = '\\'; 
	++p; 
    } 
} 
 
/* 
 * return TRUE is fname is an absolute path name 
 */ 
    int 
mch_isFullName(char_u *fname) 
{ 
    return (vim_strchr(fname, ':') != NULL); 
} 
 
/* 
 * get file permissions for 'name' 
 * -1 : error 
 * else FA_attributes defined in dos.h 
 */ 
    long 
mch_getperm(char_u *name) 
{ 
    return (long)_chmod((char *)name, 0, 0);	 /* get file mode */ 
} 
 
/* 
 * set file permission for 'name' to 'perm' 
 * 
 * return FAIL for failure, OK otherwise 
 */ 
    int 
mch_setperm( 
    char_u	*name, 
    long	perm) 
{ 
    perm |= FA_ARCH;	    /* file has changed, set archive bit */ 
    return (_chmod((char *)name, 1, (int)perm) == -1 ? FAIL : OK); 
} 
 
/* 
 * Set hidden flag for "name". 
 */ 
    void 
mch_hide(char_u *name) 
{ 
    /* DOS 6.2 share.exe causes "seek error on file write" errors when making 
     * the swap file hidden.  Thus don't do it. */ 
} 
 
/* 
 * return TRUE if "name" is a directory 
 * return FALSE if "name" is not a directory 
 * return FALSE for error 
 * 
 * beware of a trailing backslash 
 */ 
    int 
mch_isdir(char_u *name) 
{ 
    int	    f; 
    char_u  *p; 
 
    p = name + strlen((char *)name); 
    if (p > name) 
	--p; 
    if (*p == '\\')		    /* remove trailing backslash for a moment */ 
	*p = NUL; 
    else 
	p = NULL; 
    f = _chmod((char *)name, 0, 0); 
    if (p != NULL) 
	*p = '\\';		    /* put back backslash */ 
    if (f == -1) 
	return FALSE;		    /* file does not exist at all */ 
    if ((f & FA_DIREC) == 0) 
	return FALSE;		    /* not a directory */ 
    return TRUE; 
} 
 
/* 
 * Careful: mch_windexit() may be called before mch_windinit()! 
 */ 
    void 
mch_windexit(int r) 
{ 
    settmode(TMODE_COOK); 
    stoptermcap(); 
    set_interrupts(FALSE);	    /* restore interrupts */ 
    out_char('\r'); 
    out_char('\n'); 
    out_flush(); 
    ml_close_all(TRUE);		    /* remove all memfiles */ 
    mch_restore_cursor_shape(TRUE); 
    exit(r); 
} 
 
/* 
 * set the tty in (raw) ? "raw" : "cooked" mode 
 * Does not change the tty, as bioskey() and kbhit() work raw all the time. 
 */ 
    void 
mch_settmode(int tmode) 
{ 
} 
 
#ifdef USE_MOUSE 
    void 
mch_setmouse(int on) 
{ 
    mouse_active = on; 
    mouse_hidden = TRUE;	/* dont show it until moved */ 
} 
#endif 
 
/* 
 * set screen mode 
 * return FAIL for failure, OK otherwise 
 */ 
    int 
mch_screenmode(char_u *arg) 
{ 
    int		    mode; 
    int		    i; 
    static char	   *(names[]) = {"BW40", "C40", "BW80", "C80", "MONO", "C4350"}; 
    static int	    modes[]  = { BW40,	 C40,	BW80,	C80,   MONO,   C4350}; 
 
    mode = -1; 
    if (isdigit(*arg))		    /* mode number given */ 
	mode = atoi((char *)arg); 
    else 
    { 
	for (i = 0; i < sizeof(names) / sizeof(char_u *); ++i) 
	    if (stricmp(names[i], (char *)arg) == 0) 
	    { 
		mode = modes[i]; 
		break; 
	    } 
    } 
    if (mode == -1) 
    { 
	EMSG("Unsupported screen mode"); 
	return FAIL; 
    } 
    textmode(mode);		    /* use Borland function */ 
#ifdef DJGPP 
    /* base address may have changed */ 
    get_screenbase(); 
#endif 
 
    /* Screen colors may have changed. */ 
    out_str(T_ME); 
 
#ifdef USE_MOUSE 
    if (mode <= 1 || mode == 4 || mode == 5 || mode == 13 || mode == 0x13) 
	mouse_x_div = 16; 
    else 
	mouse_x_div = 8; 
    if (mode == 0x11 || mode == 0x12) 
	mouse_y_div = 16; 
    else if (mode == 0x10) 
	mouse_y_div = 14; 
    else 
	mouse_y_div = 8; 
    ui_get_winsize();	    /* Rows is used in mouse_area() */ 
    mouse_area();	    /* set area where mouse can go */ 
#endif 
    return OK; 
} 
 
/* 
 * Structure used by Turbo-C/Borland-C to store video parameters. 
 */ 
#ifndef DJGPP 
extern struct text_info _video; 
#endif 
 
/* 
 * try to get the real window size 
 * return FAIL for failure, OK otherwise 
 */ 
    int 
mch_get_winsize(void) 
{ 
    struct text_info textinfo; 
/* 
 * The screenwidth is returned by the BIOS OK. 
 * The screenheight is in a location in the bios RAM, if the display is EGA or 
 * VGA. 
 */ 
    if (!term_console) 
	return FAIL; 
    gettextinfo(&textinfo); 
    Columns = textinfo.screenwidth; 
    Rows = textinfo.screenheight; 
#ifndef DJGPP 
    if (textinfo.currmode > 10) 
	Rows = *(char far *)MK_FP(0x40, 0x84) + 1; 
#endif 
 
    /* 
     * don't call set_window() when not doing full screen, since it will move 
     * the cursor.  Also skip this when exiting. 
     */ 
    if (full_screen && !exiting) 
	set_window(); 
 
    if (Columns < MIN_COLUMNS || Rows < MIN_LINES) 
    { 
	/* these values are overwritten by termcap size or default */ 
	Columns = 80; 
	Rows = 25; 
	return FAIL; 
    } 
    check_winsize(); 
#ifdef DJGPP 
    mytextinit(&textinfo);   /* Added by JML, 1/15/98 */ 
#endif 
 
    return OK; 
} 
 
/* 
 * Set the active window for delline/insline. 
 */ 
    void 
set_window(void) 
{ 
#ifndef DJGPP 
    _video.screenheight = Rows; 
#endif 
    mywindow(1, 1, Columns, Rows); 
    screen_start(); 
} 
 
    void 
mch_set_winsize(void) 
{ 
    /* should try to set the window size to Rows and Columns */ 
    /* may involve switching display mode.... */ 
 
#ifdef USE_MOUSE 
    mouse_area();	    /* set area where mouse can go */ 
#endif 
} 
 
/* 
 * call shell, return FAIL for failure, OK otherwise 
 * options == SHELL_FILTER if called by do_filter() 
 * options == SHELL_COOKED if term needs cooked mode 
 */ 
    int 
mch_call_shell( 
    char_u	*cmd, 
    int		options) 
{ 
    int	    x; 
    char_u  *newcmd; 
 
    out_flush(); 
 
    if (options & SHELL_COOKED) 
	settmode(TMODE_COOK);	/* set to normal mode */ 
    set_interrupts(FALSE);	    /* restore interrupts */ 
 
#ifdef DJGPP 
    /* ignore signals while external command is running */ 
    signal(SIGINT, SIG_IGN); 
    signal(SIGHUP, SIG_IGN); 
    signal(SIGQUIT, SIG_IGN); 
    signal(SIGTERM, SIG_IGN); 
#endif 
    if (cmd == NULL) 
	x = system((char *)p_sh); 
    else 
    { 
#ifdef DJGPP 
	/* 
	 * Use 'shell' for system(). 
	 */ 
	setenv("SHELL", (char *)p_sh, 1); 
	x = system(cmd); 
#else 
	/* we use "command" to start the shell, slow but easy */ 
	newcmd = alloc(STRLEN(p_sh) + STRLEN(p_shcf) + STRLEN(cmd) + 3); 
	if (newcmd == NULL) 
	    x = -1; 
	else 
	{ 
	    sprintf((char *)newcmd, "%s %s %s", p_sh, p_shcf, cmd); 
	    x = system((char *)newcmd); 
	    vim_free(newcmd); 
	} 
#endif 
    } 
#ifdef DJGPP 
    signal(SIGINT, SIG_DFL); 
    signal(SIGHUP, SIG_DFL); 
    signal(SIGQUIT, SIG_DFL); 
    signal(SIGTERM, SIG_DFL); 
#endif 
    settmode(TMODE_RAW);	/* set to raw mode */ 
    set_interrupts(TRUE);	/* catch interrupts */ 
 
    if (x && !expand_interactively) 
    { 
	msg_putchar('\n'); 
	msg_outnum((long)x); 
	MSG_PUTS(" returned\n"); 
    } 
 
    /* resettitle();		    we don't have titles */ 
    (void)ui_get_winsize();	    /* display mode may have been changed */ 
    return x; 
} 
 
/* 
 * check for an "interrupt signal": CTRL-break or CTRL-C 
 */ 
    void 
mch_breakcheck(void) 
{ 
    if (ctrlc_pressed) 
    { 
	ctrlc_pressed = FALSE; 
	got_int = TRUE; 
    } 
} 
 
static int _cdecl pstrcmp();  /* BCC does not like the types */ 
 
    static int _cdecl 
pstrcmp(a, b) 
    char_u **a, **b; 
{ 
    return (stricmp((char *)*a, (char *)*b)); 
} 
 
    int 
mch_has_wildcard(char_u *s) 
{ 
    return (vim_strpbrk(s, (char_u *)"?*$~") != NULL); 
} 
 
    static void 
namelowcpy( 
    char_u *d, 
    char_u *s) 
{ 
#ifdef DJGPP 
    if (USE_LONG_FNAME)	    /* don't lower case on Windows 95/NT systems */ 
	while (*s) 
	    *d++ = *s++; 
    else 
#endif 
	while (*s) 
	    *d++ = TO_LOWER(*s++); 
    *d = NUL; 
} 
 
/* 
 * Recursive function to expand one path section with wildcards. 
 * Return the number of matches found. 
 */ 
    int 
mch_expandpath( 
    struct growarray	*gap, 
    char_u		*path, 
    int			flags) 
{ 
    char_u		*buf; 
    char_u		*p, *s, *e; 
    int			start_len, c; 
    struct ffblk	fb; 
    int			matches; 
 
    start_len = gap->ga_len; 
    buf = alloc(STRLEN(path) + BASENAMELEN + 5);   /* make room for file name */ 
    if (buf == NULL) 
	return 0; 
 
    /* 
     * 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; 
 
    /* if the file name ends in "*" and does not contain a ".", addd ".*" */ 
    if (e[-1] == '*' && vim_strchr(s, '.') == NULL) 
    { 
	*e++ = '.'; 
	*e++ = '*'; 
    } 
    /* now we have one wildcard component between s and e */ 
    *e = NUL; 
 
    /* If we are expanding wildcards we try both files and directories */ 
    if ((c = findfirst((char *)buf, &fb, 
			    (*path || (flags & EW_DIR)) ? FA_DIREC : 0)) != 0) 
    { 
	/* not found */ 
	vim_free(buf); 
	return 0; /* unexpanded or empty */ 
    } 
 
    while (!c) 
    { 
	namelowcpy((char *)s, fb.ff_name); 
	/* ignore "." and ".." */ 
	if (*s != '.' || (s[1] != NUL && (s[1] != '.' || s[2] != NUL))) 
	{ 
	    STRCAT(buf, path); 
	    if (mch_has_wildcard(path)) 
		(void)mch_expandpath(gap, buf, flags); 
	    else if (mch_getperm(buf) >= 0)	/* add existing file */ 
		addfile(gap, buf, flags); 
	} 
	c = findnext(&fb); 
    } 
    vim_free(buf); 
 
    matches = gap->ga_len - start_len; 
    if (matches) 
	qsort(((char_u **)gap->ga_data) + start_len, (size_t)matches, 
						   sizeof(char_u *), pstrcmp); 
    return matches; 
} 
 
/* 
 * The normal chdir() does not change the default drive. 
 * This one does. 
 */ 
    int 
mch_chdir(char *path) 
{ 
    if (path[0] == NUL)		    /* just checking... */ 
	return 0; 
    if (path[1] == ':')		    /* has a drive name */ 
    { 
	if (change_drive(TO_LOWER(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 */ 
} 
 
#ifdef DJGPP 
/* 
 * mch_rename() works around a bug in rename (aka MoveFile) in 
 * Windows 95: rename("foo.bar", "foo.bar~") will generate a 
 * file whose short file name is "FOO.BAR" (its long file name will 
 * be correct: "foo.bar~").  Because a file can be accessed by 
 * either its SFN or its LFN, "foo.bar" has effectively been 
 * renamed to "foo.bar", which is not at all what was wanted.  This 
 * seems to happen only when renaming files with three-character 
 * extensions by appending a suffix that does not include ".". 
 * Windows NT gets it right, however, with an SFN of "FOO~1.BAR". 
 * This works like mch_rename in os_win32.c, but is a bit simpler. 
 * 
 * Like rename(), returns 0 upon success, non-zero upon failure. 
 * Should probably set errno appropriately when errors occur. 
 */ 
 
    int 
mch_rename(const char *OldFile, const char *NewFile) 
{ 
    char_u  *TempFile; 
    int	    retval; 
    int	    fd; 
 
    /* rename() works correctly without long file names, so use that */ 
    if (!_USE_LFN) 
	return rename(OldFile, NewFile); 
 
    if ((TempFile = alloc((unsigned)(STRLEN(OldFile) + 13))) == NULL) 
	return -1; 
 
    STRCPY(TempFile, OldFile); 
    STRCPY(gettail(TempFile), "axlqwqhy.ba~"); 
    if (rename(OldFile, TempFile)) 
	retval = -1; 
    else 
    { 
	/* now create an empty file called OldFile; this prevents 
	 * the operating system using OldFile as an alias (SFN) 
	 * if we're renaming within the same directory.  For example, 
	 * we're editing a file called filename.asc.txt by its SFN, 
	 * filena~1.txt.  If we rename filena~1.txt to filena~1.txt~ 
	 * (i.e., we're making a backup while writing it), the SFN 
	 * for filena~1.txt~ will be filena~1.txt, by default, which 
	 * will cause all sorts of problems later in buf_write.  So, we 
	 * create an empty file called filena~1.txt and the system will have 
	 * to find some other SFN for filena~1.txt~, such as filena~2.txt 
	 */ 
	if ((fd = open(OldFile, O_RDWR|O_CREAT|O_EXCL, 0444)) < 0) 
	    return -1; 
	retval = rename(TempFile, NewFile); 
	close(fd); 
	mch_remove((char_u *)OldFile); 
    } 
    vim_free(TempFile); 
 
    return retval;  /* success */ 
} 
#endif 
 
/* 
 * Special version of getenv(): use $HOME when $VIM not defined. 
 */ 
    char_u * 
mch_getenv(char_u *var) 
{ 
    char_u  *retval; 
 
    retval = (char_u *)getenv((char *)var); 
 
    if (retval == NULL && STRCMP(var, "VIM") == 0) 
	retval = (char_u *)getenv("HOME"); 
 
    return retval; 
} 
 
#ifdef DJGPP 
/* 
 * setlocale() for DJGPP with MS-DOS codepage support 
 * Author: Cyril Slobin  
 * 
 * Scaled down a lot for use by Vim: Only support setlocale(LC_ALL, ""). 
 */ 
 
#undef setlocale 
 
#include  
#include  
#include  
 
#define UPCASE (__dj_ISALNUM | __dj_ISALPHA | __dj_ISGRAPH | __dj_ISPRINT | __dj_ISUPPER) 
#define LOCASE (__dj_ISALNUM | __dj_ISALPHA | __dj_ISGRAPH | __dj_ISPRINT | __dj_ISLOWER) 
 
    void 
djgpp_setlocale(void) 
{ 
    __dpmi_regs regs; 
    struct { char id; unsigned short off, seg; } __attribute__ ((packed)) info; 
    unsigned char buffer[0x82], lower, upper; 
    int i; 
 
    regs.x.ax = 0x6502; 
    regs.x.bx = 0xffff; 
    regs.x.dx = 0xffff; 
    regs.x.cx = 5; 
    regs.x.es = __tb >> 4; 
    regs.x.di = __tb & 0xf; 
 
    __dpmi_int(0x21, ®s); 
 
    if (regs.x.flags & 1) 
	return; 
 
    dosmemget(__tb, 5, &info); 
    dosmemget((info.seg << 4) + info.off, 0x82, buffer); 
 
    if (*(short *)buffer != 0x80) 
	return; 
 
    /* Fix problem of underscores being replaced with y-umlaut. (Levin) */ 
    if (buffer[26] == 0x5f) 
	buffer[26] = 0x98; 
 
    for (i = 0; i < 0x80; i++) 
    { 
	lower = i + 0x80; 
	upper = (buffer+2)[i]; 
	if (lower != upper) 
	{ 
	    __dj_ctype_flags[lower+1] = LOCASE; 
	    __dj_ctype_toupper[lower+1] = upper; 
	    if (__dj_ctype_flags[upper+1] == 0) 
		__dj_ctype_flags[upper+1] = UPCASE; 
	    if (__dj_ctype_tolower[upper+1] == upper) 
		__dj_ctype_tolower[upper+1] = lower; 
	} 
    } 
 
    return; 
} 
#endif /* DJGPP */