(read_minibuf_unwind): Clear last_overlay_modified field.
[bpt/emacs.git] / src / msdos.c
index 8c4e989..99695db 100644 (file)
@@ -1,5 +1,5 @@
 /* MS-DOS specific C utilities.
-   Copyright (C) 1993, 1994, 1995 Free Software Foundation, Inc.
+   Copyright (C) 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -32,6 +32,13 @@ Boston, MA 02111-1307, USA.  */
 #include <sys/param.h>
 #include <sys/time.h>
 #include <dos.h>
+#include <errno.h>
+#include <sys/stat.h>    /* for _fixpath */
+#if __DJGPP__ >= 2
+#include <fcntl.h>
+#include <libc/dosio.h>  /* for _USE_LFN */
+#endif
+
 #include "dosfns.h"
 #include "msdos.h"
 #include "systime.h"
@@ -47,6 +54,33 @@ Boston, MA 02111-1307, USA.  */
 /* Damn that local process.h!  Instead we can define P_WAIT ourselves.  */
 #define P_WAIT 1
 
+#ifndef _USE_LFN
+#define _USE_LFN 0
+#endif
+
+#if __DJGPP__ > 1
+
+#include <signal.h>
+
+#ifndef SYSTEM_MALLOC
+
+#ifdef GNU_MALLOC
+
+/* If other `malloc' than ours is used, force our `sbrk' behave like
+   Unix programs expect (resize memory blocks to keep them contiguous).
+   If `sbrk' from `ralloc.c' is NOT used, also zero-out sbrk'ed memory,
+   because that's what `gmalloc' expects to get.  */
+#include <crt0.h>
+
+#ifdef REL_ALLOC
+int _crt0_startup_flags = _CRT0_FLAG_UNIX_SBRK;
+#else  /* not REL_ALLOC */
+int _crt0_startup_flags = (_CRT0_FLAG_UNIX_SBRK | _CRT0_FLAG_FILL_SBRK_MEMORY);
+#endif /* not REL_ALLOC */
+#endif /* GNU_MALLOC */
+
+#endif /* not SYSTEM_MALLOC */
+#endif /* __DJGPP__ > 1 */
 
 static unsigned long
 event_timestamp ()
@@ -331,10 +365,150 @@ ScreenVisualBell (void)
 
 #ifndef HAVE_X_WINDOWS
 
-/*
- * If we write a character in the position where the mouse is,
- * the mouse cursor may need to be refreshed.
- */
+/* Enable bright background colors.  */
+static void
+bright_bg (void)
+{
+  union REGS regs;
+
+  regs.h.bl = 0;
+  regs.x.ax = 0x1003;
+  int86 (0x10, &regs, &regs);
+}
+
+/* Set the screen dimensions so that it can show no less than
+   ROWS x COLS frame.  */
+
+void
+dos_set_window_size (rows, cols)
+     int *rows, *cols;
+{
+  char video_name[30];
+  Lisp_Object video_mode;
+  int video_mode_value;
+  int have_vga = 0;
+  union REGS regs;
+  int current_rows = ScreenRows (), current_cols = ScreenCols ();
+
+  if (*rows == current_rows && *cols == current_cols)
+    return;
+
+  /* Do we have a VGA?  */
+  regs.x.ax = 0x1a00;
+  int86 (0x10, &regs, &regs);
+  if (regs.h.al == 0x1a && regs.h.bl > 5 && regs.h.bl < 13)
+    have_vga = 1;
+
+  mouse_off ();
+
+  /* If the user specified a special video mode for these dimensions,
+     use that mode.  */
+  sprintf (video_name, "screen-dimensions-%dx%d", *rows, *cols);
+  video_mode = XSYMBOL (Fintern_soft (build_string (video_name),
+                                     Qnil))-> value;
+
+  if (INTEGERP (video_mode)
+      && (video_mode_value = XINT (video_mode)) > 0)
+    {
+      regs.x.ax = video_mode_value;
+      int86 (0x10, &regs, &regs);
+
+      if (have_mouse)
+       {
+         /* Must hardware-reset the mouse, or else it won't update
+            its notion of screen dimensions for some non-standard
+            video modes.  This is *painfully* slow...  */
+         regs.x.ax = 0;
+         int86 (0x33, &regs, &regs);
+       }
+    }
+
+  /* Find one of the dimensions supported by standard EGA/VGA
+     which gives us at least the required dimensions.  */
+
+#if __DJGPP__ > 1
+
+  else
+    {
+      static struct {
+       int rows;
+       int need_vga;
+      }        std_dimension[] = {
+         {25, 0},
+         {28, 1},
+         {35, 0},
+         {40, 1},
+         {43, 0},
+         {50, 1}
+      };
+      int i = 0;
+
+      while (i < sizeof (std_dimension) / sizeof (std_dimension[0]))
+       {
+        if (std_dimension[i].need_vga <= have_vga
+            && std_dimension[i].rows >= *rows)
+          {
+            if (std_dimension[i].rows != current_rows
+                || *cols != current_cols)
+              _set_screen_lines (std_dimension[i].rows);
+            break;
+          }
+        i++;
+       }
+    }
+
+#else /* not __DJGPP__ > 1 */
+
+  else if (*rows <= 25)
+    {
+      if (current_rows != 25 || current_cols != 80)
+       {
+         regs.x.ax = 3;
+         int86 (0x10, &regs, &regs);
+         regs.x.ax = 0x1101;
+         regs.h.bl = 0;
+         int86 (0x10, &regs, &regs);
+         regs.x.ax = 0x1200;
+         regs.h.bl = 32;
+         int86 (0x10, &regs, &regs);
+         regs.x.ax = 3;
+         int86 (0x10, &regs, &regs);
+       }
+    }
+  else if (*rows <= 50)
+    if (have_vga && (current_rows != 50 || current_cols != 80)
+       || *rows <= 43 && (current_rows != 43 || current_cols != 80))
+      {
+       regs.x.ax = 3;
+       int86 (0x10, &regs, &regs);
+       regs.x.ax = 0x1112;
+       regs.h.bl = 0;
+       int86 (0x10, &regs, &regs);
+       regs.x.ax = 0x1200;
+       regs.h.bl = 32;
+       int86 (0x10, &regs, &regs);
+       regs.x.ax = 0x0100;
+       regs.x.cx = 7;
+       int86 (0x10, &regs, &regs);
+      }
+#endif /* not __DJGPP__ > 1 */
+
+  if (have_mouse)
+    {
+      mouse_init ();
+      mouse_on ();
+    }
+
+  /* Tell the caller what dimensions have been REALLY set.  */
+  *rows = ScreenRows ();
+  *cols = ScreenCols ();
+
+  /* Enable bright background colors.  */
+  bright_bg ();
+}
+
+/* If we write a character in the position where the mouse is,
+   the mouse cursor may need to be refreshed.  */
 
 static void
 mouse_off_maybe ()
@@ -503,15 +677,20 @@ IT_update_end ()
 {
 }
 
-/* This was more or less copied from xterm.c */
+/* This was more or less copied from xterm.c
+
+   Nowadays, the corresponding function under X is `x_set_menu_bar_lines_1'
+   on xfns.c  */
+
 static void
 IT_set_menu_bar_lines (window, n)
-  Lisp_Object window;
-  int n;
+     Lisp_Object window;
+     int n;
 {
   struct window *w = XWINDOW (window);
 
   XSETFASTINT (w->last_modified, 0);
+  XSETFASTINT (w->last_overlay_modified, 0);
   XSETFASTINT (w->top, XFASTINT (w->top) + n);
   XSETFASTINT (w->height, XFASTINT (w->height) - n);
 
@@ -527,10 +706,34 @@ IT_set_menu_bar_lines (window, n)
     }
 }
 
-/*
- * IT_set_terminal_modes is called when emacs is started,
- * resumed, and whenever the screen is redrawn!
- */
+/* This was copied from xfns.c  */
+
+void
+x_set_menu_bar_lines (f, value, oldval)
+     struct frame *f;
+     Lisp_Object value, oldval;
+{
+  int nlines;
+  int olines = FRAME_MENU_BAR_LINES (f);
+
+  /* Right now, menu bars don't work properly in minibuf-only frames;
+     most of the commands try to apply themselves to the minibuffer
+     frame itslef, and get an error because you can't switch buffers
+     in or split the minibuffer window.  */
+  if (FRAME_MINIBUF_ONLY_P (f))
+    return;
+
+  if (INTEGERP (value))
+    nlines = XINT (value);
+  else
+    nlines = 0;
+
+  FRAME_MENU_BAR_LINES (f) = nlines;
+  IT_set_menu_bar_lines (f->root_window, nlines - olines);
+}
+
+/* IT_set_terminal_modes is called when emacs is started,
+   resumed, and whenever the screen is redrawn!  */
 
 static
 IT_set_terminal_modes (void)
@@ -564,12 +767,12 @@ IT_set_terminal_modes (void)
   if (termscript)
     fprintf (termscript, "<SCREEN SAVED (dimensions=%dx%d)>\n",
              screen_size_X, screen_size_Y);
+
+  bright_bg ();
 }
 
-/*
- * IT_reset_terminal_modes is called when emacs is
- * suspended or killed.
- */
+/* IT_reset_terminal_modes is called when emacs is
+   suspended or killed.  */
 
 static
 IT_reset_terminal_modes (void)
@@ -640,14 +843,13 @@ IT_set_terminal_window (void)
 }
 
 void
-IT_set_frame_parameters (frame, alist)
-     FRAME_PTR frame;
+IT_set_frame_parameters (f, alist)
+     FRAME_PTR f;
      Lisp_Object alist;
 {
   Lisp_Object tail;
   int redraw;
   extern unsigned long load_color ();
-  FRAME_PTR f = (FRAME_PTR) &the_only_frame;
 
   redraw = 0;
   for (tail = alist; CONSP (tail); tail = Fcdr (tail))
@@ -666,6 +868,8 @@ IT_set_frame_parameters (frame, alist)
            {
              FRAME_FOREGROUND_PIXEL (f) = new_color;
              redraw = 1;
+             if (termscript)
+               fprintf (termscript, "<FGCOLOR %d>\n", new_color);
            }
        }
       else if (EQ (prop, intern ("background-color")))
@@ -673,35 +877,29 @@ IT_set_frame_parameters (frame, alist)
          unsigned long new_color = load_color (f, val);
          if (new_color != ~0)
            {
-             FRAME_BACKGROUND_PIXEL (f) = new_color & ~8;
+             FRAME_BACKGROUND_PIXEL (f) = new_color;
              redraw = 1;
+             if (termscript)
+               fprintf (termscript, "<BGCOLOR %d>\n", new_color);
            }
        }
       else if (EQ (prop, intern ("menu-bar-lines")))
-       {
-         int new;
-         int old = FRAME_MENU_BAR_LINES (the_only_frame);
-
-         if (INTEGERP (val))
-           new = XINT (val);
-         else
-           new = 0;
-         FRAME_MENU_BAR_LINES (f) = new;
-         IT_set_menu_bar_lines (the_only_frame.root_window, new - old);
-       }
+       x_set_menu_bar_lines (f, val, 0);
     }
 
   if (redraw)
     {
       recompute_basic_faces (f);
-      Fredraw_frame (Fselected_frame ());
+      if (f == selected_frame)
+       redraw_frame (f);
     }
 }
 
 #endif /* !HAVE_X_WINDOWS */
 
 
-/* Do we need the internal terminal? */
+/* Do we need the internal terminal?  */
+
 void
 internal_terminal_init ()
 {
@@ -722,7 +920,7 @@ internal_terminal_init ()
 #ifndef HAVE_X_WINDOWS
   if (!internal_terminal || inhibit_window_system)
     {
-      the_only_frame.output_method = output_termcap;
+      selected_frame->output_method = output_termcap;
       return;
     }
 
@@ -732,18 +930,28 @@ internal_terminal_init ()
   bzero (&the_only_x_display, sizeof the_only_x_display);
   the_only_x_display.background_pixel = 7; /* White */
   the_only_x_display.foreground_pixel = 0; /* Black */
+  bright_bg ();
   colors = getenv ("EMACSCOLORS");
   if (colors && strlen (colors) >= 2)
     {
-      the_only_x_display.foreground_pixel = colors[0] & 0x07;
-      the_only_x_display.background_pixel = colors[1] & 0x07;
+      /* The colors use 4 bits each (we enable bright background).  */
+      if (isdigit (colors[0]))
+        colors[0] -= '0';
+      else if (isxdigit (colors[0]))
+        colors[0] -= (isupper (colors[0]) ? 'A' : 'a') - 10;
+      if (colors[0] >= 0 && colors[0] < 16)
+        the_only_x_display.foreground_pixel = colors[0];
+      if (isdigit (colors[1]))
+        colors[1] -= '0';
+      else if (isxdigit (colors[1]))
+        colors[1] -= (isupper (colors[1]) ? 'A' : 'a') - 10;
+      if (colors[1] >= 0 && colors[1] < 16)
+        the_only_x_display.background_pixel = colors[1];
     }
   the_only_x_display.line_height = 1;
-  the_only_frame.output_data.x = &the_only_x_display;
-  the_only_frame.output_method = output_msdos_raw;
   the_only_x_display.font = (XFontStruct *)1;   /* must *not* be zero */
 
-  init_frame_faces ((FRAME_PTR) &the_only_frame);
+  init_frame_faces (selected_frame);
 
   ring_bell_hook = IT_ring_bell;
   write_glyphs_hook = IT_write_glyphs;
@@ -777,6 +985,19 @@ dos_get_saved_screen (screen, rows, cols)
   return 0;
 #endif  
 }
+
+#ifndef HAVE_X_WINDOWS
+
+/* We are not X, but we can emulate it well enough for our needs... */
+void
+check_x (void)
+{
+  if (! FRAME_MSDOS_P (selected_frame))
+    error ("Not running under a windows system");
+}
+
+#endif
+
 \f
 /* ----------------------- Keyboard control ----------------------
  *
@@ -1157,7 +1378,7 @@ dos_get_modifiers (keymask)
            }
        }
       
-      if (regs.h.ah & 1)               /* Left CTRL pressed
+      if (regs.h.ah & 1)               /* Left CTRL pressed ? */
        mask |= CTRL_P;
 
       if (regs.h.ah & 4)               /* Right CTRL pressed ? */
@@ -1219,6 +1440,7 @@ and then the scan code.")
 }
 
 /* Get a char from keyboard.  Function keys are put into the event queue.  */
+
 static int
 dos_rawgetc ()
 {
@@ -1423,14 +1645,37 @@ dos_rawgetc ()
       for (but = 0; but < NUM_MOUSE_BUTTONS; but++)
        for (press = 0; press < 2; press++)
          {
+           int button_num = but;
+
            if (press)
              ok = mouse_pressed (but, &x, &y);
            else
              ok = mouse_released (but, &x, &y);
            if (ok)
              {
+               /* Allow a simultaneous press/release of Mouse-1 and
+                  Mouse-2 to simulate Mouse-3 on two-button mice.  */
+               if (mouse_button_count == 2 && but < 2)
+                 {
+                   int x2, y2; /* don't clobber original coordinates */
+
+                   /* If only one button is pressed, wait 100 msec and
+                      check again.  This way, Speedy Gonzales isn't
+                      punished, while the slow get their chance.  */
+                   if (press && mouse_pressed (1-but, &x2, &y2)
+                       || !press && mouse_released (1-but, &x2, &y2))
+                     button_num = 2;
+                   else
+                     {
+                       delay (100);
+                       if (press && mouse_pressed (1-but, &x2, &y2)
+                           || !press && mouse_released (1-but, &x2, &y2))
+                         button_num = 2;
+                     }
+                 }
+
                event.kind = mouse_click;
-               event.code = but;
+               event.code = button_num;
                event.modifiers = dos_get_modifiers (0)
                  | (press ? down_modifier : up_modifier);
                event.x = x;
@@ -1448,6 +1693,7 @@ dos_rawgetc ()
 static int prev_get_char = -1;
 
 /* Return 1 if a key is ready to be read without suspending execution.  */
+
 dos_keysns ()
 {
   if (prev_get_char != -1)
@@ -1457,6 +1703,7 @@ dos_keysns ()
 }
 
 /* Read a key.  Return -1 if no key is ready.  */
+
 dos_keyread ()
 {
   if (prev_get_char != -1)
@@ -1581,7 +1828,7 @@ IT_menu_calc_size (XMenu *menu, int *width, int *height)
   *height = maxheight;
 }
 
-/* Display MENU at (X,Y) using FACES. */
+/* Display MENU at (X,Y) using FACES.  */
 
 static void
 IT_menu_display (XMenu *menu, int y, int x, int *faces)
@@ -1608,7 +1855,17 @@ IT_menu_display (XMenu *menu, int y, int x, int *faces)
       p = text;
       *p++ = FAST_MAKE_GLYPH (' ', face);
       for (j = 0, q = menu->text[i]; *q; j++)
-       *p++ = FAST_MAKE_GLYPH (*q++, face);
+       {
+         if (*q > 26)
+           *p++ = FAST_MAKE_GLYPH (*q++, face);
+         else  /* make '^x' */
+           {
+             *p++ = FAST_MAKE_GLYPH ('^', face);
+             j++;
+             *p++ = FAST_MAKE_GLYPH (*q++ + 64, face);
+           }
+       }
+           
       for (; j < width; j++)
        *p++ = FAST_MAKE_GLYPH (' ', face);
       *p++ = FAST_MAKE_GLYPH (menu->submenu[i] ? 16 : ' ', face);
@@ -1645,6 +1902,7 @@ int
 XMenuAddPane (Display *foo, XMenu *menu, char *txt, int enable)
 {
   int len;
+  char *p;
 
   if (!enable)
     abort ();
@@ -1654,8 +1912,16 @@ XMenuAddPane (Display *foo, XMenu *menu, char *txt, int enable)
   menu->text[menu->count] = txt;
   menu->panenumber[menu->count] = ++menu->panecount;
   menu->count++;
-  if ((len = strlen (txt)) > menu->width)
+
+  /* Adjust length for possible control characters (which will
+     be written as ^x).  */
+  for (len = strlen (txt), p = txt; *p; p++)
+    if (*p < 27)
+      len++;
+
+  if (len > menu->width)
     menu->width = len;
+
   return menu->panecount;
 }
 
@@ -1666,6 +1932,7 @@ XMenuAddSelection (Display *bar, XMenu *menu, int pane,
                   int foo, char *txt, int enable)
 {
   int len;
+  char *p;
 
   if (pane)
     if (!(menu = IT_menu_search_pane (menu, pane)))
@@ -1675,8 +1942,16 @@ XMenuAddSelection (Display *bar, XMenu *menu, int pane,
   menu->text[menu->count] = txt;
   menu->panenumber[menu->count] = enable;
   menu->count++;
-  if ((len = strlen (txt)) > menu->width)
+
+  /* Adjust length for possible control characters (which will
+     be written as ^x).  */
+  for (len = strlen (txt), p = txt; *p; p++)
+    if (*p < 27)
+      len++;
+
+  if (len > menu->width)
     menu->width = len;
+
   return XM_SUCCESS;
 }
 
@@ -1714,6 +1989,7 @@ XMenuActivate (Display *foo, XMenu *menu, int *pane, int *selidx,
   int faces[4], selectface;
   int leave, result, onepane;
   int title_faces[4];          /* face to display the menu title */
+  int buffers_num_deleted = 0;
 
   /* Just in case we got here without a mouse present...  */
   if (have_mouse <= 0)
@@ -1722,21 +1998,21 @@ XMenuActivate (Display *foo, XMenu *menu, int *pane, int *selidx,
   state = alloca (menu->panecount * sizeof (struct IT_menu_state));
   screensize = screen_size * 2;
   faces[0]
-    = compute_glyph_face (&the_only_frame,
+    = compute_glyph_face (selected_frame,
                          face_name_id_number
-                         (&the_only_frame,
+                         (selected_frame,
                           intern ("msdos-menu-passive-face")),
                          0);
   faces[1]
-    = compute_glyph_face (&the_only_frame,
+    = compute_glyph_face (selected_frame,
                          face_name_id_number
-                         (&the_only_frame,
+                         (selected_frame,
                           intern ("msdos-menu-active-face")),
                          0);
   selectface
-    = face_name_id_number (&the_only_frame, intern ("msdos-menu-select-face"));
-  faces[2] = compute_glyph_face (&the_only_frame, selectface, faces[0]);
-  faces[3] = compute_glyph_face (&the_only_frame, selectface, faces[1]);
+    = face_name_id_number (selected_frame, intern ("msdos-menu-select-face"));
+  faces[2] = compute_glyph_face (selected_frame, selectface, faces[0]);
+  faces[3] = compute_glyph_face (selected_frame, selectface, faces[1]);
 
   /* Make sure the menu title is always displayed with
      `msdos-menu-active-face', no matter where the mouse pointer is.  */
@@ -1744,11 +2020,25 @@ XMenuActivate (Display *foo, XMenu *menu, int *pane, int *selidx,
     title_faces[i] = faces[3];
 
   statecount = 1;
+
+  /* Don't let the title for the "Buffers" popup menu include a
+     digit (which is ugly).
+     
+     This is a terrible kludge, but I think the "Buffers" case is
+     the only one where the title includes a number, so it doesn't
+     seem to be necessary to make this more general.  */
+  if (strncmp (menu->text[0], "Buffers 1", 9) == 0)
+    {
+      menu->text[0][7] = '\0';
+      buffers_num_deleted = 1;
+    }
   state[0].menu = menu;
   mouse_off ();
   ScreenRetrieve (state[0].screen_behind = xmalloc (screensize));
 
   IT_menu_display (menu, y0 - 1, x0 - 1, title_faces); /* display menu title */
+  if (buffers_num_deleted)
+    menu->text[0][7] = ' ';
   if ((onepane = menu->count == 1 && menu->submenu[0]))
     {
       menu->width = menu->submenu[0]->width;
@@ -1869,12 +2159,16 @@ x_pixel_height (struct frame *f)
 \f
 /* ----------------------- DOS / UNIX conversion --------------------- */
 
+void msdos_downcase_filename (unsigned char *);
+
 /* Destructively turn backslashes into slashes.  */
 
 void
 dostounix_filename (p)
      register char *p;
 {
+  msdos_downcase_filename (p);
+
   while (*p)
     {
       if (*p == '\\')
@@ -1889,6 +2183,12 @@ void
 unixtodos_filename (p)
      register char *p;
 {
+  if (p[1] == ':' && *p >= 'A' && *p <= 'Z')
+    {
+      *p += 'a' - 'A';
+      p += 2;
+    }
+
   while (*p)
     {
       if (*p == '/')
@@ -1904,14 +2204,27 @@ getdefdir (drive, dst)
      int drive;
      char *dst;
 {
-  union REGS regs;
+  char in_path[4], *p = in_path;
+  int e = errno;
 
-  *dst++ = '/';
-  regs.h.dl = drive;
-  regs.x.si = (int) dst;
-  regs.h.ah = 0x47;
-  intdos (&regs, &regs);
-  return !regs.x.cflag;
+  /* Generate "X:." (when drive is X) or "." (when drive is 0).  */
+  if (drive != 0)
+    {
+      *p++ = drive + 'A' - 1;
+      *p++ = ':';
+    }
+
+  *p++ = '.';
+  *p = '\0';
+  errno = 0;
+  _fixpath (in_path, dst);
+  if (errno)
+    return 0;
+
+  msdos_downcase_filename (dst);
+
+  errno = e;
+  return 1;
 }
 
 /* Remove all CR's that are followed by a LF.  */
@@ -1942,6 +2255,131 @@ crlf_to_lf (n, buf)
     *np++ = *buf++;
   return np - startp;
 }
+
+#if defined(__DJGPP__) && __DJGPP__ == 2 && __DJGPP_MINOR__ == 0
+
+/* In DJGPP v2.0, library `write' can call `malloc', which might
+   cause relocation of the buffer whose address we get in ADDR.
+   Here is a version of `write' that avoids calling `malloc',
+   to serve us until such time as the library is fixed.
+   Actually, what we define here is called `__write', because
+   `write' is a stub that just jmp's to `__write' (to be
+   POSIXLY-correct with respect to the global name-space).  */
+
+#include <io.h>                      /* for _write */
+#include <libc/dosio.h>       /* for __file_handle_modes[] */
+
+static char xbuf[64 * 1024];  /* DOS cannot write more in one chunk */
+
+#define XBUF_END (xbuf + sizeof (xbuf) - 1)
+
+int
+__write (int handle, const void *buffer, size_t count)
+{
+  if (count == 0)
+    return 0;
+
+  if(__file_handle_modes[handle] & O_BINARY)
+    return _write (handle, buffer, count);
+  else
+    {
+      char *xbp = xbuf;
+      const char *bp = buffer;
+      int total_written = 0;
+      int nmoved = 0, ncr = 0;
+
+      while (count)
+       {
+         /* The next test makes sure there's space for at least 2 more
+            characters in xbuf[], so both CR and LF can be put there.  */
+         if (xbp < XBUF_END)
+           {
+             if (*bp == '\n')
+               {
+                 ncr++;
+                 *xbp++ = '\r';
+               }
+             *xbp++ = *bp++;
+             nmoved++;
+             count--;
+           }
+         if (xbp >= XBUF_END || !count)
+           {
+             size_t to_write = nmoved + ncr;
+             int written = _write (handle, xbuf, to_write);
+
+             if (written == -1)
+               return -1;
+             else
+               total_written += nmoved;  /* CRs aren't counted in ret value */
+
+             /* If some, but not all were written (disk full?), return
+                an estimate of the total written bytes not counting CRs.  */
+             if (written < to_write)
+               return total_written - (to_write - written) * nmoved/to_write;
+
+             nmoved = 0;
+             ncr = 0;
+             xbp = xbuf;
+           }
+       }
+      return total_written;
+    }
+}
+
+#endif /* __DJGPP__ == 2 && __DJGPP_MINOR__ == 0 */
+
+DEFUN ("msdos-long-file-names", Fmsdos_long_file_names, Smsdos_long_file_names,
+  0, 0, 0,
+  "Return non-nil if long file names are supported on MSDOS.")
+  ()
+{
+  return (_USE_LFN ? Qt : Qnil);
+}
+
+/* Convert alphabetic characters in a filename to lower-case.  */
+
+void
+msdos_downcase_filename (p)
+     register unsigned char *p;
+{
+  /* Always lower-case drive letters a-z, even if the filesystem
+     preserves case in filenames.
+     This is so MSDOS filenames could be compared by string comparison
+     functions that are case-sensitive.  Even case-preserving filesystems
+     do not distinguish case in drive letters.  */
+  if (p[1] == ':' && *p >= 'A' && *p <= 'Z')
+    {
+      *p += 'a' - 'A';
+      p += 2;
+    }
+
+  /* Under LFN we expect to get pathnames in their true case.  */
+  if (NILP (Fmsdos_long_file_names ()))
+    for ( ; *p; p++)
+      if (*p >= 'A' && *p <= 'Z')
+       *p += 'a' - 'A';
+}
+
+DEFUN ("msdos-downcase-filename", Fmsdos_downcase_filename, Smsdos_downcase_filename,
+       1, 1, 0,
+  "Convert alphabetic characters in FILENAME to lower case and return that.\n\
+When long filenames are supported, doesn't change FILENAME.\n\
+If FILENAME is not a string, returns nil.\n\
+The argument object is never altered--the value is a copy.")
+  (filename)
+     Lisp_Object filename;
+{
+  char *fname;
+  Lisp_Object tem;
+
+  if (! STRINGP (filename))
+    return Qnil;
+
+  tem = Fcopy_sequence (filename);
+  msdos_downcase_filename (XSTRING (tem)->data);
+  return tem;
+}
 \f
 /* The Emacs root directory as determined by init_environment.  */
 
@@ -1976,7 +2414,7 @@ init_environment (argc, argv, skip_args)
      "c:/emacs/bin/emacs.exe" our root will be "c:/emacs".  */
   root = alloca (MAXPATHLEN + 20);
   _fixpath (argv[0], root);
-  strlwr (root);
+  msdos_downcase_filename (root);
   len = strlen (root);
   while (len > 0 && root[len] != '/' && root[len] != ':')
     len--;
@@ -2010,7 +2448,6 @@ init_environment (argc, argv, skip_args)
   if (!s) s = "c:/command.com";
   t = alloca (strlen (s) + 1);
   strcpy (t, s);
-  strlwr (t);
   dostounix_filename (t);
   setenv ("SHELL", t, 0);
 
@@ -2021,7 +2458,6 @@ init_environment (argc, argv, skip_args)
   /* Current directory is always considered part of MsDos's path but it is
      not normally mentioned.  Now it is.  */
   strcat (strcpy (t, ".;"), s);
-  strlwr (t);
   dostounix_filename (t); /* Not a single file name, but this should work.  */
   setenv ("PATH", t, 1);
 
@@ -2073,7 +2509,7 @@ init_environment (argc, argv, skip_args)
        setenv ("TZ", "IST-02IDT-03,M4.1.6/00:00,M9.5.6/01:00", 0);
        break;
       }
-  init_gettimeofday ();
+  tzset ();
 }
 
 \f
@@ -2081,6 +2517,8 @@ init_environment (argc, argv, skip_args)
 static int break_stat;  /* BREAK check mode status.    */
 static int stdin_stat;  /* stdin IOCTL status.         */
 
+#if __DJGPP__ < 2
+
 /* These must be global.  */
 static _go32_dpmi_seginfo ctrl_break_vector;
 static _go32_dpmi_registers ctrl_break_regs;
@@ -2110,11 +2548,10 @@ install_ctrl_break_check ()
     }
 }
 
-/*
- * Turn off Dos' Ctrl-C checking and inhibit interpretation of
- * control chars by Dos.
- * Determine the keyboard type.
- */
+#endif /* __DJGPP__ < 2 */
+
+/* Turn off Dos' Ctrl-C checking and inhibit interpretation of
+   control chars by DOS.   Determine the keyboard type.  */
 
 int
 dos_ttraw ()
@@ -2124,7 +2561,9 @@ dos_ttraw ()
   
   break_stat = getcbrk ();
   setcbrk (0);
+#if __DJGPP__ < 2
   install_ctrl_break_check ();
+#endif
 
   if (first_time)
     {
@@ -2175,10 +2614,25 @@ dos_ttraw ()
              mouse_init ();
            }
        }
-      
+
       first_time = 0;
+
+#if __DJGPP__ >= 2
+
+      stdin_stat = setmode (fileno (stdin), O_BINARY);
+      return (stdin_stat != -1);
+    }
+  else
+    return (setmode (fileno (stdin), O_BINARY) != -1);
+
+#else /* __DJGPP__ < 2 */
+
     }
 
+  /* I think it is wrong to overwrite `stdin_stat' every time
+     but the first one this function is called, but I don't
+     want to change the way it used to work in v1.x.--EZ  */
+
   inregs.x.ax = 0x4400;                /* Get IOCTL status. */
   inregs.x.bx = 0x00;          /* 0 = stdin. */
   intdos (&inregs, &outregs);
@@ -2188,9 +2642,12 @@ dos_ttraw ()
   inregs.x.ax = 0x4401;                /* Set IOCTL status */
   intdos (&inregs, &outregs);
   return !outregs.x.cflag;
+
+#endif /* __DJGPP__ < 2 */
 }
 
 /*  Restore status of standard input and Ctrl-C checking.  */
+
 int
 dos_ttcooked ()
 {
@@ -2199,17 +2656,26 @@ dos_ttcooked ()
   setcbrk (break_stat);
   mouse_off ();
 
+#if __DJGPP__ >= 2
+
+  return (setmode (fileno (stdin), stdin_stat) != -1);
+
+#else  /* not __DJGPP__ >= 2 */
+
   inregs.x.ax = 0x4401;        /* Set IOCTL status.    */
   inregs.x.bx = 0x00;  /* 0 = stdin.           */
   inregs.x.dx = stdin_stat;
   intdos (&inregs, &outregs);
   return !outregs.x.cflag;
+
+#endif /* not __DJGPP__ >= 2 */
 }
 
 \f
 /* Run command as specified by ARGV in directory DIR.
    The command is run with input from TEMPIN, output to
    file TEMPOUT and stderr to TEMPERR.  */
+
 int
 run_msdos_command (argv, dir, tempin, tempout, temperr)
      unsigned char **argv;
@@ -2287,6 +2753,25 @@ run_msdos_command (argv, dir, tempin, tempout, temperr)
   dup2 (tempout, 1);
   dup2 (temperr, 2);
 
+#if __DJGPP__ > 1
+
+  if (msshell && !argv[3])
+    {
+      /* MS-DOS native shells are too restrictive.  For starters, they
+        cannot grok commands longer than 126 characters.  In DJGPP v2
+        and later, `system' is much smarter, so we'll call it instead.  */
+
+      extern char **environ;
+      environ = envv;
+
+      /* A shell gets a single argument--its full command
+        line--whose original was saved in `saveargv2'.  */
+      result = system (saveargv2);
+    }
+  else
+
+#endif /* __DJGPP__ > 1 */
+
   result = spawnve (P_WAIT, argv[0], argv, envv);
   
   dup2 (inbak, 0);
@@ -2321,15 +2806,15 @@ croak (badfunc)
   exit (1);
 }
 \f
+#if __DJGPP__ < 2
+
 /* ------------------------- Compatibility functions -------------------
  *     gethostname
  *     gettimeofday
  */
 
-/*
- * Hostnames for a pc are not really funny,
- * but they are used in change log so we emulate the best we can.
- */
+/* Hostnames for a pc are not really funny,
+   but they are used in change log so we emulate the best we can.  */
 
 gethostname (p, size)
      char *p;
@@ -2382,86 +2867,41 @@ gettimeofday (struct timeval *tp, struct timezone *tzp)
   return 0;
 }
 
+#endif /* __DJGPP__ < 2 */
 
 /*
  * A list of unimplemented functions that we silently ignore.
  */
 
+#if __DJGPP__ < 2
 unsigned alarm (s) unsigned s; {}
 fork () { return 0; }
 int kill (x, y) int x, y; { return -1; }
 nice (p) int p; {}
 void volatile pause () {}
+sigsetmask (x) int x; { return 0; }
+#endif
+
 request_sigio () {}
 setpgrp () {return 0; }
 setpriority (x,y,z) int x,y,z; { return 0; }
-sigsetmask (x) int x; { return 0; }
 sigblock (mask) int mask; { return 0; } 
 unrequest_sigio () {}
 
 #ifndef HAVE_SELECT
 #include "sysselect.h"
 
-static struct time last_time  = {120, 120, 120, 120};
-static int modeline_time_displayed = 0;
-
-Lisp_Object Vdos_display_time;
-
-static void
-check_timer (t)
-  struct time *t;
-{
-  int sec, min, hour, hund;
-
-  gettime (t);
-  sec  = t->ti_sec;
-  hund = t->ti_hund;
-  hour = t->ti_hour;
-  min  = t->ti_min;
-
-  /* Any chance of not getting here 24 hours or more since last time? */
-  if (hour == last_time.ti_hour
-      && min == last_time.ti_min
-      && sec == last_time.ti_sec)
-    return;
-
-  if (!NILP (Vdos_display_time))
-    {
-      int interval;
-      Lisp_Object dti = XSYMBOL (Fintern_soft (build_string ("display-time-interval"), Qnil))->value;
-      int delta_time  = ((hour - last_time.ti_hour) * 3600
-                        + (min  - last_time.ti_min) * 60
-                        + (sec  - last_time.ti_sec));
-
-      /* Who knows what the user may put into `display-time-interval'?  */
-      if (!INTEGERP (dti) || (interval = XINT (dti)) <= 0)
-       interval = 60;
-
-      /* When it's time to renew the display, fake a `wakeup' call.  */
-      if (!modeline_time_displayed     /* first time */
-         || delta_time >= interval     /* or if we were busy for a long time */
-         || interval == 1              /* and every `interval' seconds hence */
-         || interval == 60 && sec == 0 /* (usual cases first) */
-         || (hour * 3600 + min * 60 + sec) % interval == 0)
-       call2 (intern ("display-time-filter"), Qnil,
-              build_string ("Wake up!\n"));
-
-      modeline_time_displayed = 1;
-    }
-  else if (modeline_time_displayed)
-    {
-      modeline_time_displayed = 0;
-      Fset (intern ("display-time-string"), build_string (""));
+#ifndef EMACS_TIME_ZERO_OR_NEG_P
+#define EMACS_TIME_ZERO_OR_NEG_P(time) \
+  ((long)(time).tv_sec < 0             \
+   || ((time).tv_sec == 0              \
+       && (long)(time).tv_usec <= 0))
+#endif
 
-      /* Force immediate redisplay of modelines.  */
-      update_mode_lines++;
-      redisplay_preserve_echo_area ();
-    }
-  
-  last_time  = *t;
-}
 
 /* Only event queue is checked.  */
+/* We don't have to call timer_check here
+   because wait_reading_process_input takes care of that.  */
 int
 sys_select (nfds, rfds, wfds, efds, timeout)
      int nfds;
@@ -2469,7 +2909,6 @@ sys_select (nfds, rfds, wfds, efds, timeout)
      EMACS_TIME *timeout;
 {
   int check_input;
-  long timeoutval, clnow, cllast;
   struct time t;
 
   check_input = 0;
@@ -2490,27 +2929,39 @@ sys_select (nfds, rfds, wfds, efds, timeout)
      just read it and wait -- that's more efficient.  */
   if (!timeout)
     {
-      do
-       check_timer (&t);  /* check timer even if some input is pending */
-      while (!detect_input_pending ());
+      while (!detect_input_pending ())
+       {
+#if __DJGPP__ >= 2
+         __dpmi_yield ();
+#endif   
+       }
     }
   else
     {
-      timeoutval = EMACS_SECS (*timeout) * 100 + EMACS_USECS (*timeout) / 10000;
-      check_timer (&t);
-      cllast = t.ti_sec * 100 + t.ti_hund;
+      EMACS_TIME clnow, cllast, cldiff;
+
+      gettime (&t);
+      EMACS_SET_SECS_USECS (cllast, t.ti_sec, t.ti_hund * 10000L);
 
       while (!check_input || !detect_input_pending ())
        {
-         check_timer (&t);
-         clnow = t.ti_sec * 100 + t.ti_hund;
-         if (clnow < cllast) /* time wrap */
-           timeoutval -= clnow + 6000 - cllast;
-         else
-           timeoutval -= clnow - cllast;
-         if (timeoutval <= 0)  /* Stop on timer being cleared */
+         gettime (&t);
+         EMACS_SET_SECS_USECS (clnow, t.ti_sec, t.ti_hund * 10000L);
+         EMACS_SUB_TIME (cldiff, clnow, cllast);
+
+         /* When seconds wrap around, we assume that no more than
+            1 minute passed since last `gettime'.  */
+         if (EMACS_TIME_NEG_P (cldiff))
+           EMACS_SET_SECS (cldiff, EMACS_SECS (cldiff) + 60);
+         EMACS_SUB_TIME (*timeout, *timeout, cldiff);
+
+         /* Stop when timeout value crosses zero.  */
+         if (EMACS_TIME_ZERO_OR_NEG_P (*timeout))
            return 0;
          cllast = clnow;
+#if __DJGPP__ >= 2
+         __dpmi_yield ();
+#endif   
        }
     }
   
@@ -2603,6 +3054,11 @@ abort ()
   dos_ttcooked ();
   ScreenSetCursor (10, 0);
   cputs ("\r\n\nEmacs aborted!\r\n");
+#if __DJGPP__ > 1
+  /* Generate traceback, so we could tell whodunit.  */
+  signal (SIGINT, SIG_DFL);
+  __asm__ __volatile__ ("movb $0x1b,%al;call ___djgpp_hw_exception");
+#endif
   exit (2);
 }
 #endif
@@ -2613,10 +3069,8 @@ syms_of_msdos ()
   staticpro (&recent_doskeys);
 
   defsubr (&Srecent_doskeys);
-
-  DEFVAR_LISP ("dos-display-time", &Vdos_display_time,
-    "*When non-nil, `display-time' is in effect on DOS systems.");
-  Vdos_display_time = Qnil;
+  defsubr (&Smsdos_long_file_names);
+  defsubr (&Smsdos_downcase_filename);
 }
 
 #endif /* MSDOS */