blob: 3449d23b9b5cd4ca6aba819fec57cb7713613764 [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001/* vi:set ts=8 sts=4 sw=4:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * os_msdos.c
12 *
13 * MSDOS system-dependent routines.
14 * A cheap plastic imitation of the amiga dependent code.
15 * A lot in this file was made by Juergen Weigert (jw).
16 *
17 * DJGPP changes by Gert van Antwerpen
18 * Faster text screens by John Lange (jlange@zilker.net)
19 * Windows clipboard functionality added by David Kotchan (dk)
20 *
21 * Some functions are also used for Win16 (MS-Windows 3.1).
22 */
23
Bram Moolenaar071d4272004-06-13 20:20:40 +000024#include "vim.h"
25
26#include <conio.h>
Bram Moolenaar071d4272004-06-13 20:20:40 +000027
28/*
29 * MS-DOS only code, not used for Win16.
30 */
31#ifndef WIN16
32
33
34#include <bios.h>
35#ifdef DJGPP
36# include <dpmi.h>
37# include <signal.h>
38# include <sys/movedata.h>
39# include <crt0.h>
40# ifdef FEAT_CLIPBOARD
41# include <sys/segments.h>
42# endif
43#else
44# include <alloc.h>
45#endif
46
47#if defined(DJGPP) || defined(PROTO)
48# define _cdecl /* DJGPP doesn't have this */
49#endif
50
51static int cbrk_pressed = FALSE; /* set by ctrl-break interrupt */
52static int ctrlc_pressed = FALSE; /* set when ctrl-C or ctrl-break detected */
53static int delayed_redraw = FALSE; /* set when ctrl-C detected */
54
55static int bioskey_read = _NKEYBRD_READ; /* bioskey() argument: read key */
56static int bioskey_ready = _NKEYBRD_READY; /* bioskey() argument: key ready? */
57
58#ifdef FEAT_MOUSE
59static int mouse_avail = FALSE; /* mouse present */
60static int mouse_active; /* mouse enabled */
61static int mouse_hidden; /* mouse not shown */
62static int mouse_click = -1; /* mouse status */
63static int mouse_last_click = -1; /* previous status at click */
Bram Moolenaar035db9f2007-05-10 18:02:27 +000064static int mouse_x = -1; /* mouse x coordinate */
65static int mouse_y = -1; /* mouse y coordinate */
Bram Moolenaar071d4272004-06-13 20:20:40 +000066static long mouse_click_time = 0; /* biostime() of last click */
67static int mouse_click_count = 0; /* count for multi-clicks */
68static int mouse_click_x = 0; /* x of previous mouse click */
69static int mouse_click_y = 0; /* y of previous mouse click */
70static linenr_T mouse_topline = 0; /* w_topline at previous mouse click */
71#ifdef FEAT_DIFF
72static int mouse_topfill = 0; /* w_topfill at previous mouse click */
73#endif
74static int mouse_x_div = 8; /* column = x coord / mouse_x_div */
75static int mouse_y_div = 8; /* line = y coord / mouse_y_div */
76#endif
77
78#define BIOSTICK 55 /* biostime() increases one tick about
79 every 55 msec */
80
81static int orig_attr = 0x0700; /* video attributes when starting */
82
83static int S_iLeft = 0; /* Scroll window; these are 1 offset */
84static int S_iTop = 0;
85static int S_iRight = 0;
86static int S_iBottom = 0;
87
88/*
89 * Need to remember the values, because we set horizontal and vertical
90 * edges separately.
91 */
92 static void
93mywindow(int iLeft, int iTop, int iRight, int iBottom)
94{
95 S_iLeft = iLeft;
96 S_iTop = iTop;
97 S_iRight = iRight;
98 S_iBottom = iBottom;
99 window(iLeft, iTop, iRight, iBottom);
100}
101
102#ifdef DJGPP
103/*
104 * For DJGPP, use our own functions for fast text screens. JML 1/18/98
105 */
106
107unsigned long S_ulScreenBase = 0xb8000;
108unsigned short S_uiAttribute = 0;
109int S_iCurrentRow = 0; /* These are 0 offset */
110int S_iCurrentColumn = 0;
111short S_selVideo; /* Selector for DJGPP direct video transfers */
112
113/*
114 * Use burst writes to improve mch_write speed - VJN 01/10/99
115 */
116unsigned short S_linebuffer[8000]; /* <VN> enough for 160x50 */
117unsigned short S_blankbuffer[256]; /* <VN> max length of console line */
118unsigned short *S_linebufferpos = S_linebuffer;
119int S_iBufferRow;
120int S_iBufferColumn;
121
122 static void
123myflush(void)
124{
125 if (S_linebufferpos != S_linebuffer)
126 {
127 _dosmemputw(S_linebuffer, (S_linebufferpos - S_linebuffer),
128 S_ulScreenBase
129 + S_iBufferRow * (Columns << 1) + (S_iBufferColumn << 1));
130 S_linebufferpos = S_linebuffer;
131 }
132}
133
134 static void
135mygotoxy(int x, int y)
136{
137 S_iCurrentRow = y - 1;
138 S_iCurrentColumn = x - 1;
139}
140
141/*
142 * Set the system cursor to our cursor position.
143 */
144 static void
145set_sys_cursor(void)
146{
147 if (term_console && full_screen)
148 {
149 myflush();
150 gotoxy(S_iCurrentColumn + 1, S_iCurrentRow + 1);
151 }
152}
153
154 static void
155setblankbuffer(unsigned short uiValue)
156{
157 int i;
158 static unsigned short olduiValue = 0;
159
160 if (olduiValue != uiValue)
161 {
162 /* Load blank line buffer with spaces */
163 for (i = 0; i < Columns; ++i)
164 S_blankbuffer[i] = uiValue;
165 olduiValue = uiValue;
166 }
167}
168
169 static void
170myclreol(void)
171{
172 /* Clear to end of line */
173 setblankbuffer(S_uiAttribute | ' ');
174 _dosmemputw(S_blankbuffer, S_iRight - S_iCurrentColumn, S_ulScreenBase
175 + (S_iCurrentRow) * (Columns << 1)
176 + (S_iCurrentColumn << 1));
177}
178
179 static void
180myclrscr(void)
181{
182 /* Clear whole screen */
183 short iColumn;
184 int endpoint = (Rows * Columns) << 1;
185
186 setblankbuffer(S_uiAttribute | ' ');
187
188 for (iColumn = 0; iColumn < endpoint; iColumn += (Columns << 1))
189 _dosmemputw(S_blankbuffer, Columns, S_ulScreenBase + iColumn);
190}
191
192 static void
193mydelline(void)
194{
195 short iRow, iColumn;
196
197 iColumn = (S_iLeft - 1) << 1;
198
199 /* Copy the lines underneath */
200 for (iRow = S_iCurrentRow; iRow < S_iBottom - 1; iRow++)
201 movedata(S_selVideo, (((iRow + 1) * Columns) << 1) + iColumn,
202 S_selVideo, ((iRow * Columns) << 1) + iColumn,
203 (S_iRight - S_iLeft + 1) << 1);
204
205 /* Clear the new row */
206 setblankbuffer(S_uiAttribute | ' ');
207
208 _dosmemputw(S_blankbuffer, (S_iRight - S_iLeft) + 1, S_ulScreenBase
209 + (S_iBottom - 1) * (Columns << 1) + iColumn);
210}
211
212 static void
213myinsline(void)
214{
215 short iRow, iColumn;
216
217 iColumn = (S_iLeft - 1) << 1;
218
219 /* Copy the lines underneath */
220 for (iRow = S_iBottom - 1; iRow >= S_iTop; iRow--)
221 movedata(S_selVideo, (((iRow - 1) * Columns) << 1) + iColumn,
222 S_selVideo, ((iRow * Columns) << 1) + iColumn,
223 (S_iRight - S_iLeft + 1) << 1);
224
225 /* Clear the new row */
226 setblankbuffer(S_uiAttribute | ' ');
227
228 _dosmemputw(S_blankbuffer, (S_iRight - S_iLeft) + 1, S_ulScreenBase
229 + (S_iTop - 1) * (Columns << 1) + iColumn);
230}
231
232/*
233 * Scroll the screen one line up, clear the last line.
234 */
235 static void
236myscroll(void)
237{
238 short iRow, iColumn;
239
240 iColumn = (S_iLeft - 1) << 1;
241
242 /* Copy the screen */
243 for (iRow = S_iTop; iRow < S_iBottom; iRow++)
244 movedata(S_selVideo, ((iRow * Columns) << 1) + iColumn,
245 S_selVideo, (((iRow - 1) * Columns) << 1) + iColumn,
246 (S_iRight - S_iLeft + 1) << 1);
247
248 /* Clear the bottom row */
249 setblankbuffer(S_uiAttribute | ' ');
250
251 _dosmemputw(S_blankbuffer, (S_iRight - S_iLeft) + 1, S_ulScreenBase
252 + (S_iBottom - 1) * (Columns << 1) + iColumn);
253}
254
255 static int
256myputch(int iChar)
257{
258 unsigned short uiValue;
259
260 if (iChar == '\n')
261 {
262 myflush();
263 if (S_iCurrentRow >= S_iBottom - S_iTop)
264 myscroll();
265 else
266 {
267 S_iCurrentColumn = S_iLeft - 1;
268 S_iCurrentRow++;
269 }
270 }
271 else if (iChar == '\r')
272 {
273 myflush();
274 S_iCurrentColumn = S_iLeft - 1;
275 }
276 else if (iChar == '\b')
277 {
278 myflush();
279 if (S_iCurrentColumn >= S_iLeft)
280 S_iCurrentColumn--;
281 }
282 else if (iChar == 7)
283 {
284 sound(440); /* short beep */
285 delay(200);
286 nosound();
287 }
288 else
289 {
290 uiValue = S_uiAttribute | (unsigned char)iChar;
291
292 /*
293 * Normal char - are we starting to buffer?
294 */
295 if (S_linebufferpos == S_linebuffer)
296 {
297 S_iBufferColumn = S_iCurrentColumn;
298 S_iBufferRow = S_iCurrentRow;
299 }
300
301 *S_linebufferpos++ = uiValue;
302
303 S_iCurrentColumn++;
304 if (S_iCurrentColumn >= S_iRight && S_iCurrentRow >= S_iBottom - S_iTop)
305 {
306 myflush();
307 myscroll();
308 S_iCurrentColumn = S_iLeft - 1;
309 S_iCurrentRow++;
310 }
311 }
312
313 return 0;
314}
315
316 static void
317mytextinit(struct text_info *pTextinfo)
318{
319 S_selVideo = __dpmi_segment_to_descriptor(S_ulScreenBase >> 4);
320 S_uiAttribute = pTextinfo->normattr << 8;
321}
322
323 static void
324get_screenbase(void)
325{
326 static union REGS regs;
327
328 /* old Hercules grafic card has different base address (Macewicz) */
329 regs.h.ah = 0x0f;
330 (void)int86(0x10, &regs, &regs); /* int 10 0f */
331 if (regs.h.al == 0x07) /* video mode 7 -- hercules mono */
332 S_ulScreenBase = 0xb0000;
333 else
334 S_ulScreenBase = 0xb8000;
335}
336
337 static void
338mytextattr(int iAttribute)
339{
340 S_uiAttribute = (unsigned short)iAttribute << 8;
341}
342
343 static void
344mynormvideo(void)
345{
346 mytextattr(orig_attr);
347}
348
349 static void
350mytextcolor(int iTextColor)
351{
352 S_uiAttribute = (unsigned short)((S_uiAttribute & 0xf000)
353 | (unsigned short)iTextColor << 8);
354}
355
356 static void
357mytextbackground(int iBkgColor)
358{
359 S_uiAttribute = (unsigned short)((S_uiAttribute & 0x0f00)
360 | (unsigned short)(iBkgColor << 12));
361}
362/*
363 * Getdigits: Get a number from a string and skip over it.
364 * Note: the argument is a pointer to a char_u pointer!
365 */
366
367 static long
368mygetdigits(pp)
369 char_u **pp;
370{
371 char_u *p;
372 long retval = 0;
373
374 p = *pp;
375 if (*p == '-') /* skip negative sign */
376 ++p;
377 while (VIM_ISDIGIT(*p))
378 {
379 retval = (retval * 10) + (*p - '0');
380 ++p;
381 }
382 if (**pp == '-') /* process negative sign */
383 retval = -retval;
384
385 *pp = p;
386 return retval;
387}
388#else
389# define mygotoxy gotoxy
390# define myputch putch
391# define myscroll scroll
392# define mynormvideo normvideo
393# define mytextattr textattr
394# define mytextcolor textcolor
395# define mytextbackground textbackground
396# define mygetdigits getdigits
397# define myclreol clreol
398# define myclrscr clrscr
399# define myinsline insline
400# define mydelline delline
401#endif
402
403static const struct
404{
405 char_u scancode;
406 char_u metakey;
407} altkey_table[] =
408{
409 {0x1e, 0xe1}, /* a */
410 {0x30, 0xe2}, /* b */
411 {0x2e, 0xe3}, /* c */
412 {0x20, 0xe4}, /* d */
413 {0x12, 0xe5}, /* e */
414 {0x21, 0xe6}, /* f */
415 {0x22, 0xe7}, /* g */
416 {0x23, 0xe8}, /* h */
417 {0x17, 0xe9}, /* i */
418 {0x24, 0xea}, /* j */
419 {0x25, 0xeb}, /* k */
420 {0x26, 0xec}, /* l */
421 {0x32, 0xed}, /* m */
422 {0x31, 0xee}, /* n */
423 {0x18, 0xef}, /* o */
424 {0x19, 0xf0}, /* p */
425 {0x10, 0xf1}, /* q */
426 {0x13, 0xf2}, /* r */
427 {0x1f, 0xf3}, /* s */
428 {0x14, 0xf4}, /* t */
429 {0x16, 0xf5}, /* u */
430 {0x2f, 0xf6}, /* v */
431 {0x11, 0xf7}, /* w */
432 {0x2d, 0xf8}, /* x */
433 {0x15, 0xf9}, /* y */
434 {0x2c, 0xfa}, /* z */
435 {0x78, 0xb1}, /* 1 */
436 {0x79, 0xb2}, /* 2 */
437 {0x7a, 0xb3}, /* 3 */
438 {0x7b, 0xb4}, /* 4 */
439 {0x7c, 0xb5}, /* 5 */
440 {0x7d, 0xb6}, /* 6 */
441 {0x7e, 0xb7}, /* 7 */
442 {0x7f, 0xb8}, /* 8 */
443 {0x80, 0xb9}, /* 9 */
444 {0x81, 0xb0}, /* 0 */
445};
446
447/*
448 * Translate extended keycodes into meta-chars where applicable
449 */
450 static int
451translate_altkeys(int rawkey)
452{
453 int i, c;
454
455 if ((rawkey & 0xff) == 0)
456 {
457 c = (rawkey >> 8);
458 for (i = sizeof(altkey_table) / sizeof(altkey_table[0]); --i >= 0; )
459 {
460 if (c == altkey_table[i].scancode)
461 return (int)altkey_table[i].metakey;
462 }
463 }
464 return rawkey;
465}
466
467/*
Bram Moolenaar035db9f2007-05-10 18:02:27 +0000468 * Set normal fg/bg color, based on T_ME. Called when t_me has been set.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000469 */
470 void
471mch_set_normal_colors()
472{
473 char_u *p;
474 int n;
475
476 cterm_normal_fg_color = (orig_attr & 0xf) + 1;
477 cterm_normal_bg_color = ((orig_attr >> 4) & 0xf) + 1;
478 if (T_ME[0] == ESC && T_ME[1] == '|')
479 {
480 p = T_ME + 2;
481 n = getdigits(&p);
482 if (*p == 'm' && n > 0)
483 {
484 cterm_normal_fg_color = (n & 0xf) + 1;
485 cterm_normal_bg_color = ((n >> 4) & 0xf) + 1;
486 }
487 }
488}
489
490#if defined(MCH_CURSOR_SHAPE) || defined(PROTO)
491/*
492 * Save/restore the shape of the cursor.
493 * call with FALSE to save, TRUE to restore
494 */
495 static void
496mch_restore_cursor_shape(int restore)
497{
498 static union REGS regs;
499 static int saved = FALSE;
500
501 if (restore)
502 {
503 if (saved)
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000504 regs.h.ah = 0x01; /* Set Cursor */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000505 else
506 return;
507 }
508 else
509 {
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000510 regs.h.ah = 0x03; /* Get Cursor */
511 regs.h.bh = 0x00; /* Page */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000512 saved = TRUE;
513 }
514
515 (void)int86(0x10, &regs, &regs);
516}
517
518/*
519 * Set the shape of the cursor.
520 * 'thickness' can be from 0 (thin) to 7 (block)
521 */
522 static void
523mch_set_cursor_shape(int thickness)
524{
525 union REGS regs;
526
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000527 regs.h.ch = 7 - thickness; /* Starting Line */
528 regs.h.cl = 7; /* Ending Line */
529 regs.h.ah = 0x01; /* Set Cursor */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000530 (void)int86(0x10, &regs, &regs);
531}
532
533 void
534mch_update_cursor(void)
535{
536 int idx;
537 int thickness;
538
539 /*
540 * How the cursor is drawn depends on the current mode.
541 */
542 idx = get_shape_idx(FALSE);
543
544 if (shape_table[idx].shape == SHAPE_BLOCK)
545 thickness = 7;
546 else
547 thickness = (7 * shape_table[idx].percentage + 90) / 100;
548 mch_set_cursor_shape(thickness);
549}
550#endif
551
552/*
553 * Return amount of memory currently available.
554 */
555 long_u
556mch_avail_mem(int special)
557{
558#ifdef DJGPP
559 return _go32_dpmi_remaining_virtual_memory();
560#else
561 return coreleft();
562#endif
563}
564
565#ifdef FEAT_MOUSE
566
567/*
568 * Set area where mouse can be moved to: The whole screen.
569 * Rows and Columns must be valid when calling!
570 */
571 static void
572mouse_area(void)
573{
574 union REGS regs;
575
576 if (mouse_avail)
577 {
578 regs.x.cx = 0; /* mouse visible between cx and dx */
579 regs.x.dx = Columns * mouse_x_div - 1;
580 regs.x.ax = 7;
581 (void)int86(0x33, &regs, &regs);
582
583 regs.x.cx = 0; /* mouse visible between cx and dx */
584 regs.x.dx = Rows * mouse_y_div - 1;
585 regs.x.ax = 8;
586 (void)int86(0x33, &regs, &regs);
587 }
588}
589
590 static void
591show_mouse(int on)
592{
593 static int was_on = FALSE;
594 union REGS regs;
595
596 if (mouse_avail)
597 {
598 if (!mouse_active || mouse_hidden)
599 on = FALSE;
600 /*
601 * Careful: Each switch on must be compensated by exactly one switch
602 * off
603 */
604 if ((on && !was_on) || (!on && was_on))
605 {
606 was_on = on;
607 regs.x.ax = on ? 1 : 2;
608 int86(0x33, &regs, &regs); /* show mouse */
609 if (on)
610 mouse_area();
611 }
612 }
613}
614
615#endif
616
617/*
618 * Version of kbhit() and getch() that use direct console I/O.
619 * This avoids trouble with CTRL-P and the like, and should work over a telnet
620 * connection (it works for Xvi).
621 */
622
623static int cons_key = -1;
624
625/*
626 * Try to get one character directly from the console.
627 * If there is a key, it is stored in cons_key.
628 * Only call when cons_key is -1!
629 */
630 static void
631cons_getkey(void)
632{
633 union REGS regs;
634
635 /* call DOS function 6: Direct console I/O */
636 regs.h.ah = 0x06;
637 regs.h.dl = 0xff;
638 (void)intdos(&regs, &regs);
639 if ((regs.x.flags & 0x40) == 0) /* zero flag not set? */
640 cons_key = (regs.h.al & 0xff);
641}
642
643/*
644 * Return TRUE if a character is available.
645 */
646 static int
647cons_kbhit(void)
648{
649 if (cons_key < 0)
650 cons_getkey();
651 return (cons_key >= 0);
652}
653
654/*
655 * Return a character from the console.
656 * Should only be called when vim_kbhit() returns TRUE.
657 */
658 static int
659cons_getch(void)
660{
661 int c = -1;
662
663 if (cons_key < 0)
664 cons_getkey();
665 c = cons_key;
666 cons_key = -1;
667 return c;
668}
669
670
671#ifdef DJGPP
672/*
673 * DJGPP provides a kbhit() function that goes to the BIOS instead of DOS.
674 * This doesn't work for terminals connected to a serial port.
675 * Redefine kbhit() here to make it work.
676 */
677 static int
678vim_kbhit(void)
679{
680 union REGS regs;
681
682 regs.h.ah = 0x0b;
683 (void)intdos(&regs, &regs);
684 return regs.h.al;
685}
686
687#ifdef kbhit
688# undef kbhit /* might have been defined in conio.h */
689#endif
690#define kbhit() vim_kbhit()
691
692#endif
693
694/*
695 * Simulate WaitForChar() by slowly polling with bioskey(1) or kbhit().
696 *
697 * If Vim should work over the serial line after a 'ctty com1' we must use
698 * kbhit() and getch(). (jw)
699 * Usually kbhit() is not used, because then CTRL-C and CTRL-P
700 * will be catched by DOS (mool).
701 *
702 * return TRUE if a character is available, FALSE otherwise
703 */
704
705#define FOREVER 1999999999L
706
707 static int
708WaitForChar(long msec)
709{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000710 long starttime = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000711
712 if (msec != 0)
713 starttime = biostime(0, 0L);
714
715 for (;;)
716 {
717#ifdef FEAT_MOUSE
718 long clicktime;
719 static int old_status = 0;
Bram Moolenaarc01140a2006-03-24 22:21:52 +0000720 union REGS regs;
721 int x, y;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000722
723 if (mouse_avail && mouse_active && mouse_click < 0)
724 {
725 regs.x.ax = 3;
726 int86(0x33, &regs, &regs); /* check mouse status */
727 /* only recognize button-down and button-up event */
728 x = regs.x.cx / mouse_x_div;
729 y = regs.x.dx / mouse_y_div;
730 if ((old_status == 0) != (regs.x.bx == 0))
731 {
732 if (old_status) /* button up */
733 mouse_click = MOUSE_RELEASE;
734 else /* button down */
735 {
736 /*
737 * Translate MSDOS mouse events to Vim mouse events.
738 * TODO: should handle middle mouse button, by pressing
739 * left and right at the same time.
740 */
741 if (regs.x.bx & MSDOS_MOUSE_LEFT)
742 mouse_click = MOUSE_LEFT;
743 else if (regs.x.bx & MSDOS_MOUSE_RIGHT)
744 mouse_click = MOUSE_RIGHT;
745 else if (regs.x.bx & MSDOS_MOUSE_MIDDLE)
746 mouse_click = MOUSE_MIDDLE;
747
748 /*
749 * Find out if this is a multi-click
750 */
751 clicktime = biostime(0, 0L);
752 if (mouse_click_x == x && mouse_click_y == y
753 && mouse_topline == curwin->w_topline
754#ifdef FEAT_DIFF
755 && mouse_topfill == curwin->w_topfill
756#endif
757 && mouse_click_count != 4
758 && mouse_click == mouse_last_click
759 && clicktime < mouse_click_time
760 + p_mouset / BIOSTICK)
761 ++mouse_click_count;
762 else
763 mouse_click_count = 1;
764 mouse_click_time = clicktime;
765 mouse_last_click = mouse_click;
766 mouse_click_x = x;
767 mouse_click_y = y;
768 mouse_topline = curwin->w_topline;
769#ifdef FEAT_DIFF
770 mouse_topfill = curwin->w_topfill;
771#endif
772 SET_NUM_MOUSE_CLICKS(mouse_click, mouse_click_count);
773 }
774 }
775 else if (old_status && (x != mouse_x || y != mouse_y))
776 mouse_click = MOUSE_DRAG;
777 old_status = regs.x.bx;
778 if (mouse_hidden && mouse_x >= 0 && (mouse_x != x || mouse_y != y))
779 {
780 mouse_hidden = FALSE;
781 show_mouse(TRUE);
782 }
783 mouse_x = x;
784 mouse_y = y;
785 }
786#endif
787
788 if ((p_consk ? cons_kbhit()
789 : p_biosk ? bioskey(bioskey_ready) : kbhit())
790 || cbrk_pressed
791#ifdef FEAT_MOUSE
792 || mouse_click >= 0
793#endif
794 )
795 return TRUE;
796 /*
797 * Use biostime() to wait until our time is done.
798 * We busy-wait here. Unfortunately, delay() and usleep() have been
799 * reported to give problems with the original Windows 95. This is
800 * fixed in service pack 1, but not everybody installed that.
801 * The DJGPP implementation of usleep() uses a busy-wait loop too.
802 */
803 if (msec == 0 || (msec != FOREVER
804 && biostime(0, 0L) > starttime + msec / BIOSTICK))
805 break;
806
807#ifdef DJGPP
808 /* Yield the CPU to the next process. */
809 __dpmi_yield();
810#endif
811 }
812 return FALSE;
813}
814
815/*
816 * don't do anything for about "msec" msec
817 */
818 void
819mch_delay(
820 long msec,
821 int ignoreinput)
822{
823 long starttime;
824
825 if (ignoreinput)
826 {
827 /*
828 * We busy-wait here. Unfortunately, delay() and usleep() have been
829 * reported to give problems with the original Windows 95. This is
830 * fixed in service pack 1, but not everybody installed that.
831 */
832 starttime = biostime(0, 0L);
833 while (biostime(0, 0L) < starttime + msec / BIOSTICK)
834 ;
835 }
836 else
837 WaitForChar(msec);
838}
839
840/*
841 * mch_write(): write the output buffer to the screen
842 */
843 void
844mch_write(
845 char_u *s,
846 int len)
847{
848 char_u *p;
849 int row, col;
850
851 if (term_console && full_screen)
852 while (len--)
853 {
854 /* translate ESC | sequences into bios calls */
855 if (p_wd) /* testing: wait a bit for each char */
856 WaitForChar(p_wd);
857
858 if (s[0] == '\n')
859#ifdef DJGPP
860 {
861 myflush();
862 S_iCurrentColumn = S_iLeft - 1;
863 }
864#else
865 myputch('\r');
866#endif
867 else if (s[0] == ESC && len > 1 && s[1] == '|')
868 {
869 switch (s[2])
870 {
871#ifdef DJGPP
872 case 'B': ScreenVisualBell();
873 goto got3;
874#endif
875 case 'J':
876#ifdef DJGPP
877 myflush();
878#endif
879 myclrscr();
880 goto got3;
881
882 case 'K':
883#ifdef DJGPP
884 myflush();
885#endif
886 myclreol();
887 goto got3;
888
889 case 'L':
890#ifdef DJGPP
891 myflush();
892#endif
893 myinsline();
894 goto got3;
895
896 case 'M':
897#ifdef DJGPP
898 myflush();
899#endif
900 mydelline();
901got3: s += 3;
902 len -= 2;
903 continue;
904
905 case '0':
906 case '1':
907 case '2':
908 case '3':
909 case '4':
910 case '5':
911 case '6':
912 case '7':
913 case '8':
914 case '9': p = s + 2;
915 row = mygetdigits(&p); /* no check for length! */
916 if (p > s + len)
917 break;
918 if (*p == ';')
919 {
920 ++p;
921 col = mygetdigits(&p); /* no check for length! */
922 if (p > s + len)
923 break;
924 if (*p == 'H' || *p == 'r' || *p == 'V')
925 {
926#ifdef DJGPP
927 myflush();
928#endif
929 if (*p == 'H') /* set cursor position */
930 mygotoxy(col, row);
931 else if (*p == 'V')
932 mywindow(row, S_iTop, col, S_iBottom);
933 else /* set scroll region */
934 mywindow(S_iLeft, row, S_iRight, col);
935 len -= p - s;
936 s = p + 1;
937 continue;
938 }
939 }
940 else if (*p == 'm' || *p == 'f' || *p == 'b')
941 {
942 if (*p == 'm') /* set color */
943 {
944 if (row == 0)
945 mynormvideo();/* reset color */
946 else
947 mytextattr(row);
948 }
949 else if (*p == 'f') /* set foreground color */
950 mytextcolor(row);
951 else /* set background color */
952 mytextbackground(row);
953
954 len -= p - s;
955 s = p + 1;
956 continue;
957 }
958 }
959 }
960 myputch(*s++);
961 }
962 else
963 {
964 write(1, s, (unsigned)len);
965 }
966}
967
968/*
969 * mch_inchar(): low level input funcion.
970 * Get a characters from the keyboard.
971 * If time == 0 do not wait for characters.
972 * If time == n wait a short time for characters.
973 * If time == -1 wait forever for characters.
974 *
975 * return the number of characters obtained
976 */
977 int
978mch_inchar(
979 char_u *buf,
980 int maxlen,
981 long time,
982 int tb_change_cnt)
983{
984 int len = 0;
985 int c;
986 int tmp_c;
987 static int nextchar = 0; /* may keep character when maxlen == 1 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000988
989 /*
990 * if we got a ctrl-C when we were busy, there will be a "^C" somewhere
991 * on the sceen, so we need to redisplay it.
992 */
993 if (delayed_redraw)
994 {
995 delayed_redraw = FALSE;
996 update_screen(CLEAR);
997 setcursor();
998 out_flush();
999 }
1000
1001 /* return remaining character from last call */
1002 if (nextchar)
1003 {
1004 *buf = nextchar;
1005 nextchar = 0;
1006 return 1;
1007 }
1008
1009#ifdef FEAT_MOUSE
1010 if (time != 0)
1011 show_mouse(TRUE);
1012#endif
1013#ifdef DJGPP
1014 set_sys_cursor();
1015#endif
1016 if (time >= 0)
1017 {
1018 if (WaitForChar(time) == 0) /* no character available */
1019 {
1020#ifdef FEAT_MOUSE
1021 show_mouse(FALSE);
1022#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001023 return 0;
1024 }
1025 }
1026 else /* time == -1 */
1027 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001028 /*
1029 * If there is no character available within 2 seconds (default)
Bram Moolenaar916b7af2005-03-16 09:52:38 +00001030 * write the autoscript file to disk. Or cause the CursorHold event
1031 * to be triggered.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001032 */
Bram Moolenaar916b7af2005-03-16 09:52:38 +00001033 if (WaitForChar(p_ut) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001034 {
1035#ifdef FEAT_AUTOCMD
Bram Moolenaare3226be2005-12-18 22:10:00 +00001036 if (trigger_cursorhold() && maxlen >= 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001037 {
Bram Moolenaar916b7af2005-03-16 09:52:38 +00001038 buf[0] = K_SPECIAL;
1039 buf[1] = KS_EXTRA;
1040 buf[2] = (int)KE_CURSORHOLD;
1041 return 3;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001042 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001043#endif
Bram Moolenaar39a58ca2005-06-27 22:42:44 +00001044 before_blocking();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001045 }
1046 }
1047 WaitForChar(FOREVER); /* wait for key or mouse click */
1048
1049/*
1050 * Try to read as many characters as there are, until the buffer is full.
1051 */
1052 /*
1053 * we will get at least one key. Get more if they are available
1054 * After a ctrl-break we have to read a 0 (!) from the buffer.
1055 * bioskey(1) will return 0 if no key is available and when a
1056 * ctrl-break was typed. When ctrl-break is hit, this does not always
1057 * implies a key hit.
1058 */
1059 cbrk_pressed = FALSE;
1060#ifdef FEAT_MOUSE
1061 if (mouse_click >= 0 && maxlen >= 5)
1062 {
1063 len = 5;
1064 *buf++ = ESC + 128;
1065 *buf++ = 'M';
1066 *buf++ = mouse_click;
1067 *buf++ = mouse_x + '!';
1068 *buf++ = mouse_y + '!';
1069 mouse_click = -1;
1070 }
1071 else
1072#endif
1073 {
1074#ifdef FEAT_MOUSE
1075 mouse_hidden = TRUE;
1076#endif
1077 if (p_biosk && !p_consk)
1078 {
1079 while ((len == 0 || bioskey(bioskey_ready)) && len < maxlen)
1080 {
1081 c = translate_altkeys(bioskey(bioskey_read)); /* get the key */
1082 /*
1083 * translate a few things for inchar():
1084 * 0x0000 == CTRL-break -> 3 (CTRL-C)
1085 * 0x0300 == CTRL-@ -> NUL
1086 * 0xnn00 == extended key code -> K_NUL, nn
1087 * 0xnne0 == enhanced keyboard -> K_NUL, nn
1088 * K_NUL -> K_NUL, 3
1089 */
1090 if (c == 0)
1091 c = 3;
1092 else if (c == 0x0300)
1093 c = NUL;
1094 else if ((c & 0xff) == 0
1095 || c == K_NUL
1096 || c == 0x4e2b
1097 || c == 0x4a2d
1098 || c == 0x372a
1099 || ((c & 0xff) == 0xe0 && c != 0xe0))
1100 {
1101 if (c == K_NUL)
1102 c = 3;
1103 else
1104 c >>= 8;
1105 *buf++ = K_NUL;
1106 ++len;
1107 }
1108
1109 if (len < maxlen)
1110 {
1111 *buf++ = c;
1112 len++;
1113#ifdef FEAT_MBYTE
1114 /* Convert from 'termencoding' to 'encoding'. Only
1115 * translate normal characters, not key codes. */
1116 if (input_conv.vc_type != CONV_NONE
1117 && (len == 1 || buf[-2] != K_NUL))
1118 len += convert_input(buf - 1, 1, maxlen - len + 1) - 1;
1119#endif
1120 }
1121 else
1122 nextchar = c;
1123 }
1124 }
1125 else
1126 {
1127 while ((len == 0 || (p_consk ? cons_kbhit() : kbhit()))
1128 && len < maxlen)
1129 {
1130 switch (c = (p_consk ? cons_getch() : getch()))
1131 {
1132 case 0:
1133 /* NUL means that there is another character.
1134 * Get it immediately, because kbhit() doesn't always
1135 * return TRUE for the second character.
1136 */
1137 if (p_consk)
1138 c = cons_getch();
1139 else
1140 c = getch();
1141 tmp_c = translate_altkeys(c << 8);
1142 if (tmp_c == (c << 8))
1143 {
1144 *buf++ = K_NUL;
1145 ++len;
1146 }
1147 else
1148 c = tmp_c;
1149 break;
1150 case K_NUL:
1151 *buf++ = K_NUL;
1152 ++len;
1153 c = 3;
1154 break;
1155 case 3:
1156 cbrk_pressed = TRUE;
1157 /*FALLTHROUGH*/
1158 default:
1159 break;
1160 }
1161 if (len < maxlen)
1162 {
1163 *buf++ = c;
1164 ++len;
1165 }
1166 else
1167 nextchar = c;
1168 }
1169 }
1170 }
1171#ifdef FEAT_MOUSE
1172 show_mouse(FALSE);
1173#endif
1174
1175 beep_count = 0; /* may beep again now that we got some chars */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001176 return len;
1177}
1178
1179/*
1180 * return non-zero if a character is available
1181 */
1182 int
1183mch_char_avail(void)
1184{
1185 return WaitForChar(0L);
1186}
1187
1188#ifdef DJGPP
1189# define INT_ARG int
1190#else
1191# define INT_ARG
1192#endif
1193
1194/*
1195 * function for ctrl-break interrupt
1196 */
1197 static void interrupt
1198#ifdef DJGPP
1199catch_cbrk(int a)
1200#else
1201catch_cbrk(void)
1202#endif
1203{
1204 cbrk_pressed = TRUE;
1205 ctrlc_pressed = TRUE;
1206}
1207
1208#ifndef DJGPP
1209/*
1210 * ctrl-break handler for DOS. Never called when a ctrl-break is typed, because
1211 * we catch interrupt 1b. If you type ctrl-C while Vim is waiting for a
1212 * character this function is not called. When a ctrl-C is typed while Vim is
1213 * busy this function may be called. By that time a ^C has been displayed on
1214 * the screen, so we have to redisplay the screen. We can't do that here,
1215 * because we may be called by DOS. The redraw is in mch_inchar().
1216 */
1217 static int _cdecl
1218cbrk_handler(void)
1219{
1220 delayed_redraw = TRUE;
1221 return 1; /* resume operation after ctrl-break */
1222}
1223
1224/*
1225 * function for critical error interrupt
1226 * For DOS 1 and 2 return 0 (Ignore).
1227 * For DOS 3 and later return 3 (Fail)
1228 */
1229 static void interrupt
1230catch_cint(bp, di, si, ds, es, dx, cx, bx, ax)
1231 unsigned bp, di, si, ds, es, dx, cx, bx, ax;
1232{
1233 ax = (ax & 0xff00); /* set AL to 0 */
1234 if (_osmajor >= 3)
1235 ax |= 3; /* set AL to 3 */
1236}
1237#endif
1238
1239/*
1240 * Set the interrupt vectors for use with Vim on or off.
1241 * on == TRUE means as used within Vim
1242 */
1243 static void
1244set_interrupts(int on)
1245{
1246 static int saved_cbrk;
1247#ifndef DJGPP
1248 static void interrupt (*old_cint)();
1249#endif
1250 static void interrupt (*old_cbrk)(INT_ARG);
1251
1252 if (on)
1253 {
1254 saved_cbrk = getcbrk(); /* save old ctrl-break setting */
1255 setcbrk(0); /* do not check for ctrl-break */
1256#ifdef DJGPP
1257 old_cbrk = signal(SIGINT, catch_cbrk); /* critical error interrupt */
1258#else
1259 old_cint = getvect(0x24); /* save old critical error interrupt */
1260 setvect(0x24, catch_cint); /* install our critical error interrupt */
1261 old_cbrk = getvect(0x1B); /* save old ctrl-break interrupt */
1262 setvect(0x1B, catch_cbrk); /* install our ctrl-break interrupt */
1263 ctrlbrk(cbrk_handler); /* vim's ctrl-break handler */
1264#endif
1265 if (term_console)
1266 out_str(T_ME); /* set colors */
1267 }
1268 else
1269 {
1270 setcbrk(saved_cbrk); /* restore ctrl-break setting */
1271#ifdef DJGPP
1272 signal(SIGINT,old_cbrk); /* critical error interrupt */
1273#else
1274 setvect(0x24, old_cint); /* restore critical error interrupt */
1275 setvect(0x1B, old_cbrk); /* restore ctrl-break interrupt */
1276#endif
1277 /* restore ctrl-break handler, how ??? */
1278 if (term_console)
1279 mynormvideo(); /* restore screen colors */
1280 }
1281}
1282
1283/*
1284 * We have no job control, fake it by starting a new shell.
1285 */
1286 void
1287mch_suspend(void)
1288{
1289 suspend_shell();
1290}
1291
1292extern int _fmode;
1293
1294/*
1295 * Prepare window for use by Vim.
1296 */
1297 void
1298mch_init(void)
1299{
1300 union REGS regs;
1301
1302#if defined(DJGPP) && defined(FEAT_CLIPBOARD)
1303 __dpmi_regs dpmi_regs;
1304#endif
1305
1306 /*
1307 * Get the video attributes at the cursor. These will be used as the
1308 * default attributes.
1309 */
1310 regs.h.ah = 0x08;
1311 regs.h.bh = 0x00; /* video page 0 */
1312 int86(0x10, &regs, &regs);
1313 orig_attr = regs.h.ah;
1314 mynormvideo();
1315 if (cterm_normal_fg_color == 0)
1316 cterm_normal_fg_color = (orig_attr & 0xf) + 1;
1317 if (cterm_normal_bg_color == 0)
1318 cterm_normal_bg_color = ((orig_attr >> 4) & 0xf) + 1;
1319
1320 term_console = TRUE; /* assume using the console for the things here */
1321 _fmode = O_BINARY; /* we do our own CR-LF translation */
1322 out_flush();
1323 set_interrupts(TRUE); /* catch interrupts */
1324
1325#ifdef DJGPP
1326 /*
1327 * Use Long File Names by default, if $LFN not set.
1328 */
1329 if (getenv("LFN") == NULL)
1330 putenv("LFN=y");
1331
1332 get_screenbase();
1333#endif
1334
1335#ifdef FEAT_MOUSE
1336/* find out if a MS compatible mouse is available */
1337 regs.x.ax = 0;
1338 (void)int86(0x33, &regs, &regs);
1339 mouse_avail = regs.x.ax;
1340 /* best guess for mouse coordinate computations */
1341 mch_get_shellsize();
1342 if (Columns <= 40)
1343 mouse_x_div = 16;
1344 if (Rows == 30)
1345 mouse_y_div = 16;
1346#endif
1347
1348 /*
1349 * Try switching to 16 colors for background, instead of 8 colors and
1350 * blinking. Does this always work? Can the old value be restored?
1351 */
1352 regs.x.ax = 0x1003;
1353 regs.h.bl = 0x00;
1354 regs.h.bh = 0x00;
1355 int86(0x10, &regs, &regs);
1356
1357 /*
1358 * Test if we have an enhanced AT keyboard. Write 0xFFFF to the keyboard
1359 * buffer and try to read it back. If we can't in 16 tries, it's an old
1360 * type XT keyboard.
1361 */
1362 regs.h.ah = 0x05;
1363 regs.x.cx = 0xffff;
1364 int86(0x16, &regs, &regs);
1365 if (regs.h.al != 1) /* skip this when keyboard buffer is full */
1366 {
1367 int i;
1368
1369 for (i = 0; i < 16; ++i)
1370 {
1371 regs.h.ah = 0x10;
1372 int86(0x16, &regs, &regs);
1373 if (regs.x.ax == 0xffff)
1374 break;
1375 }
1376 if (i == 16) /* 0xffff not read, must be old keyboard */
1377 {
1378 bioskey_read = 0;
1379 bioskey_ready = 1;
1380 }
1381 }
1382
1383#ifdef MCH_CURSOR_SHAPE
1384 /* Save the old cursor shape */
1385 mch_restore_cursor_shape(FALSE);
1386 /* Initialise the cursor shape */
1387 mch_update_cursor();
1388#endif
1389
1390#if defined(DJGPP) && defined(FEAT_CLIPBOARD)
1391 /*
1392 * Check to see if the Windows clipboard is available, ie. are we
1393 * running from a DOS session within Windows. Obviously, the Windows
1394 * clipboard will not be available if we're running under pure DOS.
1395 *
1396 * int 0x2f, AX = 0x1700 identifies the Windows version we're running
1397 * under. Upon return from the interrupt, if AX is unchanged, we're
1398 * running under pure DOS and no Windows clipboard is available.
1399 *
1400 * Remark: could use int86() here but __dpmi_int() is recommended in
1401 * the DJGPP docs, since int86() doesn't cover all available interrupts.
1402 */
1403 dpmi_regs.x.ax = 0x1700;
1404 if (__dpmi_int(0x2f, &dpmi_regs) == -1)
1405 /* real-mode interrupt failed? */
1406 dpmi_regs.x.ax = 0x1700; /* force failure */
1407
1408 if (dpmi_regs.x.ax == 0x1700) /* no change in AX? */
1409 clip_init(FALSE); /* no clipboard available, too bad */
1410 else /* else, running under Windows, OK */
1411 clip_init(TRUE); /* clipboard is available */
1412#endif
1413}
1414
1415 int
1416mch_check_win(
1417 int argc,
1418 char **argv)
1419{
1420 /* store argv[0], may be used for $VIM */
1421 if (*argv[0] != NUL)
1422 exe_name = FullName_save((char_u *)argv[0], FALSE);
1423
1424 /*
1425 * Try the DOS search path. The executable may in
1426 * fact be called differently, so try this last.
1427 */
1428 if (exe_name == NULL || *exe_name == NUL)
1429 exe_name = searchpath("vim.exe");
1430
1431 if (isatty(1))
1432 return OK;
1433 return FAIL;
1434}
1435
1436/*
1437 * Return TRUE if the input comes from a terminal, FALSE otherwise.
1438 */
1439 int
1440mch_input_isatty(void)
1441{
1442 if (isatty(read_cmd_fd))
1443 return TRUE;
1444 return FALSE;
1445}
1446
1447#if defined(USE_FNAME_CASE) || defined(PROTO)
1448/*
1449 * fname_case(): Set the case of the file name, if it already exists.
1450 * TODO: should expand short to long file names. Need to use DOS interrupts,
1451 * see DJGPP sources libc/dos/dir/findfirs.c.
1452 */
1453 void
1454fname_case(char_u *name, int len)
1455{
1456 char_u *tail;
1457 struct ffblk fb;
1458
1459 slash_adjust(name);
1460 if (findfirst(name, &fb, 0) == 0)
1461 {
1462 tail = gettail(name);
1463 if (len == 0 ? STRLEN(tail) == STRLEN(fb.ff_name)
1464 : (tail - name) + STRLEN(fb.ff_name) < len)
1465 STRCPY(tail, fb.ff_name);
1466 }
1467}
1468#endif
1469
1470/*
1471 * return process ID
1472 */
1473 long
1474mch_get_pid(void)
1475{
1476 return (long)0;
1477}
1478
1479/*
1480 * Change default drive (just like _chdrive of Borland C 3.1)
1481 */
1482 static int
1483change_drive(int drive)
1484{
1485 union REGS regs;
1486
1487 regs.h.ah = 0x0e;
1488 regs.h.dl = drive - 1;
1489 intdos(&regs, &regs); /* set default drive */
1490 regs.h.ah = 0x19;
1491 intdos(&regs, &regs); /* get default drive */
1492 if (regs.h.al == drive - 1)
1493 return 0;
1494 return -1;
1495}
1496
1497/*
1498 * Get absolute file name into buffer 'buf' of length 'len' bytes.
1499 * All slashes are replaced with backslashes, to avoid trouble when comparing
1500 * file names. When 'shellslash' set do it the other way around.
1501 *
1502 * return FAIL for failure, OK otherwise
1503 */
1504 int
1505mch_FullName(
1506 char_u *fname,
1507 char_u *buf,
1508 int len,
1509 int force)
1510{
1511 if (!force && mch_isFullName(fname)) /* already expanded */
1512 {
Bram Moolenaarbbebc852005-07-18 21:47:53 +00001513 vim_strncpy(buf, fname, len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001514 slash_adjust(buf);
1515 return OK;
1516 }
1517
1518#ifdef __BORLANDC__ /* Only Borland C++ has this */
1519 if (_fullpath((char *)buf, (char *)fname, len - 1) == NULL)
1520 return FAIL;
1521 return OK;
1522#else /* almost the same as mch_FullName() in os_unix.c */
1523 {
1524# if 1
1525 char_u fullpath[MAXPATHL];
1526
1527 if (!_truename(fname, fullpath))
1528 return FAIL;
1529 slash_adjust(fullpath); /* Only needed when 'shellslash' set */
Bram Moolenaarbbebc852005-07-18 21:47:53 +00001530 vim_strncpy(buf, fullpath, len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001531 return OK;
1532
1533# else /* Old code, to be deleted... */
1534 int l;
1535 char_u olddir[MAXPATHL];
1536 char_u *p, *q;
1537 int c;
1538 int retval = OK;
1539
1540 *buf = 0;
1541 /*
1542 * change to the directory for a moment,
1543 * and then do the getwd() (and get back to where we were).
1544 * This will get the correct path name with "../" things.
1545 */
1546 p = vim_strrchr(fname, '/');
1547 q = vim_strrchr(fname, '\\');
1548 if (q != NULL && (p == NULL || q > p))
1549 p = q;
1550 q = vim_strrchr(fname, ':');
1551 if (q != NULL && (p == NULL || q > p))
1552 p = q;
1553 if (p != NULL)
1554 {
1555 if (getcwd(olddir, MAXPATHL) == NULL)
1556 {
1557 p = NULL; /* can't get current dir: don't chdir */
1558 retval = FAIL;
1559 }
1560 else
1561 {
1562 if (p == fname) /* /fname */
1563 q = p + 1; /* -> / */
1564 else if (q + 1 == p) /* ... c:\foo */
1565 q = p + 1; /* -> c:\ */
1566 else /* but c:\foo\bar */
1567 q = p; /* -> c:\foo */
1568
1569 c = *q; /* truncate at start of fname */
1570 *q = NUL;
1571# ifdef DJGPP
1572 STRCPY(buf, fname);
1573 slash_adjust(buf); /* needed when fname starts with \ */
1574 if (mch_chdir(buf)) /* change to the directory */
1575# else
1576 if (mch_chdir(fname)) /* change to the directory */
1577# endif
1578 retval = FAIL;
1579 else
1580 {
1581 fname = q;
1582 if (c == psepc) /* if we cut the name at a */
1583 fname++; /* '\', don't add it again */
1584 }
1585 *q = c;
1586 }
1587 }
1588 if (getcwd(buf, len) == NULL)
1589 {
1590 retval = FAIL;
1591 *buf = NUL;
1592 }
1593# ifdef USE_FNAME_CASE
1594 else
1595 {
1596 char_u *head;
1597 char_u *tail;
1598 struct ffblk fb;
1599 int c;
1600 int added;
1601
1602 /* Apparently "longna~1" isn't expanded by getcwd(), at least not
1603 * for DJGPP. Expand it here. Have to do each dirname
1604 * separately. */
1605 slash_adjust(buf);
1606 head = buf;
1607 if (isalpha(*head) && head[1] == ':')
1608 head += 2; /* skip "c:" */
1609 while (*head != NUL)
1610 {
1611 /* Advance "head" to the start of a dirname and "tail" to just
1612 * after it. */
1613 while (*head == '/' || *head == '\\')
1614 ++head;
1615 for (tail = head; *tail != NUL; ++tail)
1616 if (*tail == '/' || *tail == '\\')
1617 break;
1618 c = *tail;
1619 *tail = NUL;
1620
1621 if (findfirst(buf, &fb, FA_DIREC) == 0)
1622 {
1623 added = STRLEN(fb.ff_name);
1624 if ((head - buf) + added + STRLEN(tail + 1) + 2 < len)
1625 {
1626 added -= (tail - head);
1627 if (added != 0)
Bram Moolenaar446cb832008-06-24 21:56:24 +00001628 STRMOVE(tail + 1 + added, tail + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001629 STRCPY(head, fb.ff_name);
1630 tail += added;
1631 }
1632 }
1633 *tail = c;
1634 head = tail;
1635 }
1636 }
1637# endif
1638 if (p != NULL)
1639 mch_chdir(olddir);
1640 /*
1641 * Concatenate the file name to the path.
1642 */
1643 if (*fname != NUL)
1644 {
1645 l = STRLEN(buf);
1646 if (l > 0 && buf[l - 1] != '/' && buf[l - 1] != '\\')
1647 strcat(buf, pseps);
1648 strcat(buf, fname);
1649 }
1650 return retval;
1651# endif
1652 }
1653#endif
1654}
1655
1656/*
1657 * Replace all slashes by backslashes.
1658 * This used to be the other way around, but MS-DOS sometimes has problems
1659 * with slashes (e.g. in a command name). We can't have mixed slashes and
1660 * backslashes, because comparing file names will not work correctly. The
1661 * commands that use a file name should try to avoid the need to type a
1662 * backslash twice.
1663 * When 'shellslash' set do it the other way around.
1664 */
1665 void
1666slash_adjust(char_u *p)
1667{
1668#ifdef OLD_DJGPP /* this seems to have been fixed in DJGPP 2.01 */
1669 /* DJGPP can't handle a file name that starts with a backslash, and when it
1670 * starts with a slash there should be no backslashes */
1671 if (*p == '\\' || *p == '/')
1672 while (*p)
1673 {
1674 if (*p == '\\')
1675 *p = '/';
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001676 mb_ptr_adv(p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001677 }
1678 else
1679#endif
1680 while (*p)
1681 {
1682 if (*p == psepcN)
1683 *p = psepc;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001684 mb_ptr_adv(p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001685 }
1686}
1687
1688/*
1689 * Return TRUE if "fname" does not depend on the current directory.
1690 */
1691 int
1692mch_isFullName(char_u *fname)
1693{
1694 /* A name like "d:/foo" and "//server/share" is absolute */
1695 return (fname[0] != NUL && fname[1] == ':'
1696 && (fname[2] == '/' || fname[2] == '\\'))
1697 || (fname[0] == fname[1] && (fname[0] == '/' || fname[0] == '\\'));
1698}
1699
1700
1701 void
1702mch_early_init(void)
1703{
1704}
1705
1706/*
1707 * Careful: mch_exit() may be called before mch_init()!
1708 */
1709 void
1710mch_exit(int r)
1711{
1712 settmode(TMODE_COOK);
1713 stoptermcap();
1714 set_interrupts(FALSE); /* restore interrupts */
1715#ifdef DJGPP
1716 set_sys_cursor();
1717#endif
1718 /* Somehow outputting CR-NL causes the original colors to be restored */
1719 out_char('\r');
1720 out_char('\n');
1721 out_flush();
1722 ml_close_all(TRUE); /* remove all memfiles */
1723#ifdef MCH_CURSOR_SHAPE
1724 mch_restore_cursor_shape(TRUE);
1725#endif
1726 exit(r);
1727}
1728
1729/*
1730 * set the tty in (raw) ? "raw" : "cooked" mode
1731 * Does not change the tty, as bioskey() and kbhit() work raw all the time.
1732 */
1733 void
1734mch_settmode(int tmode)
1735{
1736}
1737
1738#ifdef FEAT_MOUSE
1739 void
1740mch_setmouse(int on)
1741{
1742 mouse_active = on;
1743 mouse_hidden = TRUE; /* dont show it until moved */
1744}
1745#endif
1746
1747/*
1748 * set screen mode
1749 * return FAIL for failure, OK otherwise
1750 */
1751 int
1752mch_screenmode(char_u *arg)
1753{
1754 int mode;
1755 int i;
1756 static char *(names[]) = {"BW40", "C40", "BW80", "C80", "MONO", "C4350"};
1757 static int modes[] = { BW40, C40, BW80, C80, MONO, C4350};
1758
1759 mode = -1;
1760 if (VIM_ISDIGIT(*arg)) /* mode number given */
1761 mode = atoi((char *)arg);
1762 else
1763 {
1764 for (i = 0; i < sizeof(names) / sizeof(char_u *); ++i)
1765 if (stricmp(names[i], (char *)arg) == 0)
1766 {
1767 mode = modes[i];
1768 break;
1769 }
1770 }
1771 if (mode == -1)
1772 {
1773 EMSG("E362: Unsupported screen mode");
1774 return FAIL;
1775 }
1776 textmode(mode); /* use Borland function */
1777#ifdef DJGPP
1778 /* base address may have changed */
1779 get_screenbase();
1780#endif
1781
1782 /* Screen colors may have changed. */
1783 out_str(T_ME);
1784
1785#ifdef FEAT_MOUSE
1786 if (mode <= 1 || mode == 4 || mode == 5 || mode == 13 || mode == 0x13)
1787 mouse_x_div = 16;
1788 else
1789 mouse_x_div = 8;
1790 if (mode == 0x11 || mode == 0x12)
1791 mouse_y_div = 16;
1792 else if (mode == 0x10)
1793 mouse_y_div = 14;
1794 else
1795 mouse_y_div = 8;
1796 shell_resized();
1797#endif
1798 return OK;
1799}
1800
1801/*
1802 * Structure used by Turbo-C/Borland-C to store video parameters.
1803 */
1804#ifndef DJGPP
1805extern struct text_info _video;
1806#endif
1807
1808/*
1809 * try to get the real window size
1810 * return FAIL for failure, OK otherwise
1811 */
1812 int
1813mch_get_shellsize(void)
1814{
1815 struct text_info textinfo;
1816
1817 /*
1818 * The screenwidth is returned by the BIOS OK.
1819 * The screenheight is in a location in the bios RAM, if the display is
1820 * EGA or VGA.
1821 */
1822 if (!term_console)
1823 return FAIL;
1824 gettextinfo(&textinfo);
1825 Columns = textinfo.screenwidth;
1826 Rows = textinfo.screenheight;
1827#ifndef DJGPP
1828 if (textinfo.currmode > 10)
1829 Rows = *(char far *)MK_FP(0x40, 0x84) + 1;
1830#endif
1831
1832 if (Columns < MIN_COLUMNS || Rows < MIN_LINES)
1833 {
1834 /* these values are overwritten by termcap size or default */
1835 Columns = 80;
1836 Rows = 25;
1837 return FAIL;
1838 }
1839#ifdef DJGPP
1840 mytextinit(&textinfo); /* Added by JML, 1/15/98 */
1841#endif
1842
1843 return OK;
1844}
1845
1846/*
1847 * Set the active window for delline/insline.
1848 */
1849 static void
1850set_window(void)
1851{
1852 if (term_console)
1853 {
1854#ifndef DJGPP
1855 _video.screenheight = Rows;
1856#endif
1857 mywindow(1, 1, Columns, Rows);
1858 }
1859 screen_start();
1860}
1861
1862 void
1863mch_set_shellsize(void)
1864{
1865 /* Should try to set the window size to Rows and Columns.
1866 * May involve switching display mode....
1867 * We assume the user knows the size and just use it. */
1868}
1869
1870/*
1871 * Rows and/or Columns has changed.
1872 */
1873 void
1874mch_new_shellsize()
1875{
1876#ifdef FEAT_MOUSE
1877 /* best guess for mouse coordinate computations */
1878 if (Columns <= 40)
1879 mouse_x_div = 16;
1880 if (Rows == 30)
1881 mouse_y_div = 16;
1882#endif
1883 set_window();
1884#ifdef FEAT_MOUSE
1885 mouse_area(); /* set area where mouse can go */
1886#endif
1887}
1888
1889#if defined(DJGPP) || defined(PROTO)
1890/*
1891 * Check the number of Columns with a BIOS call. This avoids a crash of the
1892 * DOS console when 'columns' is set to a too large value.
1893 */
1894 void
1895mch_check_columns()
1896{
1897 static union REGS regs;
1898
1899 regs.h.ah = 0x0f;
1900 (void)int86(0x10, &regs, &regs);
1901 if ((unsigned)Columns > (unsigned)regs.h.ah)
1902 Columns = (unsigned)regs.h.ah;
1903}
1904#endif
1905
1906/*
1907 * call shell, return FAIL for failure, OK otherwise
1908 * options: SHELL_*, see vim.h.
1909 */
1910 int
1911mch_call_shell(
1912 char_u *cmd,
1913 int options)
1914{
1915 int x;
1916 int tmode = cur_tmode;
1917#ifndef DJGPP
1918 char_u *newcmd;
1919#endif
1920
1921 out_flush();
1922#ifdef DJGPP
1923 set_sys_cursor();
1924#endif
1925
1926 if (options & SHELL_COOKED)
1927 settmode(TMODE_COOK); /* set to normal mode */
1928 set_interrupts(FALSE); /* restore interrupts */
1929
1930#ifdef DJGPP
1931 /* ignore signals while external command is running */
1932 signal(SIGINT, SIG_IGN);
1933 signal(SIGHUP, SIG_IGN);
1934 signal(SIGQUIT, SIG_IGN);
1935 signal(SIGTERM, SIG_IGN);
1936#endif
1937 if (cmd == NULL)
1938 x = system((char *)p_sh);
1939 else
1940 {
1941#ifdef DJGPP
1942 /*
1943 * Use 'shell' for system().
1944 */
1945 setenv("SHELL", (char *)p_sh, 1);
1946 x = system(cmd);
1947#else
1948 /* we use "command" to start the shell, slow but easy */
1949 newcmd = alloc(STRLEN(p_sh) + STRLEN(p_shcf) + STRLEN(cmd) + 3);
1950 if (newcmd == NULL)
1951 x = -1;
1952 else
1953 {
1954 sprintf((char *)newcmd, "%s %s %s", p_sh, p_shcf, cmd);
1955 x = system((char *)newcmd);
1956 vim_free(newcmd);
1957 }
1958#endif
1959 }
1960#ifdef DJGPP
1961 signal(SIGINT, SIG_DFL);
1962 signal(SIGHUP, SIG_DFL);
1963 signal(SIGQUIT, SIG_DFL);
1964 signal(SIGTERM, SIG_DFL);
1965#endif
1966 if (tmode == TMODE_RAW)
1967 settmode(TMODE_RAW); /* set to raw mode */
1968 set_interrupts(TRUE); /* catch interrupts */
1969
1970 if (x && !(options & SHELL_SILENT) && !emsg_silent)
1971 {
1972 MSG_PUTS("\nshell returned ");
1973 msg_outnum((long)x);
1974 msg_putchar('\n');
1975 }
1976
1977 return x;
1978}
1979
1980/*
1981 * check for an "interrupt signal": CTRL-break or CTRL-C
1982 */
1983 void
1984mch_breakcheck(void)
1985{
1986 if (ctrlc_pressed)
1987 {
1988 ctrlc_pressed = FALSE;
1989 got_int = TRUE;
1990 }
1991}
1992
1993/*
1994 * Return TRUE if "p" contain a wildcard that can be expanded by
1995 * dos_expandpath().
1996 */
1997 int
1998mch_has_exp_wildcard(char_u *p)
1999{
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002000 for ( ; *p; mb_ptr_adv(p))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002001 {
2002 if (vim_strchr((char_u *)"?*[", *p) != NULL
2003 || (*p == '~' && p[1] != NUL))
2004 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002005 }
2006 return FALSE;
2007}
2008
2009/*
2010 * Return TRUE if "p" contain a wildcard or a "~1" kind of thing (could be a
2011 * shortened file name).
2012 */
2013 int
2014mch_has_wildcard(char_u *p)
2015{
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002016 for ( ; *p; mb_ptr_adv(p))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002017 {
2018 if (vim_strchr((char_u *)
2019# ifdef VIM_BACKTICK
2020 "?*$[`"
2021# else
2022 "?*$["
2023# endif
2024 , *p) != NULL
2025 || (*p == '~' && p[1] != NUL))
2026 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002027 }
2028 return FALSE;
2029}
2030
2031/*
2032 * Change directory to "path".
2033 * The normal chdir() does not change the default drive. This one does.
2034 * Return 0 for success, -1 for failure.
2035 */
2036 int
2037mch_chdir(char *path)
2038{
2039 if (path[0] == NUL) /* just checking... */
2040 return 0;
Bram Moolenaara2974d72009-07-14 16:38:36 +00002041 if (p_verbose >= 5)
2042 {
2043 verbose_enter();
2044 smsg((char_u *)"chdir(%s)", path);
2045 verbose_leave();
2046 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002047 if (path[1] == ':') /* has a drive name */
2048 {
2049 if (change_drive(TOLOWER_ASC(path[0]) - 'a' + 1))
2050 return -1; /* invalid drive name */
2051 path += 2;
2052 }
2053 if (*path == NUL) /* drive name only */
2054 return 0;
2055 return chdir(path); /* let the normal chdir() do the rest */
2056}
2057
2058#ifdef DJGPP
2059/*
2060 * mch_rename() works around a bug in rename (aka MoveFile) in
2061 * Windows 95: rename("foo.bar", "foo.bar~") will generate a
2062 * file whose short file name is "FOO.BAR" (its long file name will
2063 * be correct: "foo.bar~"). Because a file can be accessed by
2064 * either its SFN or its LFN, "foo.bar" has effectively been
2065 * renamed to "foo.bar", which is not at all what was wanted. This
2066 * seems to happen only when renaming files with three-character
2067 * extensions by appending a suffix that does not include ".".
2068 * Windows NT gets it right, however, with an SFN of "FOO~1.BAR".
2069 * This works like mch_rename in os_win32.c, but is a bit simpler.
2070 *
2071 * Like rename(), returns 0 upon success, non-zero upon failure.
2072 * Should probably set errno appropriately when errors occur.
2073 */
2074
2075 int
2076mch_rename(const char *OldFile, const char *NewFile)
2077{
2078 char_u *TempFile;
2079 int retval;
2080 int fd;
2081
2082 /* rename() works correctly without long file names, so use that */
2083 if (!_USE_LFN)
2084 return rename(OldFile, NewFile);
2085
2086 if ((TempFile = alloc((unsigned)(STRLEN(OldFile) + 13))) == NULL)
2087 return -1;
2088
2089 STRCPY(TempFile, OldFile);
2090 STRCPY(gettail(TempFile), "axlqwqhy.ba~");
2091 if (rename(OldFile, TempFile))
2092 retval = -1;
2093 else
2094 {
2095 /* now create an empty file called OldFile; this prevents
2096 * the operating system using OldFile as an alias (SFN)
2097 * if we're renaming within the same directory. For example,
2098 * we're editing a file called filename.asc.txt by its SFN,
2099 * filena~1.txt. If we rename filena~1.txt to filena~1.txt~
2100 * (i.e., we're making a backup while writing it), the SFN
2101 * for filena~1.txt~ will be filena~1.txt, by default, which
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002102 * will cause all sorts of problems later in buf_write(). So, we
Bram Moolenaar071d4272004-06-13 20:20:40 +00002103 * create an empty file called filena~1.txt and the system will have
2104 * to find some other SFN for filena~1.txt~, such as filena~2.txt
2105 */
2106 if ((fd = open(OldFile, O_RDWR|O_CREAT|O_EXCL, 0444)) < 0)
2107 return -1;
2108 retval = rename(TempFile, NewFile);
2109 close(fd);
2110 mch_remove((char_u *)OldFile);
2111
2112 /* If renaming to NewFile failed, rename TempFile back to OldFile, so
2113 * that it looks like nothing happened. */
2114 if (retval)
2115 rename(TempFile, OldFile);
2116 }
2117 vim_free(TempFile);
2118
2119 return retval; /* success */
2120}
2121#endif
2122
2123#if defined(DJGPP) || defined(PROTO)
2124/*
2125 * setlocale() for DJGPP with MS-DOS codepage support
2126 * Author: Cyril Slobin <slobin@fe.msk.ru>
2127 *
2128 * Scaled down a lot for use by Vim: Only support setlocale(LC_ALL, "").
2129 */
2130
2131#undef setlocale
2132
2133#include <go32.h>
2134#include <inlines/ctype.ha>
2135#include <locale.h>
2136
2137#define UPCASE (__dj_ISALNUM | __dj_ISALPHA | __dj_ISGRAPH | __dj_ISPRINT | __dj_ISUPPER)
2138#define LOCASE (__dj_ISALNUM | __dj_ISALPHA | __dj_ISGRAPH | __dj_ISPRINT | __dj_ISLOWER)
2139
2140 char *
2141djgpp_setlocale(void)
2142{
2143 __dpmi_regs regs;
2144 struct { char id; unsigned short off, seg; } __attribute__ ((packed)) info;
2145 unsigned char buffer[0x82], lower, upper;
2146 int i;
2147
2148 regs.x.ax = 0x6502;
2149 regs.x.bx = 0xffff;
2150 regs.x.dx = 0xffff;
2151 regs.x.cx = 5;
2152 regs.x.es = __tb >> 4;
2153 regs.x.di = __tb & 0xf;
2154
2155 __dpmi_int(0x21, &regs);
2156
2157 if (regs.x.flags & 1)
2158 return NULL;
2159
2160 dosmemget(__tb, 5, &info);
2161 dosmemget((info.seg << 4) + info.off, 0x82, buffer);
2162
2163 if (*(short *)buffer != 0x80)
2164 return NULL;
2165
2166 /* Fix problem of underscores being replaced with y-umlaut. (Levin) */
2167 if (buffer[26] == 0x5f)
2168 buffer[26] = 0x98;
2169
2170 for (i = 0; i < 0x80; i++)
2171 {
2172 lower = i + 0x80;
2173 upper = (buffer+2)[i];
2174 if (lower != upper)
2175 {
2176 __dj_ctype_flags[lower+1] = LOCASE;
2177 __dj_ctype_toupper[lower+1] = upper;
2178 if (__dj_ctype_flags[upper+1] == 0)
2179 __dj_ctype_flags[upper+1] = UPCASE;
2180 if (__dj_ctype_tolower[upper+1] == upper)
2181 __dj_ctype_tolower[upper+1] = lower;
2182 }
2183 }
2184
2185 return "C";
2186}
2187
2188#if defined(FEAT_CLIPBOARD) || defined(PROTO)
2189
2190/*
2191 * Clipboard stuff, for cutting and pasting text to other windows.
2192 *
2193 * Implementation of DOS/Windows clipboard data transfer
2194 * by David Kotchan (dkotchan@sympatico.ca)
2195 */
2196
2197#define CF_TEXT 0x01 /* Windows clipboard format: Windows (ANSI) text */
2198#define CF_OEMTEXT 0x07 /* Windows clipboard format: OEM (DOS) text */
2199#define CF_VIMCLIP 0x04 /* trick: SYLK clipboard format for VimClipboard */
2200
2201static int Win16OpenClipboard(void);
2202static int Win16CloseClipboard(void);
2203static int Win16EmptyClipboard(void);
2204static char_u *Win16GetClipboardData(int clip_data_format);
2205static int Win16SetClipboardData(int clip_data_format, char_u *clip_data, int clip_data_size, int clip_data_type);
2206
2207/*
2208 * Make vim the owner of the current selection. Return OK upon success.
2209 */
2210 int
2211clip_mch_own_selection(VimClipboard *cbd)
2212{
2213 /*
2214 * Never actually own the clipboard. If another application sets the
2215 * clipboard, we don't want to think that we still own it.
2216 */
2217 return FAIL;
2218}
2219
2220/*
2221 * Make vim NOT the owner of the current selection.
2222 */
2223 void
2224clip_mch_lose_selection(VimClipboard *cbd)
2225{
2226 /* Nothing needs to be done here */
2227}
2228
2229/*
2230 * Read the Windows clipboard text and put it in Vim's clipboard register.
2231 */
2232 void
2233clip_mch_request_selection(VimClipboard *cbd)
2234{
Bram Moolenaard44347f2011-06-19 01:14:29 +02002235 int type = MAUTO;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002236 char_u *pAllocated = NULL;
2237 char_u *pClipText = NULL;
2238 int clip_data_format = 0;
2239
2240 if (Win16OpenClipboard())
2241 {
2242 /* Check for Vim's own clipboard format first. The CF_VIMCLIP format
2243 * is just ordinary text (like CF_TEXT) except prepended by the
2244 * selection type (as a single character). Note that under DOS we
2245 * actually cannot define a custom CF_VIMCLIP clipboard format; we
2246 * use instead one of the existing Windows-defined formats, usually
2247 * "DIF" or "SYLK". See Win16GetClipboardData() for details.
2248 *
2249 * Note that Win16GetClipboardData() returns the address of the memory
2250 * block it allocated. This is not necessary the start of the
2251 * clipboard text data: there may be other bytes ahead of the
2252 * text (particularly for CF_VIMCLIP) which are used for data
2253 * management. So pClipText is not necessarily == pAllocated.
2254 */
2255
2256 if ((pAllocated = Win16GetClipboardData(CF_VIMCLIP)) != NULL)
2257 {
2258 clip_data_format = CF_VIMCLIP;
2259 pClipText = pAllocated;
2260
2261 switch (*pClipText++) /* after ++, pClipText points to text */
2262 {
2263 default:
2264 case 'L': type = MLINE; break;
2265 case 'C': type = MCHAR; break;
2266#ifdef FEAT_VISUAL
2267 case 'B': type = MBLOCK; break;
2268#endif
2269 }
2270 }
2271
2272 /* Otherwise, check for the normal Windows text formats. There are
2273 * two of these: CF_TEXT (common) and CF_OEMTEXT (used for DOS
2274 * compatibility). Experiments show that, under the DOS/Windows
2275 * clipboard interface, writing CF_TEXT data to the clipboard
2276 * automatically creates a CF_OEMTEXT format as well.
2277 */
2278
2279 else if ((pAllocated = Win16GetClipboardData(CF_TEXT)) != NULL)
2280 {
2281 clip_data_format = CF_TEXT;
2282 pClipText = pAllocated;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002283 }
2284
2285 else if ((pAllocated = Win16GetClipboardData(CF_OEMTEXT)) != NULL)
2286 {
2287 clip_data_format = CF_OEMTEXT;
2288 pClipText = pAllocated;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002289 }
2290
2291 /* Did we get anything? */
2292
2293 if (pClipText != NULL)
2294 {
2295 char_u *pDest;
2296 char_u *pStart;
2297 char_u *pEnd;
2298
2299 long_u clip_data_size = 0;
2300
2301 /* The Windows clipboard normally stores its text lines terminated
2302 * by <CR><NL>. But Vim uses only <NL>, so translate the <CR><NL>
2303 * into <NL>. Also, watch for possible null bytes at the end of
2304 * pClipText. These are padding added by "get_clipboard_data"
2305 * (int 0x2f, AX= 0x1705) in order to round the data size up to the
2306 * next multiple of 32 bytes. See Win16GetClipboardData() for
2307 * details.
2308 */
2309
2310 pDest = strstr( pClipText, "\r\n" ); /* find first <CR><NL> */
2311
2312 if (pDest != NULL) /* found one? */
2313 {
2314 pStart = pDest + 1; /* points to <NL> after <CR> */
2315 pEnd = strstr( pStart, "\r\n" );/* find next <CR><NL> */
2316
2317 while (pEnd != NULL) /* found one? */
2318 {
2319 memmove(pDest, pStart, (long)(pEnd - pStart));
2320 /* exclude <CR> */
2321 pDest += (long)(pEnd - pStart); /* new destination */
2322 pStart = pEnd + 1; /* new starting point */
2323 pEnd = strstr(pStart, "\r\n"); /* find next <CR><NL> */
2324 }
2325
2326 /* Fell out of while() loop: no more <CR><NL> pairs. Just copy
2327 * the rest of the data, up to the first null byte. */
2328 pEnd = strchr(pStart, '\0'); /* find first null */
2329
2330 memmove(pDest, pStart, (long)(pEnd - pStart)); /* exclude nul */
2331 pDest += (long)(pEnd - pStart);
2332 *pDest = '\0'; /* terminate */
2333
2334 /* Now that all <CR><NL> pairs have been "compressed" into just
2335 * <NL>'s, determine the true text length. */
2336 clip_data_size = (long_u)(pDest - pClipText);
2337 }
2338 else
2339 {
2340 /* no <CR><NL> pairs at all */
2341 /* Since the data may have been padded with trailing nulls,
2342 * determine the true string length. */
2343 clip_data_size = STRLEN(pClipText); /* true data length */
2344 }
2345
2346 /* Copy the cleaned-up data over to Vim's clipboard "*" register. */
2347 clip_yank_selection(type, pClipText, clip_data_size, cbd);
2348
2349 /* Free the memory that Win16GetClipboardData() allocated. */
2350 vim_free(pAllocated);
2351 }
2352
2353 Win16CloseClipboard();
2354
2355 } // end if (Win16OpenClipboard())
2356}
2357
2358/*
2359 * Send the currently selected Vim text to the Windows clipboard.
2360 */
2361 void
2362clip_mch_set_selection( VimClipboard *cbd )
2363{
2364 char_u *pClipData = NULL;
2365 long_u clip_data_size;
2366 int clip_data_type;
2367
2368 /* If the '*' register isn't already filled in, fill it in now. */
2369 cbd->owned = TRUE;
2370 clip_get_selection(cbd);
2371 cbd->owned = FALSE;
2372
2373 /*
2374 * clip_convert_selection() returns a pointer to a buffer containing
2375 * the text to send to the Windows clipboard, together with a count
2376 * of the number of characters (bytes) in the buffer. The function's
2377 * return value is the 'type' of selection: MLINE, MCHAR, or MBLOCK;
2378 * or -1 for failure.
2379 */
2380 clip_data_type = clip_convert_selection(&pClipData, &clip_data_size, cbd);
2381
2382 if (clip_data_type < 0) /* could not convert? */
2383 return; /* early exit */
2384
2385 if (Win16OpenClipboard())
2386 {
2387 if (Win16EmptyClipboard())
2388 {
2389 int sentOK;
2390
2391 sentOK = Win16SetClipboardData(CF_TEXT, pClipData,
2392 clip_data_size, clip_data_type);
2393 sentOK = Win16SetClipboardData(CF_VIMCLIP,
2394 pClipData, clip_data_size, clip_data_type) && sentOK;
2395
2396 if (!sentOK)
2397 {
2398 /* one or both of Win16SetClipboardData() failed. */
2399 /* Technically we don't know why Win16SetClipboardData()
2400 * failed, but almost always it will be because there wasn't
Bram Moolenaar035db9f2007-05-10 18:02:27 +00002401 * enough DOS memory to buffer the data, so report that as the
Bram Moolenaar071d4272004-06-13 20:20:40 +00002402 * problem.
2403 *
2404 * We report the error here (instead of in
2405 * Win16SetClipboardData()) because we don't want the error
2406 * reported twice.
2407 */
2408 EMSG("E450: Selection too large, cannot allocate DOS buffer");
2409 }
2410 }
2411
2412 Win16CloseClipboard();
2413 }
2414
2415 /* release memory allocated by clip_convert_selection() */
2416 vim_free(pClipData);
2417
2418 return;
2419}
2420
2421/*
2422 * Win16OpenClipboard: open the Windows clipboard. The clipboard must be open
2423 * before it can be communicated with at all. Return TRUE on success,
2424 * FALSE on failure.
2425 */
2426 static int
2427Win16OpenClipboard(void)
2428{
2429 __dpmi_regs dpmi_regs;
2430
2431 long start_time;
2432 int tick_count;
2433
2434 /* int 02xf, AX = 0x1701 attempts to open the Windows clipboard. Upon
2435 * return from the interrupt, if AX is non-zero, the clipboard was
2436 * successfully opened. If AX is zero, the clipboard could not be opened
2437 * because it is currently in use by another process.
2438 *
2439 * Remark: other DOS programs I (dk) have written that use the Windows
2440 * clipboard sometimes encounter the problem that the clipboard cannot
2441 * be opened even though it is demonstrably not in use by any other
2442 * process. In all cases, repeated attempts to open the clipboard
2443 * eventually succeed, but the initial attempt occasionally fails.
2444 *
2445 * The problem is intermittent and appears to be related to DOS being
2446 * "busy" at certain unpredictable times. DOS maintains two internal
2447 * flags that indicate whether it's busy: InDOS and CritErr. The
2448 * location of InDOS can be found by calling int 0x21, AH = 0x34. The
2449 * location of CritErr can be found by calling int 0x21, AX = 0x5d06.
2450 * If either of these flags is set, DOS is "busy" and cannot be
2451 * interrupted. See "Undocumented DOS" by Schulman et al for details.
2452 *
2453 * However here I take the easier approach that if the first call to open
2454 * the clipboard does not succeed, just try again. In fact, try once per
2455 * biostime() clock tick, up to 18 times (about one second).
2456 */
2457
2458 tick_count = 0;
2459
2460 dpmi_regs.x.ax = 0x1701; /* open Windows clipboard */
2461 if (__dpmi_int(0x2f, &dpmi_regs) == -1)
2462 {
2463 /* real-mode interrupt failed? */
2464 return FALSE; /* FALSE --> clipboard not open */
2465 }
2466
2467 /* wait up to one second */
2468 while (dpmi_regs.x.ax == 0 && tick_count++ < 18)
2469 {
2470 /* Wait one clock tick (18.2 ticks/sec = 55 msec per tick).
2471 *
2472 * We busy-wait here. Unfortunately, delay() and usleep() have been
2473 * reported to give problems with the original Windows 95. This is
2474 * fixed in service pack 1, but not everybody installed that.
2475 */
2476 start_time = biostime(0, 0L);
2477 while (biostime(0, 0L) == start_time)
2478 ;
2479
2480 dpmi_regs.x.ax = 0x1701; /* open Windows clipboard */
2481 if (__dpmi_int(0x2f, &dpmi_regs) == -1)
2482 {
2483 /* real-mode interrupt failed? */
2484 return FALSE; /* FALSE --> clipboard not open */
2485 }
2486 }
2487
2488 /* Couldn't open the clipboard, even after 18 attempts? */
2489
2490 if (tick_count >= 18 && dpmi_regs.x.ax == 0)
2491 return FALSE; /* FALSE --> clipboard not open */
2492
2493 return TRUE; /* TRUE --> clipboard opened successfully, OK */
2494}
2495
2496/*
2497 * Win16CloseClipboard: close the Windows clipboard. Return TRUE on
2498 * success, FALSE on failure. This function can always be called,
2499 * whether the clipboard is open or not.
2500 */
2501 static int
2502Win16CloseClipboard(void)
2503{
2504 __dpmi_regs dpmi_regs;
2505
2506 /* Close the clipboard. This interrupt can always be called, even
2507 * if the clipboard is already closed.
2508 */
2509
2510 dpmi_regs.x.ax = 0x1708; /* close the clipboard */
2511 if (__dpmi_int(0x2f, &dpmi_regs) == -1)
2512 {
2513 /* real-mode interrupt failed? */
2514 return FALSE; /* FALSE --> clipboard could not be closed */
2515 }
2516
2517 return TRUE; /* TRUE --> clipboard closed successfully, OK */
2518}
2519
2520/*
2521 * Win16EmptyClipboard: empty the (previously opened) Windows clipboard.
2522 * Return TRUE on success, FALSE on failure.
2523 */
2524 static int
2525Win16EmptyClipboard(void)
2526{
2527 __dpmi_regs dpmi_regs;
2528
2529 /* int 02xf, AX = 0x1702 attempts to empty the Windows clipboard. Upon
2530 * return from the interrupt, if AX == 0, the clipboard could not be
2531 * emptied (for some reason).
2532 */
2533 dpmi_regs.x.ax = 0x1702; /* empty the Windows clipboard */
2534 if (__dpmi_int(0x2f, &dpmi_regs) == -1)
2535 {
2536 /* real-mode interrupt failed? */
2537 return FALSE; /* FALSE --> clipboard could not be emptied */
2538 }
2539
2540 /* Did we succeed in clearing the clipboard? */
2541 if (dpmi_regs.x.ax == 0)
2542 return FALSE; /* FALSE --> clipboard could not be emptied */
2543
2544 return TRUE; /* TRUE --> clipboard was emptied, OK */
2545}
2546
2547/*
2548 * FreeDOSMemory: a helper function to free memory previously
2549 * allocated by a call to __dpmi_allocate_dos_memory().
2550 */
2551 static void
2552FreeDOSMemory(int protected_mode_selector)
2553{
2554 /* Free the DOS buffer and release the DPMI prot-mode selector.
2555 *
2556 * It's important that DOS memory be properly released because
2557 * there's only a limited amount of it. Therefore, if the call
2558 * to __dpmi_free_dos_memory() fails, emit an error message
2559 * unconditionally.
2560 */
2561 if (__dpmi_free_dos_memory(protected_mode_selector) == -1)
2562 EMSG("E451: could not free DOS memory buffer (DJGPP)");
2563}
2564
2565/*
2566 * Win16GetClipboardData: query the Windows clipboard as to whether data
2567 * is available in a particular clipboard format. If data is
2568 * available, allocate a buffer for it and read the data from the
2569 * clipboard into the buffer. Return a pointer to the buffer. If
2570 * no data is available in the requested format, return NULL.
2571 *
2572 * This routine allocates memory to hold the retrieved clipboard
2573 * data. It's the caller's responsibility to free this memory
2574 * once it's finished using it. The memory should be freed by
2575 * calling vim_free().
2576 */
2577 static char_u *
2578Win16GetClipboardData(int clip_data_format)
2579{
2580 __dpmi_regs dpmi_regs;
2581
2582 int real_mode_segment_address;
2583 int protected_mode_selector;
2584
2585 char_u *clip_data_buffer;
2586 long_u clip_data_size;
2587
2588 /* We only handle clipboard formats we recognize, others are ignored.
2589 *
2590 * It's not possible to create a custom clipboard format for VimClipboard
2591 * data under DOS, so one of the predefined Windows formats had to be
2592 * used for CF_VIMCLIP. Two obscure formats, popular when Windows 3.0
2593 * came out but no longer in much use today, are the DIF and SYLK formats.
2594 * DIF is the Data Interchange Format, SYLK is the Symbolic Link format.
2595 * They are both text formats and either one can be hijacked for use as
2596 * "the VimClipboard format". Of course, this conflicts with anyone who
2597 * still *is* using DIF or SYLK data formats, but that will be very few
2598 * people.
2599 *
2600 * I (dk) chose SYLK as the more obscure format because it was used
2601 * mostly for Microsoft Multiplan (the pre-cursor to Excel) and it's not
2602 * likely Multiplan is used anywhere much anymore. Mind you, Excel can
2603 * still export to both DIF and SYLK formats.
2604 */
2605
2606 switch (clip_data_format)
2607 {
2608 case CF_VIMCLIP: /* Vim's own special clipboard format */
2609 case CF_TEXT: /* Windows text */
2610 case CF_OEMTEXT: /* DOS (OEM) text */
2611
2612 /* int 02xf, AX = 0x1704 returns the number of bytes of data currently
2613 * on the Windows clipboard, for the specified format. Upon return
2614 * from the interrupt, DX:AX = the number of bytes, rounded up to the
2615 * nearest multiple of 32.
2616 */
2617
2618 dpmi_regs.x.ax = 0x1704; /* get size of clipbd data */
2619 dpmi_regs.x.dx = clip_data_format;
2620 if (__dpmi_int(0x2f, &dpmi_regs) == -1)
2621 {
2622 /* real-mode interrupt failed? */
2623 return NULL; /* early exit */
2624 }
2625
2626 /* Did we get anything? If not, this is not an error. */
2627 if (dpmi_regs.x.dx == 0 && dpmi_regs.x.ax == 0)
2628 {
2629 /* no CF_VIMCLIP data? */
2630 return NULL; /* early exit */
2631 }
2632
2633 /* There is data available in the requested clipboard format.
2634 *
2635 * Calculate data size. Remember this is rounded up to the nearest
2636 * multiple of 32, so clip_data_size is actually an upper limit.
2637 * The extra bytes, if any, are set to null (0x00) when the data is
2638 * read from the clipboard. (Later:) actually I'm no longer sure
2639 * this is strictly true: the end-of-data is marked by a null, but
2640 * the extra bytes appear to sometimes be null, sometimes not.
2641 * They may just be garbage.
2642 */
2643 clip_data_size = dpmi_regs.x.ax + (dpmi_regs.x.dx << 16);
2644
2645 /* Allocate memory to retrieve the data. The buffer has to lie in the
2646 * DOS memory region (in the first 1 MByte of address space) because
2647 * the Windows clipboard interface expects a 16-bit segment:offset
2648 * pointer to a buffer address within the DOS region. Must therefore
2649 * use __dpmi_allocate_dos_memory() instead of lalloc() or alloc().
2650 */
2651 real_mode_segment_address = __dpmi_allocate_dos_memory(
2652 (clip_data_size + 15) >> 4, /* buffer size, in 16-byte paragraphs */
2653 &protected_mode_selector); /* prot-mode selector for the address */
2654
2655 if (real_mode_segment_address == -1)
2656 {
2657 /* memory allocation failed. */
2658
2659 /* Technically we don't know why the allocation failed, but
2660 * almost always it will be because there wasn't enough DOS
2661 * memory to satisfy the request, so report that as the problem.
2662 * On my system, DJGPP is able to satisfy a DOS allocation request
2663 * up to about 600K in size. This depends on your HIMEM.SYS and
2664 * EMM386.EXE settings however.
2665 */
2666 EMSG("E452: Clipboard data too large, cannot allocate DOS buffer");
2667 return NULL; /* early exit */
2668 }
2669
2670 /* Copy data from the clipboard into the buffer. Experiments show that
2671 * the Windows clipboard is smart enough to handle data transfers
2672 * larger than 64K properly, even though the buffer address is a 16-bit
2673 * segment:offset (which would normally limit the block size to 64K
2674 * unless ES gets incremented).
2675 */
2676 dpmi_regs.x.ax = 0x1705; /* get clipboard data */
2677 dpmi_regs.x.dx = clip_data_format; /* CF_VIMCLIP */
2678 dpmi_regs.x.es = real_mode_segment_address; /* buffer ad: segment */
2679 dpmi_regs.x.bx = 0; /* buffer ad: offset */
2680 if (__dpmi_int( 0x2f, &dpmi_regs) == -1)
2681 {
2682 /* real-mode interrupt failed? */
2683 EMSG("E453: could not copy clipboard data to DOS buffer");
2684 FreeDOSMemory(protected_mode_selector); /* clean up DOS mem */
2685 return NULL; /* early exit */
2686 }
2687
2688 /* Clipboard data is now in DOS memory in the buffer pointed to by
2689 * ES:BX. Copy this into ordinary memory that Vim can access (ie.
2690 * prot-mode memory). Allocate one extra byte to ensure the text
2691 * is terminated properly (in case it was somehow corrupted).
2692 */
2693 clip_data_buffer = (char_u *)lalloc(clip_data_size + 1, TRUE);
2694
2695 if (clip_data_buffer == NULL)
2696 {
2697 /* allocation failed? */
2698 EMSG("E454: could not allocate clipboard memory buffer");
2699 FreeDOSMemory(protected_mode_selector); /* clean up DOS mem */
2700 return NULL; /* early exit */
2701 }
2702
2703 *(clip_data_buffer + clip_data_size) = '\0'; /* ensure terminated */
2704
2705 /* Copy the data from DOS memory to Vim-accessible memory. */
2706 movedata( /* DJGPP version of memcpy() */
2707 protected_mode_selector, 0, /* source: DOS ad (via selector) */
2708 _my_ds(), (unsigned)clip_data_buffer,
2709 /* target: normal mem address */
2710 clip_data_size); /* how many bytes */
2711
2712 /* Free the DOS buffer and release the DPMI prot-mode selector. */
2713 FreeDOSMemory(protected_mode_selector); /* clean up DOS memory */
2714
2715 return clip_data_buffer; /* return pointer to allocated buffer */
2716
2717 default: /* unknown clipboard format */
2718 return NULL;
2719 }
2720}
2721
2722/*
2723 * Win16SetClipboardData: send 'clip_data_size' bytes of data from the buffer
2724 * pointed to by 'clip_data', to the Windows clipboard. The data is
2725 * registered with the clipboard as being in the 'clip_data_format'
2726 * format.
2727 */
2728 static int
2729Win16SetClipboardData(
2730 int clip_data_format,
2731 char_u *clip_data,
2732 int clip_data_size,
2733 int clip_data_type)
2734{
2735 __dpmi_regs dpmi_regs;
2736
2737 int real_mode_segment_address;
2738 int protected_mode_selector;
2739 long_u protected_mode_offset = 0L;
2740 int total_size = clip_data_size;
2741
2742 char_u *clip_sel_type;
2743
2744 /* If we're using the CF_VIMCLIP custom format, allocate an extra
2745 * byte for clip_sel_type, which is a character indicating the type
2746 * of text selection: MLINE, MCHAR, or MBLOCK.
2747 */
2748 if (clip_data_format == CF_VIMCLIP)
2749 total_size++; /* extra byte for marker */
2750
2751 /* Data cannot be sent directly from a Vim string (pClipData) to
2752 * the Windows clipboard, because the Windows clipboard interface
2753 * expects a 16-bit (DOS) segment:offset address for the source
2754 * buffer. Therefore we must create a "transfer buffer" in the DOS
2755 * memory region (in the first 1 MByte of address space) and copy
2756 * the Vim string into that. From there, the data can then be sent
2757 * to the Windows clipboard.
2758 *
2759 * To allocate DOS memory, we must use __dpmi_allocate_dos_memory()
2760 * instead of lalloc() or alloc(). If the allocation fails, it will
2761 * almost invariably be because there is not enough DOS memory
2762 * available to accommodate the size of clip_data. There is nothing
2763 * we can do about this, we simply have to fail.
2764 */
2765 real_mode_segment_address = __dpmi_allocate_dos_memory(
2766 (total_size + 15) >> 4, /* buffer size, in 16-byte paragraphs */
2767 &protected_mode_selector); /* prot-mode selector for the address */
2768
2769 if (real_mode_segment_address == -1)
2770 {
2771 /* memory allocation failed. */
2772 /* Technically we don't know why the allocation failed, but
2773 * almost always it will be because there wasn't enough DOS
2774 * memory to satisfy the request. On my system, DJGPP is able
2775 * to satisfy a DOS allocation request up to about 600K in size.
2776 * This depends however on HIMEM.SYS and EMM386.EXE settings.
2777 */
2778 return FALSE; /* early exit */
2779 }
2780
2781 /* Copy data from Vim's buffer (clip_data) into the DOS transfer buffer.
2782 * This can be larger than 64K; movedata() takes care of crossing any
2783 * 16-bit segment boundaries.
2784 *
2785 * If we're using Vim's custom clipboard format, we must copy one extra
2786 * byte to indicate the type of selection: line, character, or block.
2787 */
2788 if (clip_data_format == CF_VIMCLIP)
2789 {
2790 switch (clip_data_type)
2791 {
2792 default:
2793 case MLINE: clip_sel_type = "L"; break;
2794 case MCHAR: clip_sel_type = "C"; break;
2795#ifdef FEAT_VISUAL
2796 case MBLOCK: clip_sel_type = "B"; break;
2797#endif
2798 }
2799
2800 movedata(
2801 _my_ds(), (unsigned)clip_sel_type,
2802 /* source: normal memory address */
2803 protected_mode_selector, 0, /* target: DOS ad (via selector) */
2804 1); /* how many bytes to copy */
2805
2806 protected_mode_offset += STRLEN(clip_sel_type); /* allow for marker */
2807 }
2808
2809 movedata(
2810 _my_ds(), (unsigned)clip_data, /* source: normal memory address */
2811 protected_mode_selector, /* target: DOS address (via selector) */
2812 protected_mode_offset, /* non-zero, if using clip_sel_type */
2813 clip_data_size); /* how many bytes to copy */
2814
2815 /* Send data from the DOS transfer buffer to the Windows clipboard.
2816 * int 02xf, AX = 0x1703 sends SI:CX bytes of data from the buffer
2817 * at ES:BX, to the clipboard.
2818 */
2819 dpmi_regs.x.ax = 0x1703; /* send clipboard data */
2820 dpmi_regs.x.dx = clip_data_format; /* flag: format of the data */
2821 dpmi_regs.x.si = ((total_size >> 16)
2822 & 0x0000ffffL); /* hi word of data size */
2823 dpmi_regs.x.cx = (total_size & 0x0000ffffL);
2824 /* lo word of data size */
2825 dpmi_regs.x.es = real_mode_segment_address; /* buffer address: segment */
2826 dpmi_regs.x.bx = 0; /* buffer address: offset */
2827 if (__dpmi_int(0x2f, &dpmi_regs) == -1)
2828 {
2829 /* real-mode interrupt failed. */
2830 FreeDOSMemory(protected_mode_selector); /* clean up DOS memory */
2831 return FALSE; /* early exit */
2832 }
2833
2834 /* Free the DOS buffer and release the DPMI prot-mode selector. */
2835 FreeDOSMemory(protected_mode_selector); /* clean up DOS memory */
2836
2837 return TRUE; /* TRUE --> data successfully sent to clipboard */
2838}
2839
2840#endif /* FEAT_CLIPBOARD */
2841#endif /* DJGPP */
2842
2843/*
2844 * End of MS-DOS only code
2845 */
2846#endif /* WIN16 */
2847
2848/* common MS-DOS and Win16 code follows */
2849
2850 static int
2851vim_chmod(char_u *name)
2852{
2853 char_u *p;
2854 int f;
2855 int c = 0;
2856
2857 /* chmod() can't handle a file name with a trailing slash, remove it.
2858 * But don't remove it for "/" or "c:/". */
2859 p = name + STRLEN(name);
2860 if (p > name)
2861 --p;
2862 if (p > name && (*p == '\\' || *p == '/') && p[-1] != ':')
2863 {
2864 c = *p; /* remove trailing (back)slash */
2865 *p = NUL;
2866 }
2867 else
2868 p = NULL;
2869#if defined(__BORLANDC__) && (__BORLANDC__ > 0x410)
2870 /* this also sets the archive bit, supported by Borland C 4.0 and later,
2871 * where __BORLANDC__ is 0x450 (3.1 is 0x410) */
2872 f = _rtl_chmod((char *)name, 0, 0);
2873#else
2874 f = _chmod((char *)name, 0, 0);
2875#endif
2876 if (p != NULL)
2877 *p = c; /* put back (back)slash */
2878 return f;
2879}
2880
2881/*
2882 * get file permissions for 'name'
2883 * Returns -1 for error.
2884 * Returns FA_attributes defined in dos.h
2885 */
2886 long
2887mch_getperm(char_u *name)
2888{
2889 return (long)vim_chmod(name); /* get file mode */
2890}
2891
2892/*
2893 * set file permission for 'name' to 'perm'
2894 *
2895 * return FAIL for failure, OK otherwise
2896 */
2897 int
2898mch_setperm(
2899 char_u *name,
2900 long perm)
2901{
2902 perm |= FA_ARCH; /* file has changed, set archive bit */
2903#if defined(__BORLANDC__) && (__BORLANDC__ > 0x410)
2904 return (_rtl_chmod((char *)name, 1, (int)perm) == -1 ? FAIL : OK);
2905#else
2906 return (_chmod((char *)name, 1, (int)perm) == -1 ? FAIL : OK);
2907#endif
2908}
2909
2910/*
2911 * Set hidden flag for "name".
2912 */
2913 void
2914mch_hide(char_u *name)
2915{
2916 /* DOS 6.2 share.exe causes "seek error on file write" errors when making
2917 * the swap file hidden. Thus don't do it. */
2918}
2919
2920/*
2921 * return TRUE if "name" is a directory
2922 * return FALSE if "name" is not a directory
2923 * return FALSE for error
2924 *
2925 * beware of a trailing (back)slash
2926 */
2927 int
2928mch_isdir(char_u *name)
2929{
2930 int f;
2931
2932 f = vim_chmod(name);
2933 if (f == -1)
2934 return FALSE; /* file does not exist at all */
2935 if ((f & FA_DIREC) == 0)
2936 return FALSE; /* not a directory */
2937 return TRUE;
2938}
2939
Bram Moolenaar071d4272004-06-13 20:20:40 +00002940/*
2941 * Return 1 if "name" can be executed, 0 if not.
2942 * Return -1 if unknown.
2943 */
2944 int
2945mch_can_exe(name)
2946 char_u *name;
2947{
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002948 char *p;
2949
2950 p = searchpath(name);
2951 if (p == NULL || mch_isdir(p))
2952 return FALSE;
2953 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002954}
Bram Moolenaar071d4272004-06-13 20:20:40 +00002955
2956/*
2957 * Check what "name" is:
2958 * NODE_NORMAL: file or directory (or doesn't exist)
2959 * NODE_WRITABLE: writable device, socket, fifo, etc.
2960 * NODE_OTHER: non-writable things
2961 */
2962 int
2963mch_nodetype(char_u *name)
2964{
2965 if (STRICMP(name, "AUX") == 0
2966 || STRICMP(name, "CON") == 0
2967 || STRICMP(name, "CLOCK$") == 0
2968 || STRICMP(name, "NUL") == 0
2969 || STRICMP(name, "PRN") == 0
2970 || ((STRNICMP(name, "COM", 3) == 0
2971 || STRNICMP(name, "LPT", 3) == 0)
2972 && VIM_ISDIGIT(name[3])
2973 && name[4] == NUL))
2974 return NODE_WRITABLE;
2975 /* TODO: NODE_OTHER? */
2976 return NODE_NORMAL;
2977}
2978
2979/*
2980 * Get name of current directory into buffer 'buf' of length 'len' bytes.
2981 * Return OK for success, FAIL for failure.
2982 */
2983 int
2984mch_dirname(
2985 char_u *buf,
2986 int len)
2987{
2988#ifdef DJGPP
2989 if (getcwd((char *)buf, len) == NULL)
2990 return FAIL;
2991 /* turn the '/'s returned by DJGPP into '\'s */
2992 slash_adjust(buf);
2993 return OK;
2994#else
2995 return (getcwd((char *)buf, len) != NULL ? OK : FAIL);
2996#endif
2997}
2998
2999/*
3000 * this version of remove is not scared by a readonly (backup) file
3001 *
3002 * returns -1 on error, 0 otherwise (just like remove())
3003 */
3004 int
3005mch_remove(char_u *name)
3006{
3007 (void)mch_setperm(name, 0); /* default permissions */
3008 return unlink((char *)name);
3009}
3010
3011/*
3012 * Special version of getenv(): Use uppercase name.
3013 */
3014 char_u *
3015mch_getenv(char_u *name)
3016{
3017 int i;
3018#define MAXENVLEN 50
3019 char_u var_copy[MAXENVLEN + 1];
3020 char_u *p;
3021 char_u *res;
3022
3023 /*
3024 * Take a copy of the argument, and force it to upper case before passing
3025 * to getenv(). On DOS systems, getenv() doesn't like lower-case argument
3026 * (unlike Win32 et al.) If the name is too long to fit in var_copy[]
3027 * allocate memory.
3028 */
3029 if ((i = STRLEN(name)) > MAXENVLEN)
3030 p = alloc(i + 1);
3031 else
3032 p = var_copy;
3033 if (p == NULL)
3034 p = name; /* out of memory, fall back to unmodified name */
3035 else
3036 {
3037 for (i = 0; name[i] != NUL; ++i)
3038 p[i] = toupper(name[i]);
3039 p[i] = NUL;
3040 }
3041
3042 res = (char_u *)getenv((char *)p);
3043
3044 if (p != var_copy && p != name)
3045 vim_free(p);
3046
3047 return res;
3048}
3049
3050/*
3051 * Insert user name in s[len].
3052 */
3053 int
3054mch_get_user_name(
3055 char_u *s,
3056 int len)
3057{
3058 *s = NUL;
3059 return FAIL;
3060}
3061
3062/*
3063 * Insert host name is s[len].
3064 */
3065 void
3066mch_get_host_name(
3067 char_u *s,
3068 int len)
3069{
3070#ifdef DJGPP
Bram Moolenaarbbebc852005-07-18 21:47:53 +00003071 vim_strncpy(s, "PC (32 bits Vim)", len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003072#else
Bram Moolenaarbbebc852005-07-18 21:47:53 +00003073 vim_strncpy(s, "PC (16 bits Vim)", len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003074#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003075}