Put stdio.h, sys/types.h and sys/stat.h after config.h.
[bpt/emacs.git] / src / msdos.c
1 /* MS-DOS specific C utilities.
2 Copyright (C) 1993, 1994 Free Software Foundation, Inc.
3
4 This file is part of GNU Emacs.
5
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)
9 any later version.
10
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.
15
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. */
19
20 /* Contributed by Morten Welinder */
21
22 /* Note: some of the stuff here was taken from end of sysdep.c in demacs. */
23
24 #include <config.h>
25
26 #ifdef MSDOS
27 #include "lisp.h"
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <sys/param.h>
31 #include <sys/time.h>
32 #include <dos.h>
33 #include "dosfns.h"
34 #include "msdos.h"
35 #include "systime.h"
36 #include "termhooks.h"
37 #include "frame.h"
38 #include <go32.h>
39 #include <pc.h>
40 #include <ctype.h>
41 /* #include <process.h> */
42 /* Damn that local process.h! Instead we can define P_WAIT ourselves. */
43 #define P_WAIT 1
44
45 static int break_stat; /* BREAK check mode status. */
46 static int stdin_stat; /* stdin IOCTL status. */
47 static int extended_kbd; /* 101 (102) keyboard present. */
48
49 int have_mouse; /* Mouse present? */
50 static int mouse_last_x;
51 static int mouse_last_y;
52
53 /* Turn off Dos' Ctrl-C checking and inhibit interpretation of control chars
54 by Dos. Determine the keyboard type. */
55 int
56 dos_ttraw ()
57 {
58 union REGS inregs, outregs;
59
60 inregs.h.ah = 0xc0;
61 int86 (0x15, &inregs, &outregs);
62 extended_kbd = (!outregs.x.cflag) && (outregs.h.ah == 0);
63
64 break_stat = getcbrk ();
65 setcbrk (0);
66 install_ctrl_break_check ();
67 have_mouse = mouse_init1 ();
68
69 inregs.x.ax = 0x4400; /* Get IOCTL status. */
70 inregs.x.bx = 0x00; /* 0 = stdin. */
71 intdos (&inregs, &outregs);
72 stdin_stat = outregs.h.dl;
73
74 inregs.x.dx = (outregs.x.dx | 0x0020) & 0x0027; /* raw mode */
75 inregs.h.al = 0x01;
76 intdos (&inregs, &outregs);
77 return !outregs.x.cflag;
78 }
79
80 /* Restore status of standard input and Ctrl-C checking. */
81 int
82 dos_ttcooked ()
83 {
84 union REGS inregs, outregs;
85
86 setcbrk (break_stat);
87 if (have_mouse) mouse_off ();
88
89 inregs.x.ax = 0x4401; /* Set IOCTL status. */
90 inregs.x.bx = 0x00; /* 0 = stdin. */
91 inregs.x.dx = stdin_stat;
92 intdos (&inregs, &outregs);
93 return !outregs.x.cflag;
94 }
95
96 static unsigned short
97 ibmpc_translate_map[] =
98 {
99 /* --------------- 00 to 0f --------------- */
100 0, /* Ctrl Break */
101 0xff1b, /* Escape */
102 0xffb1, /* Keypad 1 */
103 0xffb2, /* Keypad 2 */
104 0xffb3, /* Keypad 3 */
105 0xffb4, /* Keypad 4 */
106 0xffb5, /* Keypad 5 */
107 0xffb6, /* Keypad 6 */
108 0xffb7, /* Keypad 7 */
109 0xffb8, /* Keypad 8 */
110 0xffb9, /* Keypad 9 */
111 0xffb0, /* Keypad 0 */
112 '-', '=',
113 0xff08, /* Backspace */
114 0xff74, /* (Shift) Tab [Tab doesn't use this table] */
115
116 /* --------------- 10 to 1f --------------- */
117 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']',
118 0xff8d, /* Keypad Enter */
119 0, /* Ctrl */
120 'a', 's',
121
122 /* --------------- 20 to 2f --------------- */
123 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`',
124 0, /* Left shift */
125 '\\', 'z', 'x', 'c', 'v',
126
127 /* --------------- 30 to 3f --------------- */
128 'b', 'n', 'm', ',', '.',
129 0xffaf, /* Grey / */
130 0, /* Right shift */
131 0xffaa, /* Grey * */
132 0, /* Alt */
133 ' ',
134 0, /* Caps Lock */
135 0xffbe, /* F1 */
136 0xffbf, /* F2 */
137 0xffc0, /* F3 */
138 0xffc1, /* F4 */
139 0xffc2, /* F5 */
140
141 /* --------------- 40 to 4f --------------- */
142 0xffc3, /* F6 */
143 0xffc4, /* F7 */
144 0xffc5, /* F8 */
145 0xffc6, /* F9 */
146 0xffc7, /* F10 */
147 0, /* Num Lock */
148 0, /* Scroll Lock */
149 0xff50, /* Home */
150 0xff52, /* Up */
151 0xff55, /* Page Up */
152 0xffad, /* Grey - */
153 0xff51, /* Left */
154 0xffb5, /* Keypad 5 */
155 0xff53, /* Right */
156 0xffab, /* Grey + */
157 0xff57, /* End */
158
159 /* --------------- 50 to 5f --------------- */
160 0xff54, /* Down */
161 0xff56, /* Page Down */
162 0xff63, /* Insert */
163 0xffff, /* Delete */
164 0xffbe, /* (Shift) F1 */
165 0xffbf, /* (Shift) F2 */
166 0xffc0, /* (Shift) F3 */
167 0xffc1, /* (Shift) F4 */
168 0xffc2, /* (Shift) F5 */
169 0xffc3, /* (Shift) F6 */
170 0xffc4, /* (Shift) F7 */
171 0xffc5, /* (Shift) F8 */
172 0xffc6, /* (Shift) F9 */
173 0xffc7, /* (Shift) F10 */
174 0xffbe, /* (Ctrl) F1 */
175 0xffbf, /* (Ctrl) F2 */
176
177 /* --------------- 60 to 6f --------------- */
178 0xffc0, /* (Ctrl) F3 */
179 0xffc1, /* (Ctrl) F4 */
180 0xffc2, /* (Ctrl) F5 */
181 0xffc3, /* (Ctrl) F6 */
182 0xffc4, /* (Ctrl) F7 */
183 0xffc5, /* (Ctrl) F8 */
184 0xffc6, /* (Ctrl) F9 */
185 0xffc7, /* (Ctrl) F10 */
186 0xffbe, /* (Alt) F1 */
187 0xffbf, /* (Alt) F2 */
188 0xffc0, /* (Alt) F3 */
189 0xffc1, /* (Alt) F4 */
190 0xffc2, /* (Alt) F5 */
191 0xffc3, /* (Alt) F6 */
192 0xffc4, /* (Alt) F7 */
193 0xffc5, /* (Alt) F8 */
194
195 /* --------------- 70 to 7f --------------- */
196 0xffc6, /* (Alt) F9 */
197 0xffc7, /* (Alt) F10 */
198 0xff6d, /* (Ctrl) Sys Rq */
199 0xff51, /* (Ctrl) Left */
200 0xff53, /* (Ctrl) Right */
201 0xff57, /* (Ctrl) End */
202 0xff56, /* (Ctrl) Page Down */
203 0xff50, /* (Ctrl) Home */
204 '1', '2', '3', '4', '5', '6', '7', '8', /* (Alt) */
205
206 /* --------------- 80 to 8f --------------- */
207 '9', '0', '-', '=', /* (Alt) */
208 0xff55, /* (Ctrl) Page Up */
209 0xffc8, /* F11 */
210 0xffc9, /* F12 */
211 0xffc8, /* (Shift) F11 */
212 0xffc9, /* (Shift) F12 */
213 0xffc8, /* (Ctrl) F11 */
214 0xffc9, /* (Ctrl) F12 */
215 0xffc8, /* (Alt) F11 */
216 0xffc9, /* (Alt) F12 */
217 0xff52, /* (Ctrl) Up */
218 0xffae, /* (Ctrl) Grey - */
219 0xffb5, /* (Ctrl) Keypad 5 */
220
221 /* --------------- 90 to 9f --------------- */
222 0xffab, /* (Ctrl) Grey + */
223 0xff54, /* (Ctrl) Down */
224 0xff63, /* (Ctrl) Insert */
225 0xffff, /* (Ctrl) Delete */
226 0xff09, /* (Ctrl) Tab */
227 0xffaf, /* (Ctrl) Grey / */
228 0xffaa, /* (Ctrl) Grey * */
229 0xff50, /* (Alt) Home */
230 0xff52, /* (Alt) Up */
231 0xff55, /* (Alt) Page Up */
232 0, /* NO KEY */
233 0xff51, /* (Alt) Left */
234 0, /* NO KEY */
235 0xff53, /* (Alt) Right */
236 0, /* NO KEY */
237 0xff57, /* (Alt) End */
238
239 /* --------------- a0 to af --------------- */
240 0xff54, /* (Alt) Down */
241 0xff56, /* (Alt) Page Down */
242 0xff63, /* (Alt) Insert */
243 0xffff, /* (Alt) Delete */
244 0xffaf, /* (Alt) Grey / */
245 0xff09, /* (Alt) Tab */
246 0xff0d /* (Alt) Enter */
247 };
248
249 /* Get a char from keyboard. Function keys are put into the event queue. */
250 static int
251 dos_rawgetc ()
252 {
253 struct input_event event;
254 struct timeval tv;
255 union REGS regs;
256 int ctrl_p, alt_p, shift_p;
257
258 /* Calculate modifier bits */
259 regs.h.ah = extended_kbd ? 0x12 : 0x02;
260 int86 (0x16, &regs, &regs);
261 ctrl_p = ((regs.h.al & 4) != 0);
262 shift_p = ((regs.h.al & 3) != 0);
263 /* Please be very careful here not to break international keyboard support.
264 When Keyb.Com is loaded, the key marked `Alt Gr' is used for accessing
265 characters like { and } if their positions are overlaid. */
266 alt_p = ((extended_kbd ? (regs.h.ah & 2) : (regs.h.al & 8)) != 0);
267
268 while (kbhit ())
269 {
270 union REGS regs;
271 register unsigned char c;
272 int sc, code;
273
274 regs.h.ah = extended_kbd ? 0x10 : 0x00;
275 int86 (0x16, &regs, &regs);
276 c = regs.h.al;
277 sc = regs.h.ah;
278
279 /* Determine from the scan code if a keypad key was pressed. */
280 if (c >= '0' && c <= '9' && sc > 0xb)
281 sc = (c == '0') ? 0xb : (c - '0' + 1), c = 0;
282 else if (sc == 0x53 && c != 0xe0)
283 {
284 code = 0xffae; /* Keypad decimal point/comma. */
285 goto nonascii;
286 }
287 else if (sc == 0xe0)
288 {
289 switch (c)
290 {
291 case 10: /* Ctrl Enter */
292 case 13:
293 sc = 0x1c;
294 break;
295 case '/':
296 sc = 0x35;
297 break;
298 default:
299 sc = 0;
300 };
301 c = 0;
302 }
303
304 if (c == 0
305 || c == ' '
306 || alt_p
307 || (ctrl_p && shift_p)
308 || (c == 0xe0 && sc != 0) /* Pseudo-key */
309 || sc == 0x37 /* Grey * */
310 || sc == 0x4a /* Grey - */
311 || sc == 0x4e /* Grey + */
312 || sc == 0x0e) /* Back space *key*, not Ctrl-h */
313 {
314 if (sc >= (sizeof (ibmpc_translate_map) / sizeof (short)))
315 code = 0;
316 else
317 code = ibmpc_translate_map[sc];
318 if (code != 0)
319 {
320 if (code >= 0x100)
321 {
322 nonascii:
323 event.kind = non_ascii_keystroke;
324 event.code = (code & 0xff) + 0xff00;
325 }
326 else
327 {
328 /* Don't return S- if we don't have to. `shifted' is
329 supposed to be the shifted versions of the characters
330 in `unshifted'. Unfortunately, this is only true for
331 US keyboard layout. If anyone knows how to do this
332 right, please tell us. */
333 static char *unshifted
334 = "abcdefghijklmnopqrstuvwxyz,./=;[\\]'-`0123456789";
335 static char *shifted
336 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ<>?+:{|}\"_~)!@#$%^&*(";
337 char *pos;
338
339 if (shift_p && (pos = strchr (unshifted, code)))
340 {
341 c = shifted[pos - unshifted];
342 shift_p = 0;
343 }
344 else
345 if (c == 0) c = code;
346 event.kind = ascii_keystroke;
347 event.code = c;
348 }
349 event.modifiers
350 = (shift_p ? shift_modifier : 0)
351 + (ctrl_p ? ctrl_modifier : 0)
352 + (alt_p ? meta_modifier : 0);
353 /* EMACS == Enter Meta Alt Control Shift */
354 event.frame_or_window = selected_frame;
355 gettimeofday (&tv, NULL);
356 event.timestamp = tv.tv_usec;
357 kbd_buffer_store_event (&event);
358 }
359 } else
360 return c;
361 }
362
363 if (have_mouse)
364 {
365 int but, press, x, y, ok;
366
367 /* Check for mouse movement *before* buttons. */
368 mouse_check_moved ();
369
370 for (but = 0; but < NUM_MOUSE_BUTTONS; but++)
371 for (press = 0; press < 2; press++)
372 {
373 if (press)
374 ok = mouse_pressed (but, &x, &y);
375 else
376 ok = mouse_released (but, &x, &y);
377 if (ok)
378 {
379 event.kind = mouse_click;
380 event.code = but;
381 event.modifiers
382 = (shift_p ? shift_modifier : 0)
383 + (ctrl_p ? ctrl_modifier : 0)
384 + (alt_p ? meta_modifier : 0)
385 + (press ? down_modifier : up_modifier);
386 event.x = x;
387 event.y = y;
388 event.frame_or_window = selected_frame;
389 gettimeofday (&tv, NULL);
390 event.timestamp = tv.tv_usec;
391 kbd_buffer_store_event (&event);
392 }
393 }
394 }
395
396 return -1;
397 }
398
399 static int prev_get_char = -1;
400
401 /* Return 1 if a key is ready to be read without suspending execution. */
402 dos_keysns ()
403 {
404 if (prev_get_char != -1)
405 return 1;
406 else
407 return ((prev_get_char = dos_rawgetc ()) != -1);
408 }
409
410 /* Read a key. Return -1 if no key is ready. */
411 dos_keyread ()
412 {
413 if (prev_get_char != -1)
414 {
415 int c = prev_get_char;
416 prev_get_char = -1;
417 return c;
418 }
419 else
420 return dos_rawgetc ();
421 }
422
423 /* Hostnames for a pc are not really funny, but they are used in change log
424 so we emulate the best we can. */
425 gethostname (p, size)
426 char *p;
427 int size;
428 {
429 char *q = egetenv ("HOSTNAME");
430
431 if (!q) q = "pc";
432 strcpy (p, q);
433 return 0;
434 }
435
436 /* Destructively turn backslashes into slashes. */
437 void
438 dostounix_filename (p)
439 register char *p;
440 {
441 while (*p)
442 {
443 if (*p == '\\')
444 *p = '/';
445 p++;
446 }
447 }
448
449 /* Destructively turn slashes into backslashes. */
450 void
451 unixtodos_filename (p)
452 register char *p;
453 {
454 while (*p)
455 {
456 if (*p == '/')
457 *p = '\\';
458 p++;
459 }
460 }
461
462 /* Get the default directory for a given drive. 0=def, 1=A, 2=B, ... */
463 int
464 getdefdir (drive, dst)
465 int drive;
466 char *dst;
467 {
468 union REGS regs;
469
470 *dst++ = '/';
471 regs.h.dl = drive;
472 regs.x.si = (int) dst;
473 regs.h.ah = 0x47;
474 intdos (&regs, &regs);
475 return !regs.x.cflag;
476 }
477
478 /* Remove all CR's that are followed by a LF. */
479 int
480 crlf_to_lf (n, buf)
481 register int n;
482 register unsigned char *buf;
483 {
484 unsigned char *np = buf;
485 unsigned char *startp = buf;
486 unsigned char *endp = buf + n;
487 unsigned char c;
488
489 if (n == 0)
490 return n;
491 while (buf < endp - 1)
492 {
493 if (*buf == 0x0d)
494 {
495 if (*(++buf) != 0x0a)
496 *np++ = 0x0d;
497 }
498 else
499 *np++ = *buf++;
500 }
501 if (buf < endp)
502 *np++ = *buf++;
503 return np - startp;
504 }
505
506
507 /* Run command as specified by ARGV in directory DIR.
508 The command is run with input from TEMPIN and output to file TEMPOUT. */
509 int
510 run_msdos_command (argv, dir, tempin, tempout)
511 unsigned char **argv;
512 Lisp_Object dir;
513 int tempin, tempout;
514 {
515 char *saveargv1, *saveargv2, **envv;
516 char oldwd[MAXPATHLEN + 1]; /* Fixed size is safe on MSDOS. */
517 int msshell, result = -1;
518 int in, out, inbak, outbak, errbak;
519 Lisp_Object cmd;
520
521 /* Get current directory as MSDOS cwd is not per-process. */
522 getwd (oldwd);
523
524 cmd = Ffile_name_nondirectory (build_string (argv[0]));
525 msshell = !NILP (Fmember (cmd, Fsymbol_value (intern ("msdos-shells"))))
526 && !strcmp ("-c", argv[1]);
527 if (msshell)
528 {
529 saveargv1 = argv[1];
530 saveargv2 = argv[2];
531 argv[1] = "/c";
532 if (argv[2])
533 {
534 char *p = alloca (strlen (argv[2]) + 1);
535
536 strcpy (argv[2] = p, saveargv2);
537 while (*p && isspace (*p))
538 p++;
539 while (*p && !isspace (*p))
540 if (*p == '/')
541 *p++ = '\\';
542 else
543 p++;
544 }
545 }
546
547 /* Build the environment array. */
548 {
549 extern Lisp_Object Vprocess_environment;
550 Lisp_Object tmp, lst;
551 int i, len;
552
553 lst = Vprocess_environment;
554 len = XFASTINT (Flength (lst));
555
556 envv = alloca ((len + 1) * sizeof (char *));
557 for (i = 0; i < len; i++)
558 {
559 tmp = Fcar (lst);
560 lst = Fcdr (lst);
561 CHECK_STRING (tmp, 0);
562 envv[i] = alloca (XSTRING (tmp)->size + 1);
563 strcpy (envv[i], XSTRING (tmp)->data);
564 }
565 envv[len] = (char *) 0;
566 }
567
568 if (XTYPE (dir) == Lisp_String)
569 chdir (XSTRING (dir)->data);
570 inbak = dup (0);
571 outbak = dup (1);
572 errbak = dup (2);
573 if (inbak < 0 || outbak < 0 || errbak < 0)
574 goto done; /* Allocation might fail due to lack of descriptors. */
575 dup2 (tempin, 0);
576 dup2 (tempout, 1);
577 dup2 (tempout, 2);
578 dos_ttcooked ();
579 result = spawnve (P_WAIT, argv[0], argv, envv);
580 dos_ttraw ();
581 dup2 (inbak, 0);
582 dup2 (outbak, 1);
583 dup2 (errbak, 2);
584
585 done:
586 chdir (oldwd);
587 if (msshell)
588 {
589 argv[1] = saveargv1;
590 argv[2] = saveargv2;
591 }
592 return result;
593 }
594
595
596 croak (badfunc)
597 char *badfunc;
598 {
599 fprintf (stderr, "%s not yet implemented\r\n", badfunc);
600 reset_sys_modes ();
601 exit (1);
602 }
603
604 /* A list of unimplemented functions that we silently ignore. */
605 unsigned alarm (s) unsigned s; {}
606 fork () { return 0; }
607 int kill (x, y) int x, y; { return -1; }
608 nice (p) int p; {}
609 void volatile pause () {}
610 request_sigio () {}
611 setpgrp () {return 0; }
612 setpriority (x,y,z) int x,y,z; { return 0; }
613 sigsetmask (x) int x; { return 0; }
614 unrequest_sigio () {}
615
616 #ifdef chdir
617 #undef chdir
618 #endif
619
620 int
621 sys_chdir (path)
622 const char* path;
623 {
624 int len = strlen (path);
625 char *tmp = (char *) alloca (len + 1);
626 /* Gotta do this extern here due to the corresponding #define: */
627 extern int chdir ();
628
629 if (*path && path[1] == ':' && (getdisk () != tolower (path[0]) - 'a'))
630 setdisk (tolower (path[0]) - 'a');
631
632 strcpy (tmp, path);
633 if (strcmp (path, "/") && strcmp (path + 1, ":/") && (path[len - 1] == '/'))
634 tmp[len - 1] = 0;
635 return chdir (tmp);
636 }
637
638 /* Sleep SECS. If KBDOK also return immediately if a key is pressed. */
639 void
640 sleep_or_kbd_hit (secs, kbdok)
641 int secs, kbdok;
642 {
643 long clnow, clthen;
644 struct timeval t;
645
646 gettimeofday (&t, NULL);
647 clnow = t.tv_sec * 100 + t.tv_usec / 10000;
648 clthen = clnow + (100 * secs);
649
650 do
651 {
652 gettimeofday (&t, NULL);
653 clnow = t.tv_sec * 100 + t.tv_usec / 10000;
654 if (kbdok && detect_input_pending ())
655 return;
656 }
657 while (clnow < clthen);
658 }
659
660 /* The Emacs root directory as determined by init_environment. */
661 static char emacsroot[MAXPATHLEN];
662
663 char *
664 rootrelativepath (rel)
665 char *rel;
666 {
667 static char result[MAXPATHLEN + 10];
668
669 strcpy (result, emacsroot);
670 strcat (result, "/");
671 strcat (result, rel);
672 return result;
673 }
674
675 /* Define a lot of environment variables if not already defined. Don't
676 remove anything unless you know what you're doing -- lots of code will
677 break if one or more of these are missing. */
678 void
679 init_environment (argc, argv, skip_args)
680 int argc;
681 char **argv;
682 int skip_args;
683 {
684 char *s, *t, *root;
685 int len;
686
687 /* Find our root from argv[0]. Assuming argv[0] is, say,
688 "c:/emacs/bin/emacs.exe" our root will be "c:/emacs". */
689 len = strlen (argv[0]);
690 root = alloca (len + 10); /* A little extra space for the stuff below. */
691 strcpy (root, argv[0]);
692 while (len > 0 && root[len] != '/' && root[len] != ':')
693 len--;
694 root[len] = '\0';
695 if (len > 4 && strcmp (root + len - 4, "/bin") == 0)
696 root[len - 4] = '\0';
697 else
698 strcpy (root, "c:/emacs"); /* Only under debuggers, I think. */
699 len = strlen (root);
700 strcpy (emacsroot, root);
701
702 /* We default HOME to our root. */
703 setenv ("HOME", root, 0);
704
705 /* We default EMACSPATH to root + "/bin". */
706 strcpy (root + len, "/bin");
707 setenv ("EMACSPATH", root, 0);
708
709 /* I don't expect anybody to ever use other terminals so the internal
710 terminal is the default. */
711 setenv ("TERM", "internal", 0);
712
713 /* SHELL is a bit tricky -- COMSPEC is the closest we come, but we must
714 downcase it and mirror the backslashes. */
715 s = getenv ("COMSPEC");
716 if (!s) s = "c:/command.com";
717 t = alloca (strlen (s) + 1);
718 strcpy (t, s);
719 strlwr (t);
720 dostounix_filename (t);
721 setenv ("SHELL", t, 0);
722
723 /* PATH is also downcased and backslashes mirrored. */
724 s = getenv ("PATH");
725 if (!s) s = "";
726 t = alloca (strlen (s) + 3);
727 /* Current directory is always considered part of MsDos's path but it is
728 not normally mentioned. Now it is. */
729 strcat (strcpy (t, ".;"), s);
730 strlwr (t);
731 dostounix_filename (t); /* Not a single file name, but this should work. */
732 setenv ("PATH", t, 1);
733
734 /* In some sense all dos users have root privileges, so... */
735 setenv ("USER", "root", 0);
736 setenv ("NAME", getenv ("USER"), 0);
737
738 /* Time zone determined from country code. To make this possible, the
739 country code may not span more than one time zone. In other words,
740 in the USA, you lose. */
741 switch (dos_country_code)
742 {
743 case 31: /* Belgium */
744 case 32: /* The Netherlands */
745 case 33: /* France */
746 case 34: /* Spain */
747 case 36: /* Hungary */
748 case 38: /* Yugoslavia (or what's left of it?) */
749 case 39: /* Italy */
750 case 41: /* Switzerland */
751 case 42: /* Tjekia */
752 case 45: /* Denmark */
753 case 46: /* Sweden */
754 case 47: /* Norway */
755 case 48: /* Poland */
756 case 49: /* Germany */
757 /* Daylight saving from last Sunday in March to last Sunday in
758 September, both at 2AM. */
759 setenv ("TZ", "MET-01METDST-02,M3.5.0/02:00,M9.5.0/02:00", 0);
760 break;
761 case 44: /* United Kingdom */
762 case 351: /* Portugal */
763 case 354: /* Iceland */
764 setenv ("TZ", "GMT+00", 0);
765 break;
766 case 81: /* Japan */
767 case 82: /* Korea */
768 setenv ("TZ", "???-09", 0);
769 break;
770 case 90: /* Turkey */
771 case 358: /* Finland */
772 case 972: /* Israel */
773 setenv ("TZ", "EET-02", 0);
774 break;
775 }
776 tzset ();
777 init_gettimeofday ();
778 }
779
780 /* Flash the screen as a substitute for BEEPs. */
781
782 static unsigned char _xorattr;
783
784 static void
785 do_visible_bell (xorattr)
786 unsigned char xorattr;
787 {
788 _xorattr = xorattr;
789 asm volatile
790 (" pushl %eax
791 pushl %ebx
792 pushl %ecx
793 pushl %edx
794 movl $1,%edx
795 visible_bell_0:
796 call _ScreenRows
797 pushl %eax
798 call _ScreenCols
799 pushl %eax
800 movl _ScreenPrimary,%eax
801 call dosmemsetup
802 movl %eax,%ebx
803 popl %ecx
804 popl %eax
805 imull %eax,%ecx
806 movb (__xorattr),%al
807 incl %ebx
808 visible_bell_1:
809 xorb %al,%gs:(%ebx)
810 addl $2,%ebx
811 decl %ecx
812 jne visible_bell_1
813 decl %edx
814 jne visible_bell_3
815 visible_bell_2:
816 movzwl %ax,%eax
817 movzwl %ax,%eax
818 movzwl %ax,%eax
819 movzwl %ax,%eax
820 decw %cx
821 jne visible_bell_2
822 jmp visible_bell_0
823 visible_bell_3:
824 popl %edx
825 popl %ecx
826 popl %ebx
827 popl %eax");
828 }
829
830 /* At screen position (X,Y), output C characters from string S with
831 attribute A. Do it fast! */
832
833 static void
834 output_string (x, y, s, c, a)
835 int x, y, c;
836 unsigned char *s;
837 unsigned char a;
838 {
839 char *t = (char *)ScreenPrimary + 2 * (x + ScreenCols () * y);
840 asm volatile
841 (" movl %1,%%eax
842 call dosmemsetup
843 movl %%eax,%%edi
844 movb %0,%%ah
845 movl %2,%%ecx
846 movl %3,%%esi
847 output_string1:
848 movb (%%esi),%%al
849 movw %%ax,%%gs:(%%edi)
850 addl $2,%%edi
851 incl %%esi
852 decl %%ecx
853 jne output_string1"
854 : /* no output */
855 : "m" (a), "g" (t), "g" (c), "g" (s)
856 : "%eax", "%ecx", /* "%gs",*/ "%esi", "%edi");
857 }
858
859 static int internal_terminal = 0;
860 #undef fflush
861
862 int
863 internal_flush (f)
864 FILE *f;
865 {
866 static char spaces[] = " ";
867 static int x;
868 static int y;
869 unsigned char *cp, *cp0;
870 int count, i, j;
871
872 if (internal_terminal && f == stdout)
873 {
874 if (have_mouse) mouse_off ();
875 cp = stdout->_base;
876 count = stdout->_ptr - stdout->_base;
877 while (count > 0)
878 {
879 switch (*cp++)
880 {
881 case 27:
882 switch (*cp++)
883 {
884 case '@':
885 y = *cp++;
886 x = *cp++;
887 count -= 4;
888 break;
889 case 'A':
890 ScreenAttrib = *cp++;
891 count -= 3;
892 break;
893 case 'B':
894 do_visible_bell (*cp++);
895 count -= 3;
896 break;
897 case 'C':
898 ScreenClear ();
899 x = y = 0;
900 count -= 2;
901 break;
902 case 'E':
903 i = ScreenCols () - x;
904 j = x;
905 while (i >= sizeof spaces)
906 {
907 output_string (j, y, spaces, sizeof spaces,
908 ScreenAttrib);
909 j += sizeof spaces;
910 i -= sizeof spaces;
911 }
912 if (i > 0)
913 output_string (j, y, spaces, i, ScreenAttrib);
914 count -= 2;
915 break;
916 case 'R':
917 x++;
918 count -= 2;
919 break;
920 case 'U':
921 y--;
922 count -= 2;
923 break;
924 case 'X':
925 ScreenAttrib ^= *cp++;
926 count -= 3;
927 break;
928 default:
929 count -= 2;
930 }
931 break;
932 case 7:
933 write (1, "\007", 1);
934 count--;
935 break;
936 case 8:
937 x--;
938 count--;
939 break;
940 case 13:
941 x = 0;
942 count--;
943 break;
944 case 10:
945 y++;
946 count--;
947 break;
948 default:
949 cp0 = cp - 1;
950 count--;
951 while (count > 0 && *cp >= ' ')
952 cp++, count--;
953 output_string (x, y, cp0, cp - cp0, ScreenAttrib);
954 x += (cp - cp0);
955 }
956 }
957 fpurge (stdout);
958 ScreenSetCursor (y, x);
959 if (have_mouse) mouse_on ();
960 }
961 else
962 /* This is a call to the original fflush. */
963 fflush (f);
964 }
965
966 /* Do we need the internal terminal? */
967 void
968 internal_terminal_init ()
969 {
970 char *term = getenv ("TERM");
971
972 internal_terminal
973 = (!noninteractive) && term && !strcmp (term, "internal");
974 }
975 \f
976 /* When time zones are set from Ms-Dos too may C-libraries are playing
977 tricks with time values. We solve this by defining our own version
978 of `gettimeofday' bypassing GO32. Our version needs to be initialized
979 once and after each call to `tzset' with TZ changed. */
980
981 static int daylight, gmtoffset;
982
983 int
984 gettimeofday (struct timeval *tp, struct timezone *tzp)
985 {
986 if (tp)
987 {
988 struct time t;
989 struct date d;
990 struct tm tmrec;
991
992 gettime (&t);
993 getdate (&d);
994 tmrec.tm_year = d.da_year - 1900;
995 tmrec.tm_mon = d.da_mon - 1;
996 tmrec.tm_mday = d.da_day;
997 tmrec.tm_hour = t.ti_hour;
998 tmrec.tm_min = t.ti_min;
999 tmrec.tm_sec = t.ti_sec;
1000 tmrec.tm_gmtoff = gmtoffset;
1001 tmrec.tm_isdst = daylight;
1002 tp->tv_sec = mktime (&tmrec);
1003 tp->tv_usec = t.ti_hund * (1000000 / 100);
1004 }
1005 if (tzp)
1006 {
1007 tzp->tz_minuteswest = gmtoffset;
1008 tzp->tz_dsttime = daylight;
1009 }
1010 return 0;
1011 }
1012
1013 void
1014 init_gettimeofday ()
1015 {
1016 time_t ltm, gtm;
1017 struct tm *lstm;
1018
1019 daylight = 0;
1020 gmtoffset = 0;
1021 ltm = gtm = time (NULL);
1022 ltm = mktime (lstm = localtime (&ltm));
1023 gtm = mktime (gmtime (&gtm));
1024 daylight = lstm->tm_isdst;
1025 gmtoffset = (int)(gtm - ltm) / 60;
1026 }
1027 \f
1028 /* These must be global. */
1029 static _go32_dpmi_seginfo ctrl_break_vector;
1030 static _go32_dpmi_registers ctrl_break_regs;
1031 static int ctrlbreakinstalled = 0;
1032
1033 /* Interrupt level detection of Ctrl-Break. Don't do anything fancy here! */
1034 void
1035 ctrl_break_func (regs)
1036 _go32_dpmi_registers *regs;
1037 {
1038 Vquit_flag = Qt;
1039 }
1040
1041 void
1042 install_ctrl_break_check ()
1043 {
1044 if (!ctrlbreakinstalled)
1045 {
1046 /* Don't press Ctrl-Break if you don't have either DPMI or Emacs
1047 was compiler with Djgpp 1.11 maintenance level 2 or later! */
1048 ctrlbreakinstalled = 1;
1049 ctrl_break_vector.pm_offset = (int) ctrl_break_func;
1050 _go32_dpmi_allocate_real_mode_callback_iret (&ctrl_break_vector,
1051 &ctrl_break_regs);
1052 _go32_dpmi_set_real_mode_interrupt_vector (0x1b, &ctrl_break_vector);
1053 }
1054 }
1055 \f
1056
1057 /* Mouse routines under devellopment follow. Coordinates are in screen
1058 positions and zero based. Mouse buttons are numbered from left to
1059 right and also zero based. */
1060
1061 static int mouse_button_translate[NUM_MOUSE_BUTTONS];
1062 static int mouse_button_count;
1063
1064 void
1065 mouse_init ()
1066 {
1067 union REGS regs;
1068
1069 regs.x.ax = 0x0007;
1070 regs.x.cx = 0;
1071 regs.x.dx = 8 * (ScreenCols () - 1);
1072 int86 (0x33, &regs, &regs);
1073
1074 regs.x.ax = 0x0008;
1075 regs.x.cx = 0;
1076 regs.x.dx = 8 * (ScreenRows () - 1);
1077 int86 (0x33, &regs, &regs);
1078
1079 mouse_moveto (ScreenCols () - 1, ScreenRows () - 1);
1080 mouse_on ();
1081 }
1082
1083 void
1084 mouse_on ()
1085 {
1086 union REGS regs;
1087
1088 regs.x.ax = 0x0001;
1089 int86 (0x33, &regs, &regs);
1090 }
1091
1092 void
1093 mouse_off ()
1094 {
1095 union REGS regs;
1096
1097 regs.x.ax = 0x0002;
1098 int86 (0x33, &regs, &regs);
1099 }
1100
1101 void
1102 mouse_moveto (x, y)
1103 int x, y;
1104 {
1105 union REGS regs;
1106
1107 regs.x.ax = 0x0004;
1108 mouse_last_x = regs.x.cx = x * 8;
1109 mouse_last_y = regs.x.dx = y * 8;
1110 int86 (0x33, &regs, &regs);
1111 }
1112
1113 int
1114 mouse_pressed (b, xp, yp)
1115 int b, *xp, *yp;
1116 {
1117 union REGS regs;
1118
1119 if (b >= mouse_button_count)
1120 return 0;
1121 regs.x.ax = 0x0005;
1122 regs.x.bx = mouse_button_translate[b];
1123 int86 (0x33, &regs, &regs);
1124 if (regs.x.bx)
1125 *xp = regs.x.cx / 8, *yp = regs.x.dx /8;
1126 return (regs.x.bx != 0);
1127 }
1128
1129 int
1130 mouse_released (b, xp, yp)
1131 int b, *xp, *yp;
1132 {
1133 union REGS regs;
1134
1135 if (b >= mouse_button_count)
1136 return 0;
1137 regs.x.ax = 0x0006;
1138 regs.x.bx = mouse_button_translate[b];
1139 int86 (0x33, &regs, &regs);
1140 if (regs.x.bx)
1141 *xp = regs.x.cx / 8, *yp = regs.x.dx / 8;
1142 return (regs.x.bx != 0);
1143 }
1144
1145 void
1146 mouse_get_pos (f, bar_window, part, x, y, time)
1147 FRAME_PTR *f;
1148 Lisp_Object *bar_window, *x, *y;
1149 enum scroll_bar_part *part;
1150 unsigned long *time;
1151 {
1152 union REGS regs;
1153 struct timeval tv;
1154
1155 regs.x.ax = 0x0003;
1156 int86 (0x33, &regs, &regs);
1157 *f = selected_frame;
1158 *bar_window = Qnil;
1159 gettimeofday (&tv, NULL);
1160 *x = make_number (regs.x.cx);
1161 *y = make_number (regs.x.dx);
1162 *time = tv.tv_usec;
1163 mouse_moved = 0;
1164 }
1165
1166 void
1167 mouse_check_moved ()
1168 {
1169 union REGS regs;
1170
1171 regs.x.ax = 0x0003;
1172 int86 (0x33, &regs, &regs);
1173 if (regs.x.cx != mouse_last_x || regs.x.dx != mouse_last_y)
1174 {
1175 mouse_moved = 1;
1176 mouse_last_x = regs.x.cx;
1177 mouse_last_y = regs.x.dx;
1178 }
1179 }
1180
1181 int
1182 mouse_init1 ()
1183 {
1184 union REGS regs;
1185 int present;
1186
1187 regs.x.ax = 0x0021;
1188 int86 (0x33, &regs, &regs);
1189 present = internal_terminal && (regs.x.ax & 0xffff) == 0xffff;
1190 if (present)
1191 {
1192 if (regs.x.bx == 3)
1193 {
1194 mouse_button_count = 3;
1195 mouse_button_translate[0] = 0; /* Left */
1196 mouse_button_translate[1] = 2; /* Middle */
1197 mouse_button_translate[2] = 1; /* Right */
1198 }
1199 else
1200 {
1201 mouse_button_count = 2;
1202 mouse_button_translate[0] = 0;
1203 mouse_button_translate[1] = 1;
1204 }
1205 mouse_position_hook = &mouse_get_pos;
1206 mouse_init ();
1207 }
1208 return present;
1209 }
1210
1211 /* See xterm.c for more info. */
1212 void
1213 pixel_to_glyph_coords (f, pix_x, pix_y, x, y, bounds, noclip)
1214 FRAME_PTR f;
1215 register int pix_x, pix_y;
1216 register int *x, *y;
1217 void /* XRectangle */ *bounds;
1218 int noclip;
1219 {
1220 if (bounds) abort ();
1221
1222 /* Ignore clipping. */
1223
1224 *x = pix_x;
1225 *y = pix_y;
1226 }
1227
1228 void
1229 glyph_to_pixel_coords (f, x, y, pix_x, pix_y)
1230 FRAME_PTR f;
1231 register int x, y;
1232 register int *pix_x, *pix_y;
1233 {
1234 *pix_x = x;
1235 *pix_y = y;
1236 }
1237
1238 #endif /* MSDOS */