]> code.delx.au - gnu-emacs/blobdiff - src/w16select.c
(custom-save-all): Use find-file-visit-truename for visiting the custom file.
[gnu-emacs] / src / w16select.c
index e9925c466a7604b8fd1f52f2eaa88c9743435841..cd3098bae52a3e04b5437b372345dd0a11e954e3 100644 (file)
@@ -1,6 +1,7 @@
-/* Win16 Selection processing for emacs on MS-Windows
-   Copyright (C) 1996, 1997 Free Software Foundation.
-   
+/* 16-bit Windows Selection processing for emacs on MS-Windows
+   Copyright (C) 1996, 1997, 2001, 2002, 2003, 2004,
+                 2005, 2006 Free Software Foundation, Inc.
+
 This file is part of GNU Emacs.
 
 GNU Emacs is free software; you can redistribute it and/or modify
@@ -15,8 +16,8 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with GNU Emacs; see the file COPYING.  If not, write to
-the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.  */
 
 /* These functions work by using WinOldAp interface.  WinOldAp
    (WINOLDAP.MOD) is a Microsoft Windows extension supporting
@@ -37,6 +38,10 @@ Boston, MA 02111-1307, USA.  */
 #include "dispextern.h"        /* frame.h seems to want this */
 #include "frame.h"     /* Need this to get the X window of selected_frame */
 #include "blockinput.h"
+#include "buffer.h"
+#include "charset.h"
+#include "coding.h"
+#include "composite.h"
 
 /* If ever some function outside this file will need to call any
    clipboard-related function, the following prototypes and constants
@@ -57,21 +62,38 @@ Boston, MA 02111-1307, USA.  */
 unsigned identify_winoldap_version (void);
 unsigned open_clipboard (void);
 unsigned empty_clipboard (void);
-unsigned set_clipboard_data (unsigned, void *, unsigned);
+unsigned set_clipboard_data (unsigned, void *, unsigned, int);
 unsigned get_clipboard_data_size (unsigned);
-unsigned get_clipboard_data (unsigned, void *, unsigned);
+unsigned get_clipboard_data (unsigned, void *, unsigned, int);
 unsigned close_clipboard (void);
 unsigned clipboard_compact (unsigned);
 
 Lisp_Object QCLIPBOARD, QPRIMARY;
 
+/* Coding system for communicating with other Windows programs via the
+   clipboard.  */
+static Lisp_Object Vselection_coding_system;
+
+/* Coding system for the next communicating with other Windows programs.  */
+static Lisp_Object Vnext_selection_coding_system;
+
 /* The segment address and the size of the buffer in low
    memory used to move data between us and WinOldAp module.  */
-
 static struct {
   unsigned long size;
   unsigned short rm_segment;
 } clipboard_xfer_buf_info;
+
+/* The last text we put into the clipboard.  This is used to prevent
+   passing back our own text from the clipboard, instead of using the
+   kill ring.  The former is undesirable because the clipboard data
+   could be MULEtilated by inappropriately chosen
+   (next-)selection-coding-system.  For this reason, we must store the
+   text *after* it was encoded/Unix-to-DOS-converted.  */
+static unsigned char *last_clipboard_text;
+
+/* The size of allocated storage for storing the clipboard data.  */
+static size_t clipboard_storage_size;
 \f
 /* Emulation of `__dpmi_int' and friends for DJGPP v1.x  */
 
@@ -148,7 +170,7 @@ unsigned
 empty_clipboard ()
 {
   __dpmi_regs regs;
-  
+
   /* Calls Int 2Fh/AX=1702h
      Return Values   AX == 0: Error occurred
                        <> 0: OK, Clipboard emptied */
@@ -169,6 +191,11 @@ alloc_xfer_buf (want_size)
   if (want_size <= _go32_info_block.size_of_transfer_buffer)
     return __tb & 0xfffff;
 
+  /* Don't even try to allocate more than 1MB of memory: DOS cannot
+     possibly handle that (it will overflow the BX register below).  */
+  if (want_size > 0xfffff)
+    return 0;
+
   /* Need size rounded up to the nearest paragraph, and in
      paragraph units (1 paragraph = 16 bytes).  */
   clipboard_xfer_buf_info.size = (want_size + 15) >> 4;
@@ -209,48 +236,84 @@ free_xfer_buf ()
     }
 }
 
-/* Copy data into the clipboard, return non-zero if successfull.  */
+/* Copy data into the clipboard, return zero if successfull.  */
 unsigned
-set_clipboard_data (Format, Data, Size)
+set_clipboard_data (Format, Data, Size, Raw)
      unsigned Format;
      void *Data;
      unsigned Size;
+     int Raw;
 {
   __dpmi_regs regs;
   unsigned truelen;
   unsigned long xbuf_addr, buf_offset;
   unsigned char *dp = Data, *dstart = dp;
 
-  if (Format != CF_TEXT)
-    return 0;
+  if (Format != CF_OEMTEXT)
+    return 3;
 
   /* need to know final size after '\r' chars are inserted (the
-     standard CF_TEXT clipboard format uses CRLF line endings,
+     standard CF_OEMTEXT clipboard format uses CRLF line endings,
      while Emacs uses just LF internally).  */
-  truelen = Size;
-  /* avoid using strchr because it recomputes the length everytime */
-  while ((dp = memchr (dp, '\n', Size - (dp - dstart))) != 0)
+  truelen = Size + 1;          /* +1 for the terminating null */
+
+  if (!Raw)
     {
-      truelen++;
-      dp++;
+      /* avoid using strchr because it recomputes the length everytime */
+      while ((dp = memchr (dp, '\n', Size - (dp - dstart))) != 0)
+       {
+         truelen++;
+         dp++;
+       }
     }
 
   if (clipboard_compact (truelen) < truelen)
-    return 0;
+    return 1;
 
   if ((xbuf_addr = alloc_xfer_buf (truelen)) == 0)
-    return 0;
+    return 1;
+
+  /* Move the buffer into the low memory, convert LF into CR-LF if needed.  */
+  if (Raw)
+    {
+      dosmemput (Data, Size, xbuf_addr);
+
+      /* Terminate with a null, otherwise Windows does strange things
+        when the text size is an integral multiple of 32 bytes. */
+      _farpokeb (_dos_ds, xbuf_addr + Size, '\0');
+    }
+  else
+    {
+      dp = Data;
+      buf_offset = xbuf_addr;
+      _farsetsel (_dos_ds);
+      while (Size--)
+       {
+         /* Don't allow them to put binary data into the clipboard, since
+            it will cause yanked data to be truncated at the first null.  */
+         if (*dp == '\0')
+           return 2;
+         if (*dp == '\n')
+           _farnspokeb (buf_offset++, '\r');
+         _farnspokeb (buf_offset++, *dp++);
+       }
+
+      /* Terminate with a null, otherwise Windows does strange things
+        when the text size is an integral multiple of 32 bytes. */
+      _farnspokeb (buf_offset, '\0');
+    }
 
-  /* Move the buffer into the low memory, convert LF into CR-LF pairs.  */
-  dp = Data;
-  buf_offset = xbuf_addr;
-  _farsetsel (_dos_ds);
-  while (Size--)
+  /* Stash away the data we are about to put into the clipboard, so we
+     could later check inside get_clipboard_data whether the clipboard
+     still holds our data.  */
+  if (clipboard_storage_size < truelen)
     {
-      if (*dp == '\n')
-       _farnspokeb (buf_offset++, '\r');
-      _farnspokeb (buf_offset++, *dp++);
+      clipboard_storage_size = truelen + 100;
+      last_clipboard_text =
+       (char *) xrealloc (last_clipboard_text, clipboard_storage_size);
     }
+  if (last_clipboard_text)
+    dosmemget (xbuf_addr, truelen, last_clipboard_text);
 
   /* Calls Int 2Fh/AX=1703h with:
                     DX = WinOldAp-Supported Clipboard format
@@ -268,7 +331,12 @@ set_clipboard_data (Format, Data, Size)
 
   free_xfer_buf ();
 
-  return regs.x.ax;
+  /* If the above failed, invalidate the local copy of the clipboard.  */
+  if (regs.x.ax == 0)
+    *last_clipboard_text = '\0';
+
+  /* Zero means success, otherwise (1, 2, or 3) it's an error.  */
+  return regs.x.ax > 0 ? 0 : 3;
 }
 
 /* Return the size of the clipboard data of format FORMAT.  */
@@ -294,17 +362,17 @@ get_clipboard_data_size (Format)
    Warning: this doesn't check whether DATA has enough space to hold
    SIZE bytes.  */
 unsigned
-get_clipboard_data (Format, Data, Size)
+get_clipboard_data (Format, Data, Size, Raw)
      unsigned Format;
      void *Data;
      unsigned Size;
+     int Raw;
 {
   __dpmi_regs regs;
-  unsigned datalen = 0;
   unsigned long xbuf_addr;
   unsigned char *dp = Data;
 
-  if (Format != CF_TEXT)
+  if (Format != CF_OEMTEXT)
     return 0;
 
   if (Size == 0)
@@ -326,32 +394,53 @@ get_clipboard_data (Format, Data, Size)
   __dpmi_int(0x2f, &regs);
   if (regs.x.ax != 0)
     {
-      /* Copy data from low memory, remove CR characters if before LF.  */
+      unsigned char null_char = '\0';
+      unsigned long xbuf_beg = xbuf_addr;
+
+      /* If last_clipboard_text is NULL, we don't want to slow down
+        the next loop by an additional test.  */
+      register unsigned char *lcdp =
+       last_clipboard_text == NULL ? &null_char : last_clipboard_text;
+
+      /* Copy data from low memory, remove CR
+        characters before LF if needed.  */
       _farsetsel (_dos_ds);
       while (Size--)
        {
          register unsigned char c = _farnspeekb (xbuf_addr++);
 
-         if ((*dp++ = c) == '\r' && _farnspeekb (xbuf_addr) == '\n')
+         if (*lcdp == c)
+           lcdp++;
+
+         if ((*dp++ = c) == '\r' && !Raw && _farnspeekb (xbuf_addr) == '\n')
            {
              dp--;
              *dp++ = '\n';
              xbuf_addr++;
+             if (*lcdp == '\n')
+               lcdp++;
            }
          /* Windows reportedly rounds up the size of clipboard data
-            (passed in SIZE) to a multiple of 32.  We therefore bail
-            out when we see the first null character.  */
+            (passed in SIZE) to a multiple of 32, and removes trailing
+            spaces from each line without updating SIZE.  We therefore
+            bail out when we see the first null character.  */
          else if (c == '\0')
-           {
-             datalen = dp - (unsigned char *)Data - 1;
-             break;
-           }
+           break;
        }
+
+      /* If the text in clipboard is identical to what we put there
+        last time set_clipboard_data was called, pretend there's no
+        data in the clipboard.  This is so we don't pass our own text
+        from the clipboard (which might be troublesome if the killed
+        text includes null characters).  */
+      if (last_clipboard_text &&
+         xbuf_addr - xbuf_beg == (long)(lcdp - last_clipboard_text))
+       dp = (unsigned char *)Data + 1;
     }
 
   free_xfer_buf ();
 
-  return datalen;
+  return (unsigned) (dp - (unsigned char *)Data - 1);
 }
 
 /* Close clipboard, return non-zero if successfull.  */
@@ -388,120 +477,236 @@ clipboard_compact (Size)
 \f
 static char no_mem_msg[] =
   "(Not enough DOS memory to put saved text into clipboard.)";
-
-DEFUN ("win16-set-clipboard-data", Fwin16_set_clipboard_data, Swin16_set_clipboard_data, 1, 2, 0,
-       "This sets the clipboard data to the given text.")
-    (string, frame)
-    Lisp_Object string, frame;
+static char binary_msg[] =
+  "(Binary characters in saved text; clipboard data not set.)";
+static char system_error_msg[] =
+  "(Clipboard interface failure; clipboard data not set.)";
+
+DEFUN ("w16-set-clipboard-data", Fw16_set_clipboard_data, Sw16_set_clipboard_data, 1, 2, 0,
+       doc: /* This sets the clipboard data to the given text.  */)
+     (string, frame)
+     Lisp_Object string, frame;
 {
-  int ok = 1, ok1 = 1;
-  int nbytes;
-  unsigned char *src;
+  unsigned ok = 1, put_status = 0;
+  int nbytes, charset_info, no_crlf_conversion;
+  unsigned char *src, *dst = NULL;
+
+  CHECK_STRING (string);
 
-  CHECK_STRING (string, 0);
-  
   if (NILP (frame))
     frame = Fselected_frame ();
 
-  CHECK_LIVE_FRAME (frame, 0);
+  CHECK_LIVE_FRAME (frame);
   if ( !FRAME_MSDOS_P (XFRAME (frame)))
     goto done;
-  
+
   BLOCK_INPUT;
 
-  nbytes = XSTRING (string)->size + 1;
-  src = XSTRING (string)->data;
-    
+  nbytes = SBYTES (string);
+  src = SDATA (string);
+
+  /* Since we are now handling multilingual text, we must consider
+     encoding text for the clipboard.  */
+  charset_info = find_charset_in_text (src, SCHARS (string), nbytes,
+                                      NULL, Qnil);
+
+  if (charset_info == 0)
+    {
+      /* No multibyte character in OBJ.  We need not encode it, but we
+        will have to convert it to DOS CR-LF style.  */
+      no_crlf_conversion = 0;
+    }
+  else
+    {
+      /* We must encode contents of STRING according to what
+        clipboard-coding-system specifies.  */
+      int bufsize;
+      struct coding_system coding;
+      unsigned char *htext2;
+
+      if (NILP (Vnext_selection_coding_system))
+       Vnext_selection_coding_system = Vselection_coding_system;
+      setup_coding_system
+       (Fcheck_coding_system (Vnext_selection_coding_system), &coding);
+      if (SYMBOLP (coding.pre_write_conversion)
+         && !NILP (Ffboundp (coding.pre_write_conversion)))
+       {
+         string = run_pre_post_conversion_on_str (string, &coding, 1);
+         src = SDATA (string);
+         nbytes = SBYTES (string);
+       }
+      coding.src_multibyte = 1;
+      coding.dst_multibyte = 0;
+      Vnext_selection_coding_system = Qnil;
+      coding.mode |= CODING_MODE_LAST_BLOCK;
+      Vlast_coding_system_used = coding.symbol;
+      bufsize = encoding_buffer_size (&coding, nbytes);
+      dst = (unsigned char *) xmalloc (bufsize);
+      encode_coding (&coding, src, dst, nbytes, bufsize);
+      no_crlf_conversion = 1;
+      nbytes = coding.produced;
+      src = dst;
+    }
+
   if (!open_clipboard ())
     goto error;
-  
-  ok = empty_clipboard () && (ok1 = set_clipboard_data (CF_TEXT, src, nbytes));
-  
+
+  ok = empty_clipboard ()
+    && ((put_status
+        = set_clipboard_data (CF_OEMTEXT, src, nbytes, no_crlf_conversion))
+       == 0);
+
+  if (!no_crlf_conversion)
+    Vlast_coding_system_used = Qraw_text;
   close_clipboard ();
-  
-  if (ok) goto done;
+
+  if (ok) goto unblock;
 
  error:
-  
+
   ok = 0;
 
+ unblock:
+  if (dst)
+    xfree (dst);
+  UNBLOCK_INPUT;
+
   /* Notify user if the text is too large to fit into DOS memory.
      (This will happen somewhere after 600K bytes (470K in DJGPP v1.x),
      depending on user system configuration.)  If we just silently
      fail the function, people might wonder why their text sometimes
      doesn't make it to the clipboard.  */
-  if (ok1 == 0)
+  if (put_status)
     {
-      message2 (no_mem_msg, sizeof (no_mem_msg) - 1);
-      sit_for (2, 0, 0, 1);
+      switch (put_status)
+       {
+         case 1:
+           message2 (no_mem_msg, sizeof (no_mem_msg) - 1, 0);
+           break;
+         case 2:
+           message2 (binary_msg, sizeof (binary_msg) - 1, 0);
+           break;
+         case 3:
+           message2 (system_error_msg, sizeof (system_error_msg) - 1, 0);
+           break;
+       }
+      sit_for (2, 0, 0, 1, 1);
     }
-  
+
  done:
-  UNBLOCK_INPUT;
 
-  return (ok ? string : Qnil);
+  return (ok && put_status == 0 ? string : Qnil);
 }
 
-DEFUN ("win16-get-clipboard-data", Fwin16_get_clipboard_data, Swin16_get_clipboard_data, 0, 1, 0,
-       "This gets the clipboard data in text format.")
+DEFUN ("w16-get-clipboard-data", Fw16_get_clipboard_data, Sw16_get_clipboard_data, 0, 1, 0,
+       doc: /* This gets the clipboard data in text format.  */)
      (frame)
      Lisp_Object frame;
 {
   unsigned data_size, truelen;
   unsigned char *htext;
   Lisp_Object ret = Qnil;
-  
-  if (!NILP (frame))
-    CHECK_LIVE_FRAME (frame, 0);
-  
+  int no_crlf_conversion, require_encoding = 0;
+
   if (NILP (frame))
     frame = Fselected_frame ();
 
-  CHECK_LIVE_FRAME (frame, 0);
+  CHECK_LIVE_FRAME (frame);
   if ( !FRAME_MSDOS_P (XFRAME (frame)))
     goto done;
-  
+
   BLOCK_INPUT;
-  
+
   if (!open_clipboard ())
-    goto done;
+    goto unblock;
 
-  if ((data_size = get_clipboard_data_size (CF_TEXT)) == 0 ||
+  if ((data_size = get_clipboard_data_size (CF_OEMTEXT)) == 0 ||
       (htext = (unsigned char *)xmalloc (data_size)) == 0)
     goto closeclip;
 
   /* need to know final size after '\r' chars are removed because
      we can't change the string size manually, and doing an extra
      copy is silly */
-  if ((truelen = get_clipboard_data (CF_TEXT, htext, data_size)) == 0)
+  if ((truelen = get_clipboard_data (CF_OEMTEXT, htext, data_size, 0)) == 0)
     goto closeclip;
 
-  ret = make_string (htext, truelen);
+  /* Do we need to decode it?  */
+  {
+    /* If the clipboard data contains any 8-bit Latin-1 code, we
+       need to decode it.  */
+    int i;
+
+    for (i = 0; i < truelen; i++)
+      {
+       if (htext[i] >= 0x80)
+         {
+           require_encoding = 1;
+           break;
+         }
+      }
+  }
+  if (require_encoding)
+    {
+      int bufsize;
+      unsigned char *buf;
+      struct coding_system coding;
+
+      if (NILP (Vnext_selection_coding_system))
+       Vnext_selection_coding_system = Vselection_coding_system;
+      setup_coding_system
+       (Fcheck_coding_system (Vnext_selection_coding_system), &coding);
+      coding.src_multibyte = 0;
+      coding.dst_multibyte = 1;
+      Vnext_selection_coding_system = Qnil;
+      coding.mode |= CODING_MODE_LAST_BLOCK;
+      /* We explicitely disable composition handling because selection
+        data should not contain any composition sequence.  */
+      coding.composing = COMPOSITION_DISABLED;
+      truelen = get_clipboard_data (CF_OEMTEXT, htext, data_size, 1);
+      bufsize = decoding_buffer_size (&coding, truelen);
+      buf = (unsigned char *) xmalloc (bufsize);
+      decode_coding (&coding, htext, buf, truelen, bufsize);
+      ret = make_string_from_bytes ((char *) buf,
+                                   coding.produced_char, coding.produced);
+      xfree (buf);
+      if (SYMBOLP (coding.post_read_conversion)
+         && !NILP (Ffboundp (coding.post_read_conversion)))
+       ret = run_pre_post_conversion_on_str (ret, &coding, 0);
+      Vlast_coding_system_used = coding.symbol;
+    }
+  else
+    {
+      ret = make_unibyte_string ((char *) htext, truelen);
+      Vlast_coding_system_used = Qraw_text;
+    }
+
   xfree (htext);
 
  closeclip:
   close_clipboard ();
-  
done:
+
unblock:
   UNBLOCK_INPUT;
-  
+
+ done:
+
   return (ret);
 }
 
 /* Support checking for a clipboard selection. */
 
 DEFUN ("x-selection-exists-p", Fx_selection_exists_p, Sx_selection_exists_p,
-  0, 1, 0,
-  "Whether there is an owner for the given X Selection.\n\
-The arg should be the name of the selection in question, typically one of\n\
-the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'.\n\
-\(Those are literal upper-case symbol names, since that's what X expects.)\n\
-For convenience, the symbol nil is the same as `PRIMARY',\n\
-and t is the same as `SECONDARY'.")
-  (selection)
+       0, 1, 0,
+       doc: /* Whether there is an owner for the given X Selection.
+The arg should be the name of the selection in question, typically one of
+the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'.
+\(Those are literal upper-case symbol names, since that's what X expects.)
+For convenience, the symbol nil is the same as `PRIMARY',
+and t is the same as `SECONDARY'.  */)
+     (selection)
      Lisp_Object selection;
 {
-  CHECK_SYMBOL (selection, 0);
+  CHECK_SYMBOL (selection);
 
   /* Return nil for SECONDARY selection.  For PRIMARY (or nil)
      selection, check if there is some text on the kill-ring;
@@ -514,8 +719,8 @@ and t is the same as `SECONDARY'.")
      into the clipboard if we run under Windows, so we cannot check
      the clipboard alone.)  */
   if ((EQ (selection, Qnil) || EQ (selection, QPRIMARY))
-      && ! NILP (XSYMBOL (Fintern_soft (build_string ("kill-ring"),
-                                       Qnil))->value))
+      && ! NILP (SYMBOL_VALUE (Fintern_soft (build_string ("kill-ring"),
+                                            Qnil))))
     return Qt;
 
   if (EQ (selection, QCLIPBOARD))
@@ -524,7 +729,7 @@ and t is the same as `SECONDARY'.")
 
       if (open_clipboard ())
        {
-         if (get_clipboard_data_size (CF_TEXT))
+         if (get_clipboard_data_size (CF_OEMTEXT))
            val = Qt;
          close_clipboard ();
        }
@@ -533,15 +738,33 @@ and t is the same as `SECONDARY'.")
   return Qnil;
 }
 
-void 
+void
 syms_of_win16select ()
 {
-  defsubr (&Swin16_set_clipboard_data);
-  defsubr (&Swin16_get_clipboard_data);
+  defsubr (&Sw16_set_clipboard_data);
+  defsubr (&Sw16_get_clipboard_data);
   defsubr (&Sx_selection_exists_p);
 
+  DEFVAR_LISP ("selection-coding-system", &Vselection_coding_system,
+              doc: /* Coding system for communicating with other X clients.
+When sending or receiving text via cut_buffer, selection, and clipboard,
+the text is encoded or decoded by this coding system.
+The default value is `iso-latin-1-dos'.  */);
+  Vselection_coding_system = intern ("iso-latin-1-dos");
+
+  DEFVAR_LISP ("next-selection-coding-system", &Vnext_selection_coding_system,
+              doc: /* Coding system for the next communication with other X clients.
+Usually, `selection-coding-system' is used for communicating with
+other X clients.  But, if this variable is set, it is used for the
+next communication only.  After the communication, this variable is
+set to nil.  */);
+  Vnext_selection_coding_system = Qnil;
+
   QPRIMARY   = intern ("PRIMARY");     staticpro (&QPRIMARY);
   QCLIPBOARD = intern ("CLIPBOARD");   staticpro (&QCLIPBOARD);
 }
 
 #endif /* MSDOS */
+
+/* arch-tag: 085a22c8-7324-436e-a6da-102464ce95d8
+   (do not change this comment) */