(rootrelativepath) [MSDOS]: Define, expanding to dynamic
[bpt/emacs.git] / src / msdos.c
CommitLineData
9da6e765
RS
1/* MS-DOS specific C utilities.
2 Copyright (C) 1993, 1994 Free Software Foundation, Inc.
1b94449f
RS
3
4This file is part of GNU Emacs.
5
6GNU Emacs is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 1, or (at your option)
9any later version.
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Emacs; see the file COPYING. If not, write to
18the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
19
9da6e765
RS
20/* Contributed by Morten Welinder */
21
1b94449f
RS
22/* Note: some of the stuff here was taken from end of sysdep.c in demacs. */
23
48984716 24#include <config.h>
1b94449f
RS
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
45static int break_stat; /* BREAK check mode status. */
46static int stdin_stat; /* stdin IOCTL status. */
47static int extended_kbd; /* 101 (102) keyboard present. */
48
49int have_mouse; /* Mouse present? */
50static int mouse_last_x;
51static 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. */
55int
56dos_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 ();
49a09c76 67 have_mouse = mouse_init1 ();
1b94449f
RS
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. */
81int
82dos_ttcooked ()
83{
84 union REGS inregs, outregs;
85
86 setcbrk (break_stat);
49a09c76 87 if (have_mouse) mouse_off ();
1b94449f
RS
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
96static unsigned short
97ibmpc_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. */
250static int
251dos_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);
9da6e765
RS
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. */
1b94449f
RS
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;
9da6e765
RS
282 else if (sc == 0x53 && c != 0xe0)
283 {
284 code = 0xffae; /* Keypad decimal point/comma. */
285 goto nonascii;
286 }
1b94449f
RS
287 else if (sc == 0xe0)
288 {
289 switch (c)
290 {
291 case 10: /* Ctrl Enter */
292 case 13:
293 sc = 0x1c;
294 break;
1b94449f
RS
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 {
9da6e765 322 nonascii:
1b94449f 323 event.kind = non_ascii_keystroke;
49a09c76 324 event.code = (code & 0xff) + 0xff00;
1b94449f
RS
325 }
326 else
327 {
9da6e765
RS
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)))
1b94449f 340 {
9da6e765 341 c = shifted[pos - unshifted];
1b94449f
RS
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
9da6e765
RS
350 = (shift_p ? shift_modifier : 0)
351 + (ctrl_p ? ctrl_modifier : 0)
352 + (alt_p ? meta_modifier : 0);
1b94449f
RS
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. */
49a09c76 368 mouse_check_moved ();
1b94449f
RS
369
370 for (but = 0; but < NUM_MOUSE_BUTTONS; but++)
371 for (press = 0; press < 2; press++)
372 {
373 if (press)
49a09c76 374 ok = mouse_pressed (but, &x, &y);
1b94449f 375 else
49a09c76 376 ok = mouse_released (but, &x, &y);
1b94449f
RS
377 if (ok)
378 {
379 event.kind = mouse_click;
380 event.code = but;
381 event.modifiers
9da6e765
RS
382 = (shift_p ? shift_modifier : 0)
383 + (ctrl_p ? ctrl_modifier : 0)
384 + (alt_p ? meta_modifier : 0)
385 + (press ? down_modifier : up_modifier);
1b94449f
RS
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
399static int prev_get_char = -1;
400
401/* Return 1 if a key is ready to be read without suspending execution. */
402dos_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. */
411dos_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. */
425gethostname (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. */
437void
438dostounix_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. */
450void
451unixtodos_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, ... */
463int
464getdefdir (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. */
479int
480crlf_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;
3d4ad6e0 491 while (buf < endp - 1)
1b94449f
RS
492 {
493 if (*buf == 0x0d)
494 {
495 if (*(++buf) != 0x0a)
496 *np++ = 0x0d;
497 }
498 else
499 *np++ = *buf++;
500 }
3d4ad6e0
RS
501 if (buf < endp)
502 *np++ = *buf++;
1b94449f
RS
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. */
509int
510run_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];
647c32eb 530 saveargv2 = argv[2];
1b94449f
RS
531 argv[1] = "/c";
532 if (argv[2])
533 {
647c32eb
KH
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++;
1b94449f
RS
544 }
545 }
546
547 /* Build the environment array. */
548 {
549 extern Lisp_Object Vprocess_environment;
091d0bdf
KH
550 Lisp_Object tmp, lst;
551 int i, len;
552
553 lst = Vprocess_environment;
554 len = XFASTINT (Flength (lst));
1b94449f
RS
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;
647c32eb 590 argv[2] = saveargv2;
1b94449f
RS
591 }
592 return result;
593}
594
595
596croak (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. */
605unsigned alarm (s) unsigned s; {}
606fork () { return 0; }
607int kill (x, y) int x, y; { return -1; }
608nice (p) int p; {}
609void volatile pause () {}
610request_sigio () {}
611setpgrp () {return 0; }
612setpriority (x,y,z) int x,y,z; { return 0; }
613sigsetmask (x) int x; { return 0; }
614unrequest_sigio () {}
615
616#ifdef chdir
617#undef chdir
618#endif
619
620int
621sys_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. */
639void
640sleep_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/* Define a lot of environment variables if not already defined. Don't
661 remove anything unless you know what you're doing -- lots of code will
662 break if one or more of these are missing. */
663void
664init_environment (argc, argv, skip_args)
665 int argc;
666 char **argv;
667 int skip_args;
668{
669 char *s, *t;
670
671 /* We default HOME to the directory from which Emacs was started, but with
672 a "/bin" suffix removed. */
673 s = argv[0];
674 t = alloca (strlen (s) + 1);
675 strcpy (t, s);
676 s = t + strlen (t);
677 while (s != t && *s != '/' && *s != ':') s--;
678 if (s == t)
679 t = "c:/emacs"; /* When run under debug32. */
680 else
681 {
682 if (*s == ':') s++;
683 *s = 0;
684 if (s - 4 >= t && strcmp (s - 4, "/bin") == 0)
685 s[strlen (s) - 4] = 0;
686 }
687 setenv ("HOME", t, 0);
688
689 /* We set EMACSPATH to ~/bin (expanded) */
690 s = getenv ("HOME");
691 t = strcpy (alloca (strlen (s) + 6), s);
692 if (s[strlen (s) - 1] != '/') strcat (t, "/");
693 strcat (t, "bin");
694 setenv ("EMACSPATH", t, 0);
695
696 /* I don't expect anybody to ever use other terminals so the internal
697 terminal is the default. */
698 setenv ("TERM", "internal", 0);
699
700 /* SHELL is a bit tricky -- COMSPEC is the closest we come, but we must
701 downcase it and mirror the backslashes. */
702 s = getenv ("COMSPEC");
703 if (!s) s = "c:/command.com";
704 t = alloca (strlen (s) + 1);
705 strcpy (t, s);
706 strlwr (t);
707 dostounix_filename (t);
708 setenv ("SHELL", t, 0);
709
710 /* PATH is also downcased and backslashes mirrored. */
711 s = getenv ("PATH");
712 if (!s) s = "";
713 t = alloca (strlen (s) + 3);
714 /* Current directory is always considered part of MsDos's path but it is
715 not normally mentioned. Now it is. */
716 strcat (strcpy (t, ".;"), s);
717 strlwr (t);
718 dostounix_filename (t); /* Not a single file name, but this should work. */
719 setenv ("PATH", t, 1);
720
721 /* In some sense all dos users have root privileges, so... */
722 setenv ("USER", "root", 0);
723 setenv ("NAME", getenv ("USER"), 0);
724
725 /* Time zone determined from country code. To make this possible, the
726 country code may not span more than one time zone. In other words,
727 in the USA, you lose. */
728 switch (dos_country_code)
729 {
730 case 31: /* Belgium */
731 case 32: /* The Netherlands */
732 case 33: /* France */
733 case 34: /* Spain */
734 case 36: /* Hungary */
735 case 38: /* Yugoslavia (or what's left of it?) */
736 case 39: /* Italy */
737 case 41: /* Switzerland */
738 case 42: /* Tjekia */
739 case 45: /* Denmark */
740 case 46: /* Sweden */
741 case 47: /* Norway */
742 case 48: /* Poland */
743 case 49: /* Germany */
744 /* Daylight saving from last Sunday in March to last Sunday in
745 September, both at 2AM. */
746 setenv ("TZ", "MET-01METDST-02,M3.5.0/02:00,M9.5.0/02:00", 0);
747 break;
748 case 44: /* United Kingdom */
749 case 351: /* Portugal */
750 case 354: /* Iceland */
751 setenv ("TZ", "GMT+00", 0);
752 break;
753 case 81: /* Japan */
754 case 82: /* Korea */
755 setenv ("TZ", "???-09", 0);
756 break;
757 case 90: /* Turkey */
758 case 358: /* Finland */
759 case 972: /* Israel */
760 setenv ("TZ", "EET-02", 0);
761 break;
762 }
763 tzset ();
b278e52a 764 init_gettimeofday ();
1b94449f
RS
765}
766
767/* Flash the screen as a substitute for BEEPs. */
768
769static unsigned char _xorattr;
770
49a09c76 771static void
fcea9cd4 772do_visible_bell (xorattr)
1b94449f
RS
773 unsigned char xorattr;
774{
775 _xorattr = xorattr;
49a09c76
RS
776 asm volatile
777 (" pushl %eax
778 pushl %ebx
779 pushl %ecx
780 pushl %edx
781 movl $1,%edx
1b94449f 782visible_bell_0:
49a09c76
RS
783 call _ScreenRows
784 pushl %eax
785 call _ScreenCols
786 pushl %eax
787 movl _ScreenPrimary,%eax
788 call dosmemsetup
789 movl %eax,%ebx
790 popl %ecx
791 popl %eax
792 imull %eax,%ecx
793 movb (__xorattr),%al
794 incl %ebx
1b94449f 795visible_bell_1:
49a09c76
RS
796 xorb %al,%gs:(%ebx)
797 addl $2,%ebx
798 decl %ecx
799 jne visible_bell_1
800 decl %edx
801 jne visible_bell_3
1b94449f 802visible_bell_2:
49a09c76
RS
803 movzwl %ax,%eax
804 movzwl %ax,%eax
805 movzwl %ax,%eax
806 movzwl %ax,%eax
807 decw %cx
808 jne visible_bell_2
809 jmp visible_bell_0
1b94449f 810visible_bell_3:
49a09c76
RS
811 popl %edx
812 popl %ecx
813 popl %ebx
814 popl %eax");
1b94449f
RS
815}
816
09e2ac30
RS
817/* At screen position (X,Y), output C characters from string S with
818 attribute A. Do it fast! */
819
820static void
821output_string (x, y, s, c, a)
822 int x, y, c;
823 unsigned char *s;
824 unsigned char a;
825{
826 char *t = (char *)ScreenPrimary + 2 * (x + ScreenCols () * y);
827 asm volatile
828 (" movl %1,%%eax
829 call dosmemsetup
830 movl %%eax,%%edi
831 movb %0,%%ah
832 movl %2,%%ecx
833 movl %3,%%esi
834output_string1:
835 movb (%%esi),%%al
836 movw %%ax,%%gs:(%%edi)
837 addl $2,%%edi
838 incl %%esi
839 decl %%ecx
840 jne output_string1"
841 : /* no output */
842 : "m" (a), "g" (t), "g" (c), "g" (s)
843 : "%eax", "%ecx", /* "%gs",*/ "%esi", "%edi");
844}
845
1b94449f
RS
846static int internal_terminal = 0;
847#undef fflush
848
849int
850internal_flush (f)
851 FILE *f;
852{
09e2ac30 853 static char spaces[] = " ";
1b94449f
RS
854 static int x;
855 static int y;
09e2ac30
RS
856 unsigned char *cp, *cp0;
857 int count, i, j;
1b94449f
RS
858
859 if (internal_terminal && f == stdout)
860 {
49a09c76 861 if (have_mouse) mouse_off ();
1b94449f
RS
862 cp = stdout->_base;
863 count = stdout->_ptr - stdout->_base;
864 while (count > 0)
865 {
09e2ac30 866 switch (*cp++)
1b94449f
RS
867 {
868 case 27:
869 switch (*cp++)
870 {
871 case '@':
872 y = *cp++;
873 x = *cp++;
874 count -= 4;
875 break;
876 case 'A':
877 ScreenAttrib = *cp++;
878 count -= 3;
879 break;
880 case 'B':
fcea9cd4 881 do_visible_bell (*cp++);
1b94449f
RS
882 count -= 3;
883 break;
884 case 'C':
885 ScreenClear ();
886 x = y = 0;
887 count -= 2;
888 break;
889 case 'E':
09e2ac30
RS
890 i = ScreenCols () - x;
891 j = x;
892 while (i >= sizeof spaces)
893 {
894 output_string (j, y, spaces, sizeof spaces,
895 ScreenAttrib);
896 j += sizeof spaces;
897 i -= sizeof spaces;
898 }
899 if (i > 0)
900 output_string (j, y, spaces, i, ScreenAttrib);
1b94449f
RS
901 count -= 2;
902 break;
903 case 'R':
904 x++;
905 count -= 2;
906 break;
907 case 'U':
908 y--;
909 count -= 2;
910 break;
911 case 'X':
912 ScreenAttrib ^= *cp++;
913 count -= 3;
914 break;
915 default:
916 count -= 2;
917 }
918 break;
fcea9cd4
RS
919 case 7:
920 write (1, "\007", 1);
921 count--;
922 break;
1b94449f
RS
923 case 8:
924 x--;
925 count--;
926 break;
927 case 13:
928 x = 0;
929 count--;
930 break;
931 case 10:
932 y++;
933 count--;
934 break;
935 default:
09e2ac30 936 cp0 = cp - 1;
1b94449f 937 count--;
09e2ac30
RS
938 while (count > 0 && *cp >= ' ')
939 cp++, count--;
940 output_string (x, y, cp0, cp - cp0, ScreenAttrib);
941 x += (cp - cp0);
1b94449f
RS
942 }
943 }
944 fpurge (stdout);
945 ScreenSetCursor (y, x);
49a09c76 946 if (have_mouse) mouse_on ();
1b94449f
RS
947 }
948 else
949 /* This is a call to the original fflush. */
950 fflush (f);
951}
952
953/* Do we need the internal terminal? */
954void
955internal_terminal_init ()
956{
957 char *term = getenv ("TERM");
958
959 internal_terminal
960 = (!noninteractive) && term && !strcmp (term, "internal");
961}
b278e52a
RS
962\f
963/* When time zones are set from Ms-Dos too may C-libraries are playing
964 tricks with time values. We solve this by defining our own version
965 of `gettimeofday' bypassing GO32. Our version needs to be initialized
966 once and after each call to `tzset' with TZ changed. */
1b94449f 967
b278e52a
RS
968static int daylight, gmtoffset;
969
970int
971gettimeofday (struct timeval *tp, struct timezone *tzp)
972{
973 if (tp)
974 {
975 struct time t;
976 struct date d;
977 struct tm tmrec;
978
979 gettime (&t);
980 getdate (&d);
981 tmrec.tm_year = d.da_year - 1900;
982 tmrec.tm_mon = d.da_mon - 1;
983 tmrec.tm_mday = d.da_day;
984 tmrec.tm_hour = t.ti_hour;
985 tmrec.tm_min = t.ti_min;
986 tmrec.tm_sec = t.ti_sec;
987 tmrec.tm_gmtoff = gmtoffset;
988 tmrec.tm_isdst = daylight;
989 tp->tv_sec = mktime (&tmrec);
990 tp->tv_usec = t.ti_hund * (1000000 / 100);
991 }
992 if (tzp)
993 {
994 tzp->tz_minuteswest = gmtoffset;
995 tzp->tz_dsttime = daylight;
996 }
997 return 0;
998}
999
1000void
1001init_gettimeofday ()
1002{
1003 time_t ltm, gtm;
1004 struct tm *lstm;
1005
1006 daylight = 0;
1007 gmtoffset = 0;
1008 ltm = gtm = time (NULL);
1009 ltm = mktime (lstm = localtime (&ltm));
1010 gtm = mktime (gmtime (&gtm));
1011 daylight = lstm->tm_isdst;
1012 gmtoffset = (int)(gtm - ltm) / 60;
1013}
1014\f
1b94449f
RS
1015/* These must be global. */
1016static _go32_dpmi_seginfo ctrl_break_vector;
1017static _go32_dpmi_registers ctrl_break_regs;
1018static int ctrlbreakinstalled = 0;
1019
1020/* Interrupt level detection of Ctrl-Break. Don't do anything fancy here! */
1021void
1022ctrl_break_func (regs)
1023 _go32_dpmi_registers *regs;
1024{
1025 Vquit_flag = Qt;
1026}
1027
1028void
1029install_ctrl_break_check ()
1030{
1031 if (!ctrlbreakinstalled)
1032 {
1033 /* Don't press Ctrl-Break if you don't have either DPMI or Emacs
1034 was compiler with Djgpp 1.11 maintenance level 2 or later! */
1035 ctrlbreakinstalled = 1;
1036 ctrl_break_vector.pm_offset = (int) ctrl_break_func;
1037 _go32_dpmi_allocate_real_mode_callback_iret (&ctrl_break_vector,
1038 &ctrl_break_regs);
1039 _go32_dpmi_set_real_mode_interrupt_vector (0x1b, &ctrl_break_vector);
1040 }
1041}
1042\f
1043
1044/* Mouse routines under devellopment follow. Coordinates are in screen
1045 positions and zero based. Mouse buttons are numbered from left to
1046 right and also zero based. */
1047
1048static int mouse_button_translate[NUM_MOUSE_BUTTONS];
1049static int mouse_button_count;
1050
1051void
1052mouse_init ()
1053{
1054 union REGS regs;
1055
1056 regs.x.ax = 0x0007;
1057 regs.x.cx = 0;
1058 regs.x.dx = 8 * (ScreenCols () - 1);
1059 int86 (0x33, &regs, &regs);
1060
1061 regs.x.ax = 0x0008;
1062 regs.x.cx = 0;
1063 regs.x.dx = 8 * (ScreenRows () - 1);
1064 int86 (0x33, &regs, &regs);
1065
1066 mouse_moveto (ScreenCols () - 1, ScreenRows () - 1);
1067 mouse_on ();
1068}
1069
1070void
1071mouse_on ()
1072{
1073 union REGS regs;
1074
1075 regs.x.ax = 0x0001;
1076 int86 (0x33, &regs, &regs);
1077}
1078
1079void
1080mouse_off ()
1081{
1082 union REGS regs;
1083
1084 regs.x.ax = 0x0002;
1085 int86 (0x33, &regs, &regs);
1086}
1087
1088void
1089mouse_moveto (x, y)
1090 int x, y;
1091{
1092 union REGS regs;
1093
1094 regs.x.ax = 0x0004;
1095 mouse_last_x = regs.x.cx = x * 8;
1096 mouse_last_y = regs.x.dx = y * 8;
1097 int86 (0x33, &regs, &regs);
1098}
1099
1100int
1101mouse_pressed (b, xp, yp)
1102 int b, *xp, *yp;
1103{
1104 union REGS regs;
1105
1106 if (b >= mouse_button_count)
1107 return 0;
1108 regs.x.ax = 0x0005;
1109 regs.x.bx = mouse_button_translate[b];
1110 int86 (0x33, &regs, &regs);
1111 if (regs.x.bx)
1112 *xp = regs.x.cx / 8, *yp = regs.x.dx /8;
1113 return (regs.x.bx != 0);
1114}
1115
1116int
1117mouse_released (b, xp, yp)
1118 int b, *xp, *yp;
1119{
1120 union REGS regs;
1121
1122 if (b >= mouse_button_count)
1123 return 0;
1124 regs.x.ax = 0x0006;
1125 regs.x.bx = mouse_button_translate[b];
1126 int86 (0x33, &regs, &regs);
1127 if (regs.x.bx)
1128 *xp = regs.x.cx / 8, *yp = regs.x.dx / 8;
1129 return (regs.x.bx != 0);
1130}
1131
1132void
1133mouse_get_pos (f, bar_window, part, x, y, time)
1134 FRAME_PTR *f;
1135 Lisp_Object *bar_window, *x, *y;
1136 enum scroll_bar_part *part;
1137 unsigned long *time;
1138{
1139 union REGS regs;
1140 struct timeval tv;
1141
1142 regs.x.ax = 0x0003;
1143 int86 (0x33, &regs, &regs);
1144 *f = selected_frame;
1145 *bar_window = Qnil;
1146 gettimeofday (&tv, NULL);
411acee3
RS
1147 *x = make_number (regs.x.cx);
1148 *y = make_number (regs.x.dx);
1b94449f
RS
1149 *time = tv.tv_usec;
1150 mouse_moved = 0;
1151}
1152
1153void
1154mouse_check_moved ()
1155{
1156 union REGS regs;
1157
1158 regs.x.ax = 0x0003;
1159 int86 (0x33, &regs, &regs);
1160 if (regs.x.cx != mouse_last_x || regs.x.dx != mouse_last_y)
1161 {
1162 mouse_moved = 1;
1163 mouse_last_x = regs.x.cx;
1164 mouse_last_y = regs.x.dx;
1165 }
1166}
1167
1168int
1169mouse_init1 ()
1170{
1171 union REGS regs;
1172 int present;
1173
1174 regs.x.ax = 0x0021;
1175 int86 (0x33, &regs, &regs);
1176 present = internal_terminal && (regs.x.ax & 0xffff) == 0xffff;
1177 if (present)
1178 {
1179 if (regs.x.bx == 3)
1180 {
1181 mouse_button_count = 3;
1182 mouse_button_translate[0] = 0; /* Left */
1183 mouse_button_translate[1] = 2; /* Middle */
1184 mouse_button_translate[2] = 1; /* Right */
1185 }
1186 else
1187 {
1188 mouse_button_count = 2;
1189 mouse_button_translate[0] = 0;
1190 mouse_button_translate[1] = 1;
1191 }
1192 mouse_position_hook = &mouse_get_pos;
1193 mouse_init ();
1194 }
1195 return present;
1196}
1197
49a09c76
RS
1198/* See xterm.c for more info. */
1199void
1200pixel_to_glyph_coords (f, pix_x, pix_y, x, y, bounds, noclip)
1201 FRAME_PTR f;
1202 register int pix_x, pix_y;
1203 register int *x, *y;
1204 void /* XRectangle */ *bounds;
1205 int noclip;
1206{
1207 if (bounds) abort ();
1208
1209 /* Ignore clipping. */
1210
1211 *x = pix_x;
1212 *y = pix_y;
1213}
1214
1215void
1216glyph_to_pixel_coords (f, x, y, pix_x, pix_y)
1217 FRAME_PTR f;
1218 register int x, y;
1219 register int *pix_x, *pix_y;
1220{
1221 *pix_x = x;
1222 *pix_y = y;
1223}
1224
1b94449f 1225#endif /* MSDOS */