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