1 /* MS-DOS specific C utilities. Coded by Morten Welinder 1993
2 Copyright (C) 1993 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
6 GNU Emacs is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 1, or (at your option)
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs; see the file COPYING. If not, write to
18 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
20 /* Note: some of the stuff here was taken from end of sysdep.c in demacs. */
28 #include <sys/param.h>
34 #include "termhooks.h"
39 /* #include <process.h> */
40 /* Damn that local process.h! Instead we can define P_WAIT ourselves. */
43 static int break_stat
; /* BREAK check mode status. */
44 static int stdin_stat
; /* stdin IOCTL status. */
45 static int extended_kbd
; /* 101 (102) keyboard present. */
47 int have_mouse
; /* Mouse present? */
48 static int mouse_last_x
;
49 static int mouse_last_y
;
51 /* Turn off Dos' Ctrl-C checking and inhibit interpretation of control chars
52 by Dos. Determine the keyboard type. */
56 union REGS inregs
, outregs
;
59 int86 (0x15, &inregs
, &outregs
);
60 extended_kbd
= (!outregs
.x
.cflag
) && (outregs
.h
.ah
== 0);
62 break_stat
= getcbrk ();
64 install_ctrl_break_check ();
65 have_mouse
= Mouse_init1 ();
67 inregs
.x
.ax
= 0x4400; /* Get IOCTL status. */
68 inregs
.x
.bx
= 0x00; /* 0 = stdin. */
69 intdos (&inregs
, &outregs
);
70 stdin_stat
= outregs
.h
.dl
;
72 inregs
.x
.dx
= (outregs
.x
.dx
| 0x0020) & 0x0027; /* raw mode */
74 intdos (&inregs
, &outregs
);
75 return !outregs
.x
.cflag
;
78 /* Restore status of standard input and Ctrl-C checking. */
82 union REGS inregs
, outregs
;
85 if (have_mouse
) Mouse_off ();
87 inregs
.x
.ax
= 0x4401; /* Set IOCTL status. */
88 inregs
.x
.bx
= 0x00; /* 0 = stdin. */
89 inregs
.x
.dx
= stdin_stat
;
90 intdos (&inregs
, &outregs
);
91 return !outregs
.x
.cflag
;
95 ibmpc_translate_map
[] =
97 /* --------------- 00 to 0f --------------- */
100 0xffb1, /* Keypad 1 */
101 0xffb2, /* Keypad 2 */
102 0xffb3, /* Keypad 3 */
103 0xffb4, /* Keypad 4 */
104 0xffb5, /* Keypad 5 */
105 0xffb6, /* Keypad 6 */
106 0xffb7, /* Keypad 7 */
107 0xffb8, /* Keypad 8 */
108 0xffb9, /* Keypad 9 */
109 0xffb0, /* Keypad 0 */
111 0xff08, /* Backspace */
112 0xff74, /* (Shift) Tab [Tab doesn't use this table] */
114 /* --------------- 10 to 1f --------------- */
115 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']',
116 0xff8d, /* Keypad Enter */
120 /* --------------- 20 to 2f --------------- */
121 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`',
123 '\\', 'z', 'x', 'c', 'v',
125 /* --------------- 30 to 3f --------------- */
126 'b', 'n', 'm', ',', '.',
139 /* --------------- 40 to 4f --------------- */
149 0xff55, /* Page Up */
152 0xffb5, /* Keypad 5 */
157 /* --------------- 50 to 5f --------------- */
159 0xff56, /* Page Down */
162 0xffbe, /* (Shift) F1 */
163 0xffbf, /* (Shift) F2 */
164 0xffc0, /* (Shift) F3 */
165 0xffc1, /* (Shift) F4 */
166 0xffc2, /* (Shift) F5 */
167 0xffc3, /* (Shift) F6 */
168 0xffc4, /* (Shift) F7 */
169 0xffc5, /* (Shift) F8 */
170 0xffc6, /* (Shift) F9 */
171 0xffc7, /* (Shift) F10 */
172 0xffbe, /* (Ctrl) F1 */
173 0xffbf, /* (Ctrl) F2 */
175 /* --------------- 60 to 6f --------------- */
176 0xffc0, /* (Ctrl) F3 */
177 0xffc1, /* (Ctrl) F4 */
178 0xffc2, /* (Ctrl) F5 */
179 0xffc3, /* (Ctrl) F6 */
180 0xffc4, /* (Ctrl) F7 */
181 0xffc5, /* (Ctrl) F8 */
182 0xffc6, /* (Ctrl) F9 */
183 0xffc7, /* (Ctrl) F10 */
184 0xffbe, /* (Alt) F1 */
185 0xffbf, /* (Alt) F2 */
186 0xffc0, /* (Alt) F3 */
187 0xffc1, /* (Alt) F4 */
188 0xffc2, /* (Alt) F5 */
189 0xffc3, /* (Alt) F6 */
190 0xffc4, /* (Alt) F7 */
191 0xffc5, /* (Alt) F8 */
193 /* --------------- 70 to 7f --------------- */
194 0xffc6, /* (Alt) F9 */
195 0xffc7, /* (Alt) F10 */
196 0xff6d, /* (Ctrl) Sys Rq */
197 0xff51, /* (Ctrl) Left */
198 0xff53, /* (Ctrl) Right */
199 0xff57, /* (Ctrl) End */
200 0xff56, /* (Ctrl) Page Down */
201 0xff50, /* (Ctrl) Home */
202 '1', '2', '3', '4', '5', '6', '7', '8', /* (Alt) */
204 /* --------------- 80 to 8f --------------- */
205 '9', '0', '-', '=', /* (Alt) */
206 0xff55, /* (Ctrl) Page Up */
209 0xffc8, /* (Shift) F11 */
210 0xffc9, /* (Shift) F12 */
211 0xffc8, /* (Ctrl) F11 */
212 0xffc9, /* (Ctrl) F12 */
213 0xffc8, /* (Alt) F11 */
214 0xffc9, /* (Alt) F12 */
215 0xff52, /* (Ctrl) Up */
216 0xffae, /* (Ctrl) Grey - */
217 0xffb5, /* (Ctrl) Keypad 5 */
219 /* --------------- 90 to 9f --------------- */
220 0xffab, /* (Ctrl) Grey + */
221 0xff54, /* (Ctrl) Down */
222 0xff63, /* (Ctrl) Insert */
223 0xffff, /* (Ctrl) Delete */
224 0xff09, /* (Ctrl) Tab */
225 0xffaf, /* (Ctrl) Grey / */
226 0xffaa, /* (Ctrl) Grey * */
227 0xff50, /* (Alt) Home */
228 0xff52, /* (Alt) Up */
229 0xff55, /* (Alt) Page Up */
231 0xff51, /* (Alt) Left */
233 0xff53, /* (Alt) Right */
235 0xff57, /* (Alt) End */
237 /* --------------- a0 to af --------------- */
238 0xff54, /* (Alt) Down */
239 0xff56, /* (Alt) Page Down */
240 0xff63, /* (Alt) Insert */
241 0xffff, /* (Alt) Delete */
242 0xffaf, /* (Alt) Grey / */
243 0xff09, /* (Alt) Tab */
244 0xff0d /* (Alt) Enter */
247 /* Get a char from keyboard. Function keys are put into the event queue. */
251 struct input_event event
;
254 int ctrl_p
, alt_p
, shift_p
;
256 /* Calculate modifier bits */
257 regs
.h
.ah
= extended_kbd
? 0x12 : 0x02;
258 int86 (0x16, ®s
, ®s
);
259 ctrl_p
= ((regs
.h
.al
& 4) != 0);
260 shift_p
= ((regs
.h
.al
& 3) != 0);
261 alt_p
= ((extended_kbd
? (regs
.h
.ah
& 2) : (regs
.h
.al
& 8)) != 0);
266 register unsigned char c
;
269 regs
.h
.ah
= extended_kbd
? 0x10 : 0x00;
270 int86 (0x16, ®s
, ®s
);
274 /* Determine from the scan code if a keypad key was pressed. */
275 if (c
>= '0' && c
<= '9' && sc
> 0xb)
276 sc
= (c
== '0') ? 0xb : (c
- '0' + 1), c
= 0;
281 case 10: /* Ctrl Enter */
285 case '.': /* Decimal point or decimal comma */
301 || (ctrl_p
&& shift_p
)
302 || (c
== 0xe0 && sc
!= 0) /* Pseudo-key */
303 || sc
== 0x37 /* Grey * */
304 || sc
== 0x4a /* Grey - */
305 || sc
== 0x4e /* Grey + */
306 || sc
== 0x0e) /* Back space *key*, not Ctrl-h */
308 if (sc
>= (sizeof (ibmpc_translate_map
) / sizeof (short)))
311 code
= ibmpc_translate_map
[sc
];
316 event
.kind
= non_ascii_keystroke
;
317 event
.code
= code
& 0xff;
321 /* Don't return S- if we don't have to. */
322 if (code
>= 'a' && code
<= 'z')
324 c
= shift_p
? toupper (code
) : code
;
328 if (c
== 0) c
= code
;
329 event
.kind
= ascii_keystroke
;
333 = (shift_p
? shift_modifier
: 0)
334 + (ctrl_p
? ctrl_modifier
: 0)
335 + (alt_p
? meta_modifier
: 0);
336 /* EMACS == Enter Meta Alt Control Shift */
337 event
.frame_or_window
= selected_frame
;
338 gettimeofday (&tv
, NULL
);
339 event
.timestamp
= tv
.tv_usec
;
340 kbd_buffer_store_event (&event
);
348 int but
, press
, x
, y
, ok
;
350 /* Check for mouse movement *before* buttons. */
351 Mouse_check_moved ();
353 for (but
= 0; but
< NUM_MOUSE_BUTTONS
; but
++)
354 for (press
= 0; press
< 2; press
++)
357 ok
= Mouse_pressed (but
, &x
, &y
);
359 ok
= Mouse_released (but
, &x
, &y
);
362 event
.kind
= mouse_click
;
365 = (shift_p
? shift_modifier
: 0)
366 + (ctrl_p
? ctrl_modifier
: 0)
367 + (alt_p
? meta_modifier
: 0)
368 + (press
? down_modifier
: up_modifier
);
371 event
.frame_or_window
= selected_frame
;
372 gettimeofday (&tv
, NULL
);
373 event
.timestamp
= tv
.tv_usec
;
374 kbd_buffer_store_event (&event
);
382 static int prev_get_char
= -1;
384 /* Return 1 if a key is ready to be read without suspending execution. */
387 if (prev_get_char
!= -1)
390 return ((prev_get_char
= dos_rawgetc ()) != -1);
393 /* Read a key. Return -1 if no key is ready. */
396 if (prev_get_char
!= -1)
398 int c
= prev_get_char
;
403 return dos_rawgetc ();
406 /* Hostnames for a pc are not really funny, but they are used in change log
407 so we emulate the best we can. */
408 gethostname (p
, size
)
412 char *q
= egetenv ("HOSTNAME");
419 /* Destructively turn backslashes into slashes. */
421 dostounix_filename (p
)
432 /* Destructively turn slashes into backslashes. */
434 unixtodos_filename (p
)
445 /* Get the default directory for a given drive. 0=def, 1=A, 2=B, ... */
447 getdefdir (drive
, dst
)
455 regs
.x
.si
= (int) dst
;
457 intdos (®s
, ®s
);
458 return !regs
.x
.cflag
;
461 /* Remove all CR's that are followed by a LF. */
465 register unsigned char *buf
;
467 unsigned char *np
= buf
;
468 unsigned char *startp
= buf
;
469 unsigned char *endp
= buf
+ n
;
478 if (*(++buf
) != 0x0a)
488 /* Run command as specified by ARGV in directory DIR.
489 The command is run with input from TEMPIN and output to file TEMPOUT. */
491 run_msdos_command (argv
, dir
, tempin
, tempout
)
492 unsigned char **argv
;
496 char *saveargv1
, *saveargv2
, **envv
;
497 char oldwd
[MAXPATHLEN
+ 1]; /* Fixed size is safe on MSDOS. */
498 int msshell
, result
= -1;
499 int in
, out
, inbak
, outbak
, errbak
;
502 /* Get current directory as MSDOS cwd is not per-process. */
505 cmd
= Ffile_name_nondirectory (build_string (argv
[0]));
506 msshell
= !NILP (Fmember (cmd
, Fsymbol_value (intern ("msdos-shells"))))
507 && !strcmp ("-c", argv
[1]);
515 unixtodos_filename (argv
[2] = strdup (argv
[2]));
519 /* Build the environment array. */
521 extern Lisp_Object Vprocess_environment
;
522 Lisp_Object tmp
, lst
;
525 lst
= Vprocess_environment
;
526 len
= XFASTINT (Flength (lst
));
528 envv
= alloca ((len
+ 1) * sizeof (char *));
529 for (i
= 0; i
< len
; i
++)
533 CHECK_STRING (tmp
, 0);
534 envv
[i
] = alloca (XSTRING (tmp
)->size
+ 1);
535 strcpy (envv
[i
], XSTRING (tmp
)->data
);
537 envv
[len
] = (char *) 0;
540 if (XTYPE (dir
) == Lisp_String
)
541 chdir (XSTRING (dir
)->data
);
545 if (inbak
< 0 || outbak
< 0 || errbak
< 0)
546 goto done
; /* Allocation might fail due to lack of descriptors. */
551 result
= spawnve (P_WAIT
, argv
[0], argv
, envv
);
575 fprintf (stderr
, "%s not yet implemented\r\n", badfunc
);
580 /* A list of unimplemented functions that we silently ignore. */
581 unsigned alarm (s
) unsigned s
; {}
582 fork () { return 0; }
583 int kill (x
, y
) int x
, y
; { return -1; }
585 void volatile pause () {}
587 setpgrp () {return 0; }
588 setpriority (x
,y
,z
) int x
,y
,z
; { return 0; }
589 sigsetmask (x
) int x
; { return 0; }
590 unrequest_sigio () {}
600 int len
= strlen (path
);
601 char *tmp
= (char *) alloca (len
+ 1);
602 /* Gotta do this extern here due to the corresponding #define: */
605 if (*path
&& path
[1] == ':' && (getdisk () != tolower (path
[0]) - 'a'))
606 setdisk (tolower (path
[0]) - 'a');
609 if (strcmp (path
, "/") && strcmp (path
+ 1, ":/") && (path
[len
- 1] == '/'))
614 /* Sleep SECS. If KBDOK also return immediately if a key is pressed. */
616 sleep_or_kbd_hit (secs
, kbdok
)
622 gettimeofday (&t
, NULL
);
623 clnow
= t
.tv_sec
* 100 + t
.tv_usec
/ 10000;
624 clthen
= clnow
+ (100 * secs
);
628 gettimeofday (&t
, NULL
);
629 clnow
= t
.tv_sec
* 100 + t
.tv_usec
/ 10000;
630 if (kbdok
&& detect_input_pending ())
633 while (clnow
< clthen
);
636 /* Define a lot of environment variables if not already defined. Don't
637 remove anything unless you know what you're doing -- lots of code will
638 break if one or more of these are missing. */
640 init_environment (argc
, argv
, skip_args
)
647 /* We default HOME to the directory from which Emacs was started, but with
648 a "/bin" suffix removed. */
650 t
= alloca (strlen (s
) + 1);
653 while (s
!= t
&& *s
!= '/' && *s
!= ':') s
--;
655 t
= "c:/emacs"; /* When run under debug32. */
660 if (s
- 4 >= t
&& strcmp (s
- 4, "/bin") == 0)
661 s
[strlen (s
) - 4] = 0;
663 setenv ("HOME", t
, 0);
665 /* We set EMACSPATH to ~/bin (expanded) */
667 t
= strcpy (alloca (strlen (s
) + 6), s
);
668 if (s
[strlen (s
) - 1] != '/') strcat (t
, "/");
670 setenv ("EMACSPATH", t
, 0);
672 /* I don't expect anybody to ever use other terminals so the internal
673 terminal is the default. */
674 setenv ("TERM", "internal", 0);
676 /* SHELL is a bit tricky -- COMSPEC is the closest we come, but we must
677 downcase it and mirror the backslashes. */
678 s
= getenv ("COMSPEC");
679 if (!s
) s
= "c:/command.com";
680 t
= alloca (strlen (s
) + 1);
683 dostounix_filename (t
);
684 setenv ("SHELL", t
, 0);
686 /* PATH is also downcased and backslashes mirrored. */
689 t
= alloca (strlen (s
) + 3);
690 /* Current directory is always considered part of MsDos's path but it is
691 not normally mentioned. Now it is. */
692 strcat (strcpy (t
, ".;"), s
);
694 dostounix_filename (t
); /* Not a single file name, but this should work. */
695 setenv ("PATH", t
, 1);
697 /* In some sense all dos users have root privileges, so... */
698 setenv ("USER", "root", 0);
699 setenv ("NAME", getenv ("USER"), 0);
701 /* Time zone determined from country code. To make this possible, the
702 country code may not span more than one time zone. In other words,
703 in the USA, you lose. */
704 switch (dos_country_code
)
706 case 31: /* Belgium */
707 case 32: /* The Netherlands */
708 case 33: /* France */
710 case 36: /* Hungary */
711 case 38: /* Yugoslavia (or what's left of it?) */
713 case 41: /* Switzerland */
714 case 42: /* Tjekia */
715 case 45: /* Denmark */
716 case 46: /* Sweden */
717 case 47: /* Norway */
718 case 48: /* Poland */
719 case 49: /* Germany */
720 /* Daylight saving from last Sunday in March to last Sunday in
721 September, both at 2AM. */
722 setenv ("TZ", "MET-01METDST-02,M3.5.0/02:00,M9.5.0/02:00", 0);
724 case 44: /* United Kingdom */
725 case 351: /* Portugal */
726 case 354: /* Iceland */
727 setenv ("TZ", "GMT+00", 0);
731 setenv ("TZ", "???-09", 0);
733 case 90: /* Turkey */
734 case 358: /* Finland */
735 case 972: /* Israel */
736 setenv ("TZ", "EET-02", 0);
742 /* Flash the screen as a substitute for BEEPs. */
744 static unsigned char _xorattr
;
747 visible_bell (xorattr
)
748 unsigned char xorattr
;
757 movl _ScreenPrimary,%eax
788 static int internal_terminal
= 0;
800 if (internal_terminal
&& f
== stdout
)
802 if (have_mouse
) Mouse_off ();
804 count
= stdout
->_ptr
- stdout
->_base
;
818 ScreenAttrib
= *cp
++;
822 visible_bell (*cp
++);
831 for (i
= ScreenCols () - 1; i
>= x
; i
--)
832 ScreenPutChar (' ', ScreenAttrib
, i
, y
);
844 ScreenAttrib
^= *cp
++;
864 ScreenPutChar (c
, ScreenAttrib
, x
++, y
);
869 ScreenSetCursor (y
, x
);
870 if (have_mouse
) Mouse_on ();
873 /* This is a call to the original fflush. */
877 /* Do we need the internal terminal? */
879 internal_terminal_init ()
881 char *term
= getenv ("TERM");
884 = (!noninteractive
) && term
&& !strcmp (term
, "internal");
887 /* These must be global. */
888 static _go32_dpmi_seginfo ctrl_break_vector
;
889 static _go32_dpmi_registers ctrl_break_regs
;
890 static int ctrlbreakinstalled
= 0;
892 /* Interrupt level detection of Ctrl-Break. Don't do anything fancy here! */
894 ctrl_break_func (regs
)
895 _go32_dpmi_registers
*regs
;
901 install_ctrl_break_check ()
903 if (!ctrlbreakinstalled
)
905 /* Don't press Ctrl-Break if you don't have either DPMI or Emacs
906 was compiler with Djgpp 1.11 maintenance level 2 or later! */
907 ctrlbreakinstalled
= 1;
908 ctrl_break_vector
.pm_offset
= (int) ctrl_break_func
;
909 _go32_dpmi_allocate_real_mode_callback_iret (&ctrl_break_vector
,
911 _go32_dpmi_set_real_mode_interrupt_vector (0x1b, &ctrl_break_vector
);
916 /* Mouse routines under devellopment follow. Coordinates are in screen
917 positions and zero based. Mouse buttons are numbered from left to
918 right and also zero based. */
920 static int mouse_button_translate
[NUM_MOUSE_BUTTONS
];
921 static int mouse_button_count
;
930 regs
.x
.dx
= 8 * (ScreenCols () - 1);
931 int86 (0x33, ®s
, ®s
);
935 regs
.x
.dx
= 8 * (ScreenRows () - 1);
936 int86 (0x33, ®s
, ®s
);
938 mouse_moveto (ScreenCols () - 1, ScreenRows () - 1);
948 int86 (0x33, ®s
, ®s
);
957 int86 (0x33, ®s
, ®s
);
967 mouse_last_x
= regs
.x
.cx
= x
* 8;
968 mouse_last_y
= regs
.x
.dx
= y
* 8;
969 int86 (0x33, ®s
, ®s
);
973 mouse_pressed (b
, xp
, yp
)
978 if (b
>= mouse_button_count
)
981 regs
.x
.bx
= mouse_button_translate
[b
];
982 int86 (0x33, ®s
, ®s
);
984 *xp
= regs
.x
.cx
/ 8, *yp
= regs
.x
.dx
/8;
985 return (regs
.x
.bx
!= 0);
989 mouse_released (b
, xp
, yp
)
994 if (b
>= mouse_button_count
)
997 regs
.x
.bx
= mouse_button_translate
[b
];
998 int86 (0x33, ®s
, ®s
);
1000 *xp
= regs
.x
.cx
/ 8, *yp
= regs
.x
.dx
/ 8;
1001 return (regs
.x
.bx
!= 0);
1005 mouse_get_pos (f
, bar_window
, part
, x
, y
, time
)
1007 Lisp_Object
*bar_window
, *x
, *y
;
1008 enum scroll_bar_part
*part
;
1009 unsigned long *time
;
1015 int86 (0x33, ®s
, ®s
);
1016 *f
= selected_frame
;
1018 gettimeofday (&tv
, NULL
);
1019 *x
= make_number (regs
.x
.cx
);
1020 *y
= make_number (regs
.x
.dx
);
1026 mouse_check_moved ()
1031 int86 (0x33, ®s
, ®s
);
1032 if (regs
.x
.cx
!= mouse_last_x
|| regs
.x
.dx
!= mouse_last_y
)
1035 mouse_last_x
= regs
.x
.cx
;
1036 mouse_last_y
= regs
.x
.dx
;
1047 int86 (0x33, ®s
, ®s
);
1048 present
= internal_terminal
&& (regs
.x
.ax
& 0xffff) == 0xffff;
1053 mouse_button_count
= 3;
1054 mouse_button_translate
[0] = 0; /* Left */
1055 mouse_button_translate
[1] = 2; /* Middle */
1056 mouse_button_translate
[2] = 1; /* Right */
1060 mouse_button_count
= 2;
1061 mouse_button_translate
[0] = 0;
1062 mouse_button_translate
[1] = 1;
1064 mouse_position_hook
= &mouse_get_pos
;