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
= Vprocess_environment
;
523 int i
, len
= XFASTINT (Flength (lst
));
525 envv
= alloca ((len
+ 1) * sizeof (char *));
526 for (i
= 0; i
< len
; i
++)
530 CHECK_STRING (tmp
, 0);
531 envv
[i
] = alloca (XSTRING (tmp
)->size
+ 1);
532 strcpy (envv
[i
], XSTRING (tmp
)->data
);
534 envv
[len
] = (char *) 0;
537 if (XTYPE (dir
) == Lisp_String
)
538 chdir (XSTRING (dir
)->data
);
542 if (inbak
< 0 || outbak
< 0 || errbak
< 0)
543 goto done
; /* Allocation might fail due to lack of descriptors. */
548 result
= spawnve (P_WAIT
, argv
[0], argv
, envv
);
572 fprintf (stderr
, "%s not yet implemented\r\n", badfunc
);
577 /* A list of unimplemented functions that we silently ignore. */
578 unsigned alarm (s
) unsigned s
; {}
579 fork () { return 0; }
580 int kill (x
, y
) int x
, y
; { return -1; }
582 void volatile pause () {}
584 setpgrp () {return 0; }
585 setpriority (x
,y
,z
) int x
,y
,z
; { return 0; }
586 sigsetmask (x
) int x
; { return 0; }
587 unrequest_sigio () {}
597 int len
= strlen (path
);
598 char *tmp
= (char *) alloca (len
+ 1);
599 /* Gotta do this extern here due to the corresponding #define: */
602 if (*path
&& path
[1] == ':' && (getdisk () != tolower (path
[0]) - 'a'))
603 setdisk (tolower (path
[0]) - 'a');
606 if (strcmp (path
, "/") && strcmp (path
+ 1, ":/") && (path
[len
- 1] == '/'))
611 /* Sleep SECS. If KBDOK also return immediately if a key is pressed. */
613 sleep_or_kbd_hit (secs
, kbdok
)
619 gettimeofday (&t
, NULL
);
620 clnow
= t
.tv_sec
* 100 + t
.tv_usec
/ 10000;
621 clthen
= clnow
+ (100 * secs
);
625 gettimeofday (&t
, NULL
);
626 clnow
= t
.tv_sec
* 100 + t
.tv_usec
/ 10000;
627 if (kbdok
&& detect_input_pending ())
630 while (clnow
< clthen
);
633 /* Define a lot of environment variables if not already defined. Don't
634 remove anything unless you know what you're doing -- lots of code will
635 break if one or more of these are missing. */
637 init_environment (argc
, argv
, skip_args
)
644 /* We default HOME to the directory from which Emacs was started, but with
645 a "/bin" suffix removed. */
647 t
= alloca (strlen (s
) + 1);
650 while (s
!= t
&& *s
!= '/' && *s
!= ':') s
--;
652 t
= "c:/emacs"; /* When run under debug32. */
657 if (s
- 4 >= t
&& strcmp (s
- 4, "/bin") == 0)
658 s
[strlen (s
) - 4] = 0;
660 setenv ("HOME", t
, 0);
662 /* We set EMACSPATH to ~/bin (expanded) */
664 t
= strcpy (alloca (strlen (s
) + 6), s
);
665 if (s
[strlen (s
) - 1] != '/') strcat (t
, "/");
667 setenv ("EMACSPATH", t
, 0);
669 /* I don't expect anybody to ever use other terminals so the internal
670 terminal is the default. */
671 setenv ("TERM", "internal", 0);
673 /* SHELL is a bit tricky -- COMSPEC is the closest we come, but we must
674 downcase it and mirror the backslashes. */
675 s
= getenv ("COMSPEC");
676 if (!s
) s
= "c:/command.com";
677 t
= alloca (strlen (s
) + 1);
680 dostounix_filename (t
);
681 setenv ("SHELL", t
, 0);
683 /* PATH is also downcased and backslashes mirrored. */
686 t
= alloca (strlen (s
) + 3);
687 /* Current directory is always considered part of MsDos's path but it is
688 not normally mentioned. Now it is. */
689 strcat (strcpy (t
, ".;"), s
);
691 dostounix_filename (t
); /* Not a single file name, but this should work. */
692 setenv ("PATH", t
, 1);
694 /* In some sense all dos users have root privileges, so... */
695 setenv ("USER", "root", 0);
696 setenv ("NAME", getenv ("USER"), 0);
698 /* Time zone determined from country code. To make this possible, the
699 country code may not span more than one time zone. In other words,
700 in the USA, you lose. */
701 switch (dos_country_code
)
703 case 31: /* Belgium */
704 case 32: /* The Netherlands */
705 case 33: /* France */
707 case 36: /* Hungary */
708 case 38: /* Yugoslavia (or what's left of it?) */
710 case 41: /* Switzerland */
711 case 42: /* Tjekia */
712 case 45: /* Denmark */
713 case 46: /* Sweden */
714 case 47: /* Norway */
715 case 48: /* Poland */
716 case 49: /* Germany */
717 /* Daylight saving from last Sunday in March to last Sunday in
718 September, both at 2AM. */
719 setenv ("TZ", "MET-01METDST-02,M3.5.0/02:00,M9.5.0/02:00", 0);
721 case 44: /* United Kingdom */
722 case 351: /* Portugal */
723 case 354: /* Iceland */
724 setenv ("TZ", "GMT+00", 0);
728 setenv ("TZ", "???-09", 0);
730 case 90: /* Turkey */
731 case 358: /* Finland */
732 case 972: /* Israel */
733 setenv ("TZ", "EET-02", 0);
739 /* Flash the screen as a substitute for BEEPs. */
741 static unsigned char _xorattr
;
744 visible_bell (xorattr
)
745 unsigned char xorattr
;
754 movl _ScreenPrimary,%eax
785 static int internal_terminal
= 0;
797 if (internal_terminal
&& f
== stdout
)
799 if (have_mouse
) Mouse_off ();
801 count
= stdout
->_ptr
- stdout
->_base
;
815 ScreenAttrib
= *cp
++;
819 visible_bell (*cp
++);
828 for (i
= ScreenCols () - 1; i
>= x
; i
--)
829 ScreenPutChar (' ', ScreenAttrib
, i
, y
);
841 ScreenAttrib
^= *cp
++;
861 ScreenPutChar (c
, ScreenAttrib
, x
++, y
);
866 ScreenSetCursor (y
, x
);
867 if (have_mouse
) Mouse_on ();
870 /* This is a call to the original fflush. */
874 /* Do we need the internal terminal? */
876 internal_terminal_init ()
878 char *term
= getenv ("TERM");
881 = (!noninteractive
) && term
&& !strcmp (term
, "internal");
884 /* These must be global. */
885 static _go32_dpmi_seginfo ctrl_break_vector
;
886 static _go32_dpmi_registers ctrl_break_regs
;
887 static int ctrlbreakinstalled
= 0;
889 /* Interrupt level detection of Ctrl-Break. Don't do anything fancy here! */
891 ctrl_break_func (regs
)
892 _go32_dpmi_registers
*regs
;
898 install_ctrl_break_check ()
900 if (!ctrlbreakinstalled
)
902 /* Don't press Ctrl-Break if you don't have either DPMI or Emacs
903 was compiler with Djgpp 1.11 maintenance level 2 or later! */
904 ctrlbreakinstalled
= 1;
905 ctrl_break_vector
.pm_offset
= (int) ctrl_break_func
;
906 _go32_dpmi_allocate_real_mode_callback_iret (&ctrl_break_vector
,
908 _go32_dpmi_set_real_mode_interrupt_vector (0x1b, &ctrl_break_vector
);
913 /* Mouse routines under devellopment follow. Coordinates are in screen
914 positions and zero based. Mouse buttons are numbered from left to
915 right and also zero based. */
917 static int mouse_button_translate
[NUM_MOUSE_BUTTONS
];
918 static int mouse_button_count
;
927 regs
.x
.dx
= 8 * (ScreenCols () - 1);
928 int86 (0x33, ®s
, ®s
);
932 regs
.x
.dx
= 8 * (ScreenRows () - 1);
933 int86 (0x33, ®s
, ®s
);
935 mouse_moveto (ScreenCols () - 1, ScreenRows () - 1);
945 int86 (0x33, ®s
, ®s
);
954 int86 (0x33, ®s
, ®s
);
964 mouse_last_x
= regs
.x
.cx
= x
* 8;
965 mouse_last_y
= regs
.x
.dx
= y
* 8;
966 int86 (0x33, ®s
, ®s
);
970 mouse_pressed (b
, xp
, yp
)
975 if (b
>= mouse_button_count
)
978 regs
.x
.bx
= mouse_button_translate
[b
];
979 int86 (0x33, ®s
, ®s
);
981 *xp
= regs
.x
.cx
/ 8, *yp
= regs
.x
.dx
/8;
982 return (regs
.x
.bx
!= 0);
986 mouse_released (b
, xp
, yp
)
991 if (b
>= mouse_button_count
)
994 regs
.x
.bx
= mouse_button_translate
[b
];
995 int86 (0x33, ®s
, ®s
);
997 *xp
= regs
.x
.cx
/ 8, *yp
= regs
.x
.dx
/ 8;
998 return (regs
.x
.bx
!= 0);
1002 mouse_get_pos (f
, bar_window
, part
, x
, y
, time
)
1004 Lisp_Object
*bar_window
, *x
, *y
;
1005 enum scroll_bar_part
*part
;
1006 unsigned long *time
;
1012 int86 (0x33, ®s
, ®s
);
1013 *f
= selected_frame
;
1015 gettimeofday (&tv
, NULL
);
1016 *x
= make_number (regs
.x
.cx
/ 8);
1017 *y
= make_number (regs
.x
.dx
/ 8);
1023 mouse_check_moved ()
1028 int86 (0x33, ®s
, ®s
);
1029 if (regs
.x
.cx
!= mouse_last_x
|| regs
.x
.dx
!= mouse_last_y
)
1032 mouse_last_x
= regs
.x
.cx
;
1033 mouse_last_y
= regs
.x
.dx
;
1044 int86 (0x33, ®s
, ®s
);
1045 present
= internal_terminal
&& (regs
.x
.ax
& 0xffff) == 0xffff;
1050 mouse_button_count
= 3;
1051 mouse_button_translate
[0] = 0; /* Left */
1052 mouse_button_translate
[1] = 2; /* Middle */
1053 mouse_button_translate
[2] = 1; /* Right */
1057 mouse_button_count
= 2;
1058 mouse_button_translate
[0] = 0;
1059 mouse_button_translate
[1] = 1;
1061 mouse_position_hook
= &mouse_get_pos
;