Subject: bug#8439: [PATCH] ffap.el -- detect paths with spaces (v4)

From: jari <[email-masked]>
Date: Sat, 20 Oct 2012 11:45:51 +0300

On 2012-10-20 10:11:56 Eli Zaretskii:
| Someone said in another discussion that Cygwin already does accept
| Windows-style file names.

Cygwin Emacs 24.2.1:

   C-x C-f c:/test.txt
   => Use M-x make-directory RET RET to create the directory and its parents

   C-x C-f c:\test.txt
   => Use M-x make-directory RET RET to create the directory and its parents

| On 2012-10-20 18:49:10 Daniel Colascione:
| please use cygwin-convert-path-from-windows and

In this patch (compared to previous v3):

   - remove functions related to Cygwin and use cygwin-convert-path-from-windows

   - obey END parameter in ffap-search-backward-file-end as originally
     intended

   - correct "path-separator" term into "dir-separator" everywhere

Jari
>From 7b52dadb3c1901d3c19b0a75854648e660fa4ac8 Mon Sep 17 00:00:00 2001
From: Jari Aalto <[email-masked]>
Date: Sat, 20 Oct 2012 11:29:27 +0300
Subject: [PATCH] lisp/ffap.el: Detect patchs with spaces.
Organization: Private
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 8bit

2012-10-19 Fri  Jari Aalto  <[email-masked]>

        * ffap.el (ffap-file-name-with-spaces-flag): New variable.
        (ffap-cygwin-convert): New function.
        (ffap-search-forward-file-end): New function.
        (ffap-search-backward-file-end): New function.
        (ffap-dir-separator-near-point): New function.
        (ffap-string-at-point): If `ffap-file-name-with-spaces-flag' is set,
        allow spaces in file names.

Signed-off-by: Jari Aalto <[email-masked]>
---
 lisp/ffap.el |  178 +++++++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 159 insertions(+), 19 deletions(-)

diff --git a/lisp/ffap.el b/lisp/ffap.el
index 4c75609..02dbab5 100644
--- a/lisp/ffap.el
+++ b/lisp/ffap.el
@@ -1036,6 +1036,125 @@ possibly a major-mode name, or one of the symbol
   ;; Added at suggestion of RHOGEE (for ff-paths), 7/24/95.
   "Last string returned by `ffap-string-at-point'.")
 
+;; Test cases: (let ((ffap-file-name-with-spaces-flag t)) (ffap-string-at-point))
+;;
+;; c:/Program Files/Open Text Evaluation Media/Open Text Exceed 14 x86/Program here.txt
+;; c:/Program Files/Open Text Evaluation Media/Open Text Exceed 14 x86/Program Files/Hummingbird/
+;; c:\Program Files\Open Text Evaluation Media\Open Text Exceed 14 x86\Program Files\Hummingbird\
+;; c:\Program Files\Freescale\CW for MPC55xx and MPC56xx 2.10\PowerPC_EABI_Tools\Command_Line_Tools\CLT_Usage_Notes.txt
+;; C:\temp\program.log on Windows or /var/log/program.log on Unix.
+
+(defvar ffap-file-name-with-spaces-flag (memq system-type '(ms-dos windows-nt))
+  "If non-nil, enable looking for paths with spaces in `ffap-string-at-point'.
+Enabled in W32 by default.")
+
+(defun ffap-search-backward-file-end (&optional dir-separator end)
+  "Search backward position point where file would probably end.
+Optional DIR-SEPARATOR defaults to \"/\". The search maximum is
+`line-end-position' or optional END point.
+
+Suppose the cursor is somewhere that might be near end of file,
+the guessing would position point before punctuation (like comma)
+after the file extension:
+
+  C:\temp\file.log, which contain ....
+  =============================== (before)
+  ---------------- (after)
+
+
+  C:\temp\file.log on Windows or /tmp/file.log on Unix
+  =============================== (before)
+  ---------------- (after)
+
+The strategy is to search backward until DIR-SEPARATOR which defaults to
+\"/\" and then take educated guesses.
+
+Move point and return point if an adjustment was done."
+  (or dir-separator
+      (setq dir-separator "/"))
+  (let ((opoint (point))
+	point
+	puct
+	end
+	whitespace-p)
+    (when (re-search-backward
+	   (regexp-quote dir-separator) (line-beginning-position) t)
+      (forward-char 1)			 ;move to the beginning of match
+      ;; until typical punctuation
+      (when (re-search-forward "\\([][<>()\"'`,.:;]\\)"
+			       (or end
+				   (line-end-position))
+			       t)
+	(setq end (match-end 0))
+	(setq punct (match-string 1))
+	(setq whitespace-p (looking-at "[ \t\r\n]\\|$"))
+	(goto-char end)
+	(cond
+	 ((and (string-equal punct ".")
+	       whitespace-p)		     ;end of sentence
+	  (setq point (1- (point))))
+	 ((and (string-equal punct ".")
+	       (looking-at "[a-zA-Z0-9.]+")) ;possibly file extensionl
+	  (setq point (match-end 0)))
+	 (t
+	  (setq point (point)))))
+      (goto-char opoint)
+      (when point
+	(goto-char point)
+	point))))
+
+(defun ffap-search-forward-file-end (&optional dir-separator)
+  "Search DIR-SEPARATOR and position point at file's maximum ending (including spaces).
+Optional DIR-SEPARATOR defaults to \"/\".
+Call `ffap-search-backward-file-end' to refine the ending point."
+  (or dir-separator
+      (setq dir-separator "/"))
+  (let* ((chars  ;expected chars in file name
+	  (concat "[^][^<>()\"'`;,#*|"
+		  ;; exclude the opposite as we know the separator
+		  (if (string-equal dir-separator "/")
+		      "\\\\"
+		    "/")
+		  "\t\r\n]"))
+	 (re (concat
+	      chars "*"
+	      (if dir-separator
+		  (regexp-quote dir-separator)
+		"/")
+	      chars "*")))
+    (when (looking-at re)
+      (goto-char (match-end 0)))))
+
+(defun ffap-dir-separator-near-point ()
+  "Search backward and forward for closest slash or backlash in line.
+Return string slash or backslash. Point is moved to closest position."
+  (let ((point (point))
+	str
+	pos)
+    (if (looking-at ".*?/")
+	(setq str "/"
+	      pos (match-end 0)))
+    (if (looking-at ".*?\\\\")
+	(if (or (null pos)
+		(< (match-end 0) pos))
+	    (setq str "\\"
+		  pos (match-end 0))))
+    (goto-char point)
+    (if (re-search-backward "/" (line-beginning-position) t)
+	(if (or (null pos)
+		(< (- point (point)) (- pos point)))
+	    (setq str "/"
+		  pos (1+ (point)))))	;1+ to keep cursor at the end of char
+    (goto-char point)
+    (if (re-search-backward "\\\\" (line-beginning-position) t)
+	(if (or (null pos)
+		(< (- point (point)) (- pos point)))
+	    (setq str "\\"
+		  pos (1+ (point)))))
+    (if pos
+	(goto-char pos))
+    str))
+
 (defun ffap-string-at-point (&optional mode)
   "Return a string of characters from around point.
 MODE (defaults to value of `major-mode') is a symbol used to look up string
@@ -1043,26 +1162,47 @@ syntax parameters in `ffap-string-at-point-mode-alist'.
 If MODE is not found, we use `file' instead of MODE.
 If the region is active, return a string from the region.
 Sets `ffap-string-at-point' and `ffap-string-at-point-region'."
-  (let* ((args
-	  (cdr
-	   (or (assq (or mode major-mode) ffap-string-at-point-mode-alist)
-	       (assq 'file ffap-string-at-point-mode-alist))))
-	 (pt (point))
-	 (str
-	  (if (and transient-mark-mode mark-active)
-	      (buffer-substring
-	       (setcar ffap-string-at-point-region (region-beginning))
-	       (setcar (cdr ffap-string-at-point-region) (region-end)))
-	    (buffer-substring
-	     (save-excursion
-	       (skip-chars-backward (car args))
-	       (skip-chars-forward (nth 1 args) pt)
-	       (setcar ffap-string-at-point-region (point)))
-	     (save-excursion
-	       (skip-chars-forward (car args))
-	       (skip-chars-backward (nth 2 args) pt)
-	       (setcar (cdr ffap-string-at-point-region) (point)))))))
+  (let* ((cygwin-p (string-match "cygwin" (emacs-version)))
+	 dir-separator
+	 beg
+	 end
+	 (args
+          (cdr
+           (or (assq (or mode major-mode) ffap-string-at-point-mode-alist)
+               (assq 'file ffap-string-at-point-mode-alist))))
+         (pt (point))
+         (str
+          (if (and transient-mark-mode mark-active)
+              (buffer-substring
+               (setcar ffap-string-at-point-region (region-beginning))
+               (setcar (cdr ffap-string-at-point-region) (region-end)))
+            (buffer-substring
+             (save-excursion
+	       (if (and ffap-file-name-with-spaces-flag
+			(memq mode '(nil file)))
+		   (when (setq dir-separator (ffap-dir-separator-near-point))
+		     (while (re-search-backward
+			     (regexp-quote dir-separator)
+			     (line-beginning-position) t)
+		       (goto-char (match-beginning 0)))))
+               (skip-chars-backward (car args))
+               (skip-chars-forward (nth 1 args) pt)
+               (setcar ffap-string-at-point-region (point))
+	       (setq beg (point)))
+             (save-excursion
+               (skip-chars-forward (car args))
+               (skip-chars-backward (nth 2 args) pt)
+	       (setq end (point))
+	       (when (and ffap-file-name-with-spaces-flag
+			  (memq mode '(nil file)))
+		 (ffap-search-forward-file-end dir-separator)
+		 (ffap-search-backward-file-end dir-separator)
+		 (setq end (point)))
+               (setcar (cdr ffap-string-at-point-region) end))))))
     (set-text-properties 0 (length str) nil str)
+    (if (and (eq system-type 'cygwin)
+	     (fboundp 'cygwin-convert-path-from-windows))
+	(setq str (cygwin-convert-path-from-windows str)))
     (setq ffap-string-at-point str)))
 
 (defun ffap-string-around ()
-- 
1.7.10.4