]> code.delx.au - gnu-emacs/blobdiff - src/msdos.c
(substitute-key-definition): Fix previous change.
[gnu-emacs] / src / msdos.c
index cc05323c97fe1f9a886350d52ddd116d48069646..94b75a5efc30c8920c9b3eabdbcbe8ef90bac1a9 100644 (file)
@@ -33,10 +33,16 @@ Boston, MA 02111-1307, USA.  */
 #include <sys/time.h>
 #include <dos.h>
 #include <errno.h>
+#include <string.h>     /* for bzero and string functions */
 #include <sys/stat.h>    /* for _fixpath */
+#include <unistd.h>     /* for chdir, dup, dup2, etc. */
 #if __DJGPP__ >= 2
 #include <fcntl.h>
+#include <io.h>                 /* for setmode */
+#include <dpmi.h>       /* for __dpmi_xxx stuff */
+#include <sys/farptr.h>         /* for _farsetsel, _farnspokeb */
 #include <libc/dosio.h>  /* for _USE_LFN */
+#include <conio.h>      /* for cputs */
 #endif
 
 #include "dosfns.h"
@@ -47,6 +53,8 @@ Boston, MA 02111-1307, USA.  */
 #include "termopts.h"
 #include "frame.h"
 #include "window.h"
+#include "buffer.h"
+#include "commands.h"
 #include <go32.h>
 #include <pc.h>
 #include <ctype.h>
@@ -58,9 +66,14 @@ Boston, MA 02111-1307, USA.  */
 #define _USE_LFN 0
 #endif
 
+#ifndef _dos_ds
+#define _dos_ds _go32_info_block.selector_for_linear_memory
+#endif
+
 #if __DJGPP__ > 1
 
 #include <signal.h>
+#include "syssignal.h"
 
 #ifndef SYSTEM_MALLOC
 
@@ -294,11 +307,6 @@ struct x_output the_only_x_display;
 /* This is never dereferenced.  */
 Display *x_current_display;
 
-
-#define SCREEN_SET_CURSOR()                                            \
-  if (current_pos_X != new_pos_X || current_pos_Y != new_pos_Y)        \
-    ScreenSetCursor (current_pos_Y = new_pos_Y, current_pos_X = new_pos_X)
-
 static
 dos_direct_output (y, x, buf, len)
      int y;
@@ -307,11 +315,17 @@ dos_direct_output (y, x, buf, len)
      int len;
 {
   int t = (int) ScreenPrimary + 2 * (x + y * screen_size_X);
-  
+
+#if (__DJGPP__ < 2)
   while (--len >= 0) {
     dosmemput (buf++, 1, t);
     t += 2;
   }
+#else
+  /* This is faster.  */
+  for (_farsetsel (_dos_ds); --len >= 0; t += 2, buf++)
+    _farnspokeb (t, *buf);
+#endif
 }
 #endif
 
@@ -365,6 +379,39 @@ ScreenVisualBell (void)
 
 #ifndef HAVE_X_WINDOWS
 
+static int blink_bit = -1;     /* the state of the blink bit at startup */
+
+/* Enable bright background colors.  */
+static void
+bright_bg (void)
+{
+  union REGS regs;
+
+  /* Remember the original state of the blink/bright-background bit.
+     It is stored at 0040:0065h in the BIOS data area.  */
+  if (blink_bit == -1)
+    blink_bit = (_farpeekb (_dos_ds, 0x465) & 0x20) == 0x20;
+
+  regs.h.bl = 0;
+  regs.x.ax = 0x1003;
+  int86 (0x10, &regs, &regs);
+}
+
+/* Disable bright background colors (and enable blinking) if we found
+   the video system in that state at startup.  */
+static void
+maybe_enable_blinking (void)
+{
+  if (blink_bit == 1)
+    {
+      union REGS regs;
+
+      regs.h.bl = 1;
+      regs.x.ax = 0x1003;
+      int86 (0x10, &regs, &regs);
+    }
+}
+
 /* Set the screen dimensions so that it can show no less than
    ROWS x COLS frame.  */
 
@@ -401,9 +448,6 @@ dos_set_window_size (rows, cols)
     {
       regs.x.ax = video_mode_value;
       int86 (0x10, &regs, &regs);
-      regs.h.bl = 0;
-      regs.x.ax = 0x1003;
-      int86 (0x10, &regs, &regs);
 
       if (have_mouse)
        {
@@ -494,6 +538,9 @@ dos_set_window_size (rows, cols)
   /* 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,
@@ -636,6 +683,75 @@ IT_cursor_to (int y, int x)
   new_pos_Y = y;
 }
 
+static int cursor_cleared;
+
+static
+IT_display_cursor (int on)
+{
+  if (on && cursor_cleared)
+    {
+      ScreenSetCursor (current_pos_Y, current_pos_X);
+      cursor_cleared = 0;
+    }
+  else if (!on && !cursor_cleared)
+    {
+      ScreenSetCursor (-1, -1);
+      cursor_cleared = 1;
+    }
+}
+
+/* Emacs calls cursor-movement functions a lot when it updates the
+   display (probably a legacy of old terminals where you cannot
+   update a screen line without first moving the cursor there).
+   However, cursor movement is expensive on MSDOS (it calls a slow
+   BIOS function and requires 2 mode switches), while actual screen
+   updates access the video memory directly and don't depend on
+   cursor position.  To avoid slowing down the redisplay, we cheat:
+   all functions that move the cursor only set internal variables
+   which record the cursor position, whereas the cursor is only
+   moved to its final position whenever screen update is complete.
+
+   `IT_cmgoto' is called from the keyboard reading loop and when the
+   frame update is complete.  This means that we are ready for user
+   input, so we update the cursor position to show where the point is,
+   and also make the mouse pointer visible.
+
+   Special treatment is required when the cursor is in the echo area,
+   to put the cursor at the end of the text displayed there.  */
+
+static
+IT_cmgoto (f)
+     FRAME_PTR f;
+{
+  /* Only set the cursor to where it should be if the display is
+     already in sync with the window contents.  */
+  int update_cursor_pos = MODIFF == unchanged_modified;
+
+  /* If we are in the echo area, put the cursor at the end of text.  */
+  if (!update_cursor_pos
+      && XFASTINT (XWINDOW (FRAME_MINIBUF_WINDOW (f))->top) <= new_pos_Y)
+    {
+      new_pos_X = FRAME_DESIRED_GLYPHS (f)->used[new_pos_Y];
+      update_cursor_pos = 1;
+    }
+
+  if (update_cursor_pos
+      && (current_pos_X != new_pos_X || current_pos_Y != new_pos_Y))
+    {
+      ScreenSetCursor (current_pos_Y = new_pos_Y, current_pos_X = new_pos_X);
+      if (termscript)
+       fprintf (termscript, "\n<CURSOR:%dx%d>", current_pos_X, current_pos_Y);
+    }
+
+  /* Maybe cursor is invisible, so make it visible.  */
+  IT_display_cursor (1);
+
+  /* Mouse pointer should be always visible if we are waiting for
+     keyboard input.  */
+  if (!mouse_visible)
+    mouse_on ();
+}
+
 static
 IT_reassert_line_highlight (new, vpos)
      int new, vpos;
@@ -666,7 +782,10 @@ 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)
@@ -676,6 +795,7 @@ IT_set_menu_bar_lines (window, 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);
 
@@ -691,16 +811,38 @@ IT_set_menu_bar_lines (window, n)
     }
 }
 
+/* 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)
 {
-  char *colors;
-  FRAME_PTR f;
-  struct face *fp;
-
   if (termscript)
     fprintf (termscript, "\n<SET_TERM>");
   highlight = 0;
@@ -726,6 +868,8 @@ 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
@@ -752,6 +896,10 @@ IT_reset_terminal_modes (void)
     return;
   
   mouse_off ();
+
+  /* Leave the video system in the same state as we found it,
+     as far as the blink/bright-background bit is concerned.  */
+  maybe_enable_blinking ();
  
   /* We have a situation here.
      We cannot just do ScreenUpdate(startup_screen_buffer) because
@@ -800,14 +948,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))
@@ -826,6 +973,8 @@ IT_set_frame_parameters (frame, alist)
            {
              FRAME_FOREGROUND_PIXEL (f) = new_color;
              redraw = 1;
+             if (termscript)
+               fprintf (termscript, "<FGCOLOR %lu>\n", new_color);
            }
        }
       else if (EQ (prop, intern ("background-color")))
@@ -833,31 +982,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 %lu>\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)
     {
+      extern void recompute_basic_faces (FRAME_PTR);
+      extern void redraw_frame (FRAME_PTR);
+
       recompute_basic_faces (f);
-      Fredraw_frame (Fselected_frame ());
+      if (f == selected_frame)
+       redraw_frame (f);
     }
 }
 
+extern void init_frame_faces (FRAME_PTR);
+
 #endif /* !HAVE_X_WINDOWS */
 
 
@@ -883,7 +1030,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;
     }
 
@@ -893,23 +1040,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)
     {
-      /* Foreground colrs use 4 bits, background only 3.  */
-      if (isxdigit (colors[0]) && !isdigit (colors[0]))
-        colors[0] += 10 - (isupper (colors[0]) ? 'A' : 'a');
+      /* 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 (colors[1] >= 0 && colors[1] < 8)
+      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;
@@ -921,6 +1073,7 @@ internal_terminal_init ()
   update_begin_hook = IT_update_begin;
   update_end_hook = IT_update_end;
   reassert_line_highlight_hook = IT_reassert_line_highlight;
+  frame_up_to_date_hook = IT_cmgoto; /* position cursor when update is done */
 
   /* These hooks are called by term.c without being checked.  */
   set_terminal_modes_hook = IT_set_terminal_modes;
@@ -943,6 +1096,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 ----------------------
  *
@@ -1007,6 +1173,7 @@ static struct keyboard_layout_list
 
 static struct dos_keyboard_map *keyboard;
 static int keyboard_map_all;
+static int international_keyboard;
 
 int
 dos_set_keyboard (code, always)
@@ -1014,6 +1181,13 @@ dos_set_keyboard (code, always)
      int always;
 {
   int i;
+  union REGS regs;
+
+  /* See if Keyb.Com is installed (for international keyboard support).  */
+  regs.x.ax = 0xad80;
+  int86 (0x2f, &regs, &regs);
+  if (regs.h.al == 0xff)
+    international_keyboard = 1;
 
   /* Initialize to US settings, for countries that don't have their own.  */
   keyboard = keyboard_layout_list[0].keyboard_map;
@@ -1321,6 +1495,13 @@ dos_get_modifiers (keymask)
              mask |= SUPER_P;
              modifiers |= super_modifier;
            }
+         else if (!international_keyboard)
+           {
+             /* If Keyb.Com is NOT installed, let Right Alt behave
+                like the Left Alt.  */
+             mask &= ~ALT_GR_P;
+             mask |= ALT_P;
+           }
        }
       
       if (regs.h.ah & 1)               /* Left CTRL pressed ? */
@@ -1386,6 +1567,8 @@ and then the scan code.")
 
 /* Get a char from keyboard.  Function keys are put into the event queue.  */
 
+extern void kbd_buffer_store_event (struct input_event *);
+
 static int
 dos_rawgetc ()
 {
@@ -1393,10 +1576,10 @@ dos_rawgetc ()
   union REGS regs;
   
 #ifndef HAVE_X_WINDOWS
-  SCREEN_SET_CURSOR ();
-  if (!mouse_visible) mouse_on ();
+  /* Maybe put the cursor where it should be.  */
+  IT_cmgoto (selected_frame);
 #endif
-    
+
   /* The following condition is equivalent to `kbhit ()', except that
      it uses the bios to do its job.  This pleases DESQview/X.  */
   while ((regs.h.ah = extended_kbd ? 0x11 : 0x01),
@@ -1463,7 +1646,19 @@ dos_rawgetc ()
       
       if (c == 0)
        {
-         if (code & Alt)
+        /* We only look at the keyboard Ctrl/Shift/Alt keys when
+           Emacs is ready to read a key.  Therefore, if they press
+           `Alt-x' when Emacs is busy, by the time we get to
+           `dos_get_modifiers', they might have already released the
+           Alt key, and Emacs gets just `x', which is BAD.
+           However, for keys with the `Map' property set, the ASCII
+           code returns zero iff Alt is pressed.  So, when we DON'T
+           have to support international_keyboard, we don't have to
+           distinguish between the left and  right Alt keys, and we
+           can set the META modifier for any keys with the `Map'
+           property if they return zero ASCII code (c = 0).  */
+        if ( (code & Alt)
+             || ( (code & 0xf000) == Map && !international_keyboard))
            modifiers |= meta_modifier;
          if (code & Ctrl)
            modifiers |= ctrl_modifier;
@@ -1800,7 +1995,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);
@@ -1837,6 +2042,7 @@ int
 XMenuAddPane (Display *foo, XMenu *menu, char *txt, int enable)
 {
   int len;
+  char *p;
 
   if (!enable)
     abort ();
@@ -1846,8 +2052,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;
 }
 
@@ -1858,6 +2072,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)))
@@ -1867,8 +2082,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;
 }
 
@@ -1906,29 +2129,36 @@ 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)
     return XM_IA_SELECT;
+  /* Don't allow non-positive x0 and y0, lest the menu will wrap
+     around the display.  */
+  if (x0 <= 0)
+    x0 = 1;
+  if (y0 <= 0)
+    y0 = 1;
 
   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.  */
@@ -1936,11 +2166,29 @@ 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));
 
+  /* Turn off the cursor.  Otherwise it shows through the menu
+     panes, which is ugly.  */
+  IT_display_cursor (0);
+
   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;
@@ -2025,6 +2273,7 @@ XMenuActivate (Display *foo, XMenu *menu, int *pane, int *selidx,
   ScreenUpdate (state[0].screen_behind);
   while (statecount--)
     xfree (state[statecount].screen_behind);
+  IT_display_cursor (1);       /* turn cursor back on */
   return result;
 }
 
@@ -2061,12 +2310,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 == '\\')
@@ -2081,6 +2334,12 @@ void
 unixtodos_filename (p)
      register char *p;
 {
+  if (p[1] == ':' && *p >= 'A' && *p <= 'Z')
+    {
+      *p += 'a' - 'A';
+      p += 2;
+    }
+
   while (*p)
     {
       if (*p == '/')
@@ -2113,11 +2372,7 @@ getdefdir (drive, dst)
   if (errno)
     return 0;
 
-  /* Under LFN we expect to get pathnames in their true case.  */
-  if (! (_USE_LFN))
-    for (p = dst; *p; p++)
-      if (*p >= 'A' && *p <= 'Z')
-       *p += 'a' - 'A';
+  msdos_downcase_filename (dst);
 
   errno = e;
   return 1;
@@ -2133,7 +2388,6 @@ crlf_to_lf (n, buf)
   unsigned char *np = buf;
   unsigned char *startp = buf;
   unsigned char *endp = buf + n;
-  unsigned char c;
 
   if (n == 0)
     return n;
@@ -2223,6 +2477,122 @@ __write (int handle, const void *buffer, size_t count)
     }
 }
 
+/* A low-level file-renaming function which works around Windows 95 bug.
+   This is pulled directly out of DJGPP v2.01 library sources, and only
+   used when you compile with DJGPP v2.0.  */
+
+#include <io.h>
+int _rename(const char *old, const char *new)
+{
+  __dpmi_regs r;
+  int olen    = strlen(old) + 1;
+  int i;
+  int use_lfn = _USE_LFN;
+  char tempfile[FILENAME_MAX];
+  const char *orig = old;
+  int lfn_fd = -1;
+
+  r.x.dx = __tb_offset;
+  r.x.di = __tb_offset + olen;
+  r.x.ds = r.x.es = __tb_segment;
+
+  if (use_lfn)
+    {
+      /* Windows 95 bug: for some filenames, when you rename
+        file -> file~ (as in Emacs, to leave a backup), the
+        short 8+3 alias doesn't change, which effectively
+        makes OLD and NEW the same file.  We must rename
+        through a temporary file to work around this.  */
+
+      char *pbase = 0, *p;
+      static char try_char[] = "abcdefghijklmnopqrstuvwxyz012345789";
+      int idx = sizeof(try_char) - 1;
+
+      /* Generate a temporary name.  Can't use `tmpnam', since $TMPDIR
+        might point to another drive, which will fail the DOS call.  */
+      strcpy(tempfile, old);
+      for (p = tempfile; *p; p++) /* ensure temporary is on the same drive */
+       if (*p == '/' || *p == '\\' || *p == ':')
+         pbase = p;
+      if (pbase)
+       pbase++;
+      else
+       pbase = tempfile;
+      strcpy(pbase, "X$$djren$$.$$temp$$");
+
+      do
+       {
+         if (idx <= 0)
+           return -1;
+         *pbase = try_char[--idx];
+       } while (_chmod(tempfile, 0) != -1);
+
+      r.x.ax = 0x7156;
+      _put_path2(tempfile, olen);
+      _put_path(old);
+      __dpmi_int(0x21, &r);
+      if (r.x.flags & 1)
+       {
+         errno = __doserr_to_errno(r.x.ax);
+         return -1;
+       }
+
+      /* Now create a file with the original name.  This will
+        ensure that NEW will always have a 8+3 alias
+        different from that of OLD.  (Seems to be required
+        when NameNumericTail in the Registry is set to 0.)  */
+      lfn_fd = _creat(old, 0);
+
+      olen = strlen(tempfile) + 1;
+      old  = tempfile;
+      r.x.di = __tb_offset + olen;
+    }
+
+  for (i=0; i<2; i++)
+    {
+      if(use_lfn)
+       r.x.ax = 0x7156;
+      else
+       r.h.ah = 0x56;
+      _put_path2(new, olen);
+      _put_path(old);
+      __dpmi_int(0x21, &r);
+      if(r.x.flags & 1)
+       {
+         if (r.x.ax == 5 && i == 0) /* access denied */
+           remove(new);                 /* and try again */
+         else
+           {
+             errno = __doserr_to_errno(r.x.ax);
+
+             /* Restore to original name if we renamed it to temporary.  */
+             if (use_lfn)
+               {
+                 if (lfn_fd != -1)
+                   {
+                     _close (lfn_fd);
+                     remove (orig);
+                   }
+                 _put_path2(orig, olen);
+                 _put_path(tempfile);
+                 r.x.ax = 0x7156;
+                 __dpmi_int(0x21, &r);
+               }
+             return -1;
+           }
+       }
+      else
+       break;
+    }
+
+  /* Success.  Delete the file possibly created to work
+     around the Windows 95 bug.  */
+  if (lfn_fd != -1)
+    return (_close (lfn_fd) == 0) ? remove (orig) : -1;
+  return 0;
+}
+
 #endif /* __DJGPP__ == 2 && __DJGPP_MINOR__ == 0 */
 
 DEFUN ("msdos-long-file-names", Fmsdos_long_file_names, Smsdos_long_file_names,
@@ -2232,6 +2602,49 @@ DEFUN ("msdos-long-file-names", Fmsdos_long_file_names, Smsdos_long_file_names,
 {
   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;
+{
+  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.  */
 
@@ -2266,7 +2679,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--;
@@ -2300,7 +2713,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);
 
@@ -2311,7 +2723,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);
 
@@ -2536,17 +2947,29 @@ run_msdos_command (argv, dir, tempin, tempout, temperr)
      Lisp_Object dir;
      int tempin, tempout, temperr;
 {
-  char *saveargv1, *saveargv2, **envv;
+  char *saveargv1, *saveargv2, **envv, *lowcase_argv0, *pa, *pl;
   char oldwd[MAXPATHLEN + 1]; /* Fixed size is safe on MSDOS.  */
   int msshell, result = -1;
-  int in, out, inbak, outbak, errbak;
+  int inbak, outbak, errbak;
   int x, y;
   Lisp_Object cmd;
 
   /* Get current directory as MSDOS cwd is not per-process.  */
   getwd (oldwd);
 
-  cmd = Ffile_name_nondirectory (build_string (argv[0]));
+  /* If argv[0] is the shell, it might come in any lettercase.
+     Since `Fmember' is case-sensitive, we need to downcase
+     argv[0], even if we are on case-preserving filesystems.  */
+  lowcase_argv0 = alloca (strlen (argv[0]) + 1);
+  for (pa = argv[0], pl = lowcase_argv0; *pa; pl++)
+    {
+      *pl = *pa++;
+      if (*pl >= 'A' && *pl <= 'Z')
+       *pl += 'a' - 'A';
+    }
+  *pl = '\0';
+
+  cmd = Ffile_name_nondirectory (build_string (lowcase_argv0));
   msshell = !NILP (Fmember (cmd, Fsymbol_value (intern ("msdos-shells"))))
     && !strcmp ("-c", argv[1]);
   if (msshell)
@@ -2641,6 +3064,11 @@ run_msdos_command (argv, dir, tempin, tempout, temperr)
       mouse_init ();
       mouse_moveto (x, y);
     }
+
+  /* Some programs might change the meaning of the highest bit of the
+     text attribute byte, so we get blinking characters instead of the
+     bright background colors.  Restore that.  */
+  bright_bg ();
   
  done:
   chdir (oldwd);
@@ -2734,14 +3162,123 @@ int kill (x, y) int x, y; { return -1; }
 nice (p) int p; {}
 void volatile pause () {}
 sigsetmask (x) int x; { return 0; }
+sigblock (mask) int mask; { return 0; } 
 #endif
 
 request_sigio () {}
 setpgrp () {return 0; }
 setpriority (x,y,z) int x,y,z; { return 0; }
-sigblock (mask) int mask; { return 0; } 
 unrequest_sigio () {}
 
+#if __DJGPP__ > 1
+
+#ifdef POSIX_SIGNALS
+
+/* Augment DJGPP library POSIX signal functions.  This is needed
+   as of DJGPP v2.01, but might be in the library in later releases. */
+
+#include <libc/bss.h>
+
+/* A counter to know when to re-initialize the static sets.  */
+static int sigprocmask_count = -1;
+
+/* Which signals are currently blocked (initially none).  */
+static sigset_t current_mask;
+
+/* Which signals are pending (initially none).  */
+static sigset_t pending_signals;
+
+/* Previous handlers to restore when the blocked signals are unblocked.  */
+typedef void (*sighandler_t)(int);
+static sighandler_t prev_handlers[320];
+
+/* A signal handler which just records that a signal occured
+   (it will be raised later, if and when the signal is unblocked).  */
+static void
+sig_suspender (signo)
+     int signo;
+{
+  sigaddset (&pending_signals, signo);
+}
+
+int
+sigprocmask (how, new_set, old_set)
+     int how;
+     const sigset_t *new_set;
+     sigset_t *old_set;
+{
+  int signo;
+  sigset_t new_mask;
+
+  /* If called for the first time, initialize.  */
+  if (sigprocmask_count != __bss_count)
+    {
+      sigprocmask_count = __bss_count;
+      sigemptyset (&pending_signals);
+      sigemptyset (&current_mask);
+      for (signo = 0; signo < 320; signo++)
+       prev_handlers[signo] = SIG_ERR;
+    }
+
+  if (old_set)
+    *old_set = current_mask;
+
+  if (new_set == 0)
+    return 0;
+
+  if (how != SIG_BLOCK && how != SIG_UNBLOCK && how != SIG_SETMASK)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  sigemptyset (&new_mask);
+
+  /* DJGPP supports upto 320 signals.  */
+  for (signo = 0; signo < 320; signo++)
+    {
+      if (sigismember (&current_mask, signo))
+       sigaddset (&new_mask, signo);
+      else if (sigismember (new_set, signo) && how != SIG_UNBLOCK)
+       {
+         sigaddset (&new_mask, signo);
+
+         /* SIGKILL is silently ignored, as on other platforms.  */
+         if (signo != SIGKILL && prev_handlers[signo] == SIG_ERR)
+           prev_handlers[signo] = signal (signo, sig_suspender);
+       }
+      if ((   how == SIG_UNBLOCK
+             && sigismember (&new_mask, signo)
+             && sigismember (new_set, signo))
+         || (how == SIG_SETMASK
+             && sigismember (&new_mask, signo)
+             && !sigismember (new_set, signo)))
+       {
+         sigdelset (&new_mask, signo);
+         if (prev_handlers[signo] != SIG_ERR)
+           {
+             signal (signo, prev_handlers[signo]);
+             prev_handlers[signo] = SIG_ERR;
+           }
+         if (sigismember (&pending_signals, signo))
+           {
+             sigdelset (&pending_signals, signo);
+             raise (signo);
+           }
+       }
+    }
+  current_mask = new_mask;
+  return 0;
+}
+
+#else /* not POSIX_SIGNALS */
+
+sigsetmask (x) int x; { return 0; }
+sigblock (mask) int mask; { return 0; } 
+
+#endif /* not POSIX_SIGNALS */
+#endif /* __DJGPP__ > 1 */
+
 #ifndef HAVE_SELECT
 #include "sysselect.h"
 
@@ -2923,7 +3460,8 @@ syms_of_msdos ()
   staticpro (&recent_doskeys);
 
   defsubr (&Srecent_doskeys);
-  defsubt (&Smsdos_long_file_names);
+  defsubr (&Smsdos_long_file_names);
+  defsubr (&Smsdos_downcase_filename);
 }
 
 #endif /* MSDOS */