home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 2: Applications / Linux Cubed Series 2 - Applications.iso / editors / emacs / xemacs / xemacs-1.004 / xemacs-1 / xemacs-19.13 / src / msdos.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-08-15  |  27.7 KB  |  1,206 lines

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