1 ;;; cedet-files.el --- Common routines dealing with file names.
3 ;;; Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc.
5 ;; Author: Eric M. Ludlam <eric@siege-engine.com>
7 ;; This file is part of GNU Emacs.
9 ;; GNU Emacs is free software: you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation, either version 3 of the License, or
12 ;; (at your option) any later version.
14 ;; GNU Emacs is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
24 ;; Various useful routines for dealing with file names in the tools
25 ;; which are a part of CEDET.
28 (defvar cedet-dir-sep-char
(if (boundp 'directory-sep-char
)
29 (symbol-value 'directory-sep-char
)
31 "Character used for directory separation.
32 Obsoleted in some versions of Emacs. Needed in others.")
35 (defun cedet-directory-name-to-file-name (referencedir &optional testmode
)
36 "Convert the REFERENCEDIR (a full path name) into a filename.
37 Converts directory seperation characters into ! characters.
38 Optional argument TESTMODE is used by tests to avoid conversion
39 to the file's truename, and dodging platform tricks."
40 (let ((file referencedir
)
42 ;; Expand to full file name
44 (setq file
(file-truename file
)))
45 ;; If FILE is a directory, then force it to end in /.
46 (when (file-directory-p file
)
47 (setq file
(file-name-as-directory file
)))
48 ;; Handle Windows Special cases
49 (when (or (memq system-type
'(windows-nt ms-dos
)) testmode
)
50 ;; Replace any invalid file-name characters (for the
51 ;; case of backing up remote files).
53 (setq file
(expand-file-name (convert-standard-filename file
))))
54 (setq dir-sep-string
(char-to-string cedet-dir-sep-char
))
55 ;; Normalize DOSish file names: convert all slashes to
56 ;; directory-sep-char, downcase the drive letter, if any,
57 ;; and replace the leading "x:" with "/drive_x".
58 (if (eq (aref file
1) ?
:)
59 (setq file
(concat dir-sep-string
61 (char-to-string (downcase (aref file
0)))
62 (if (eq (aref file
2) cedet-dir-sep-char
)
65 (substring file
2)))))
66 ;; Make the name unique by substituting directory
67 ;; separators. It may not really be worth bothering about
68 ;; doubling `!'s in the original name...
69 (setq file
(subst-char-in-string
71 (replace-regexp-in-string "!" "!!" file
)))
74 (defun cedet-file-name-to-directory-name (referencefile &optional testmode
)
75 "Reverse the process of `cedet-directory-name-to-file-name'.
76 Convert REFERENCEFILE to a directory name replacing ! with /.
77 Optional TESTMODE is used in tests to avoid doing some platform
78 specific conversions during tests."
79 (let ((file referencefile
))
80 ;; Replace the ! with /
81 (setq file
(subst-char-in-string ?
! ?
/ file
))
82 ;; Occurances of // meant there was once a single !.
83 (setq file
(replace-regexp-in-string "//" "!" file
))
85 ;; Handle Windows special cases
86 (when (or (memq system-type
'(windows-nt ms-dos
)) testmode
)
88 ;; Handle drive letters from DOSish file names.
89 (when (string-match "^/drive_\\([a-z]\\)/" file
)
90 (let ((driveletter (match-string 1 file
))
92 (setq file
(concat driveletter
":"
93 (substring file
(match-end 1))))))
95 ;; Handle the \\file\name nomenclature on some windows boxes.
96 (when (string-match "^!" file
)
97 (setq file
(concat "//" (substring file
1))))
104 (defvar cedet-files-utest-list
106 ( "/home/me/src/myproj/src/foo.c" .
"!home!me!src!myproj!src!foo.c" )
107 ( "c:/work/myproj/foo.el" .
"!drive_c!work!myproj!foo.el" )
108 ( "//windows/proj/foo.java" .
"!!windows!proj!foo.java" )
109 ( "/home/me/proj!bang/foo.c" .
"!home!me!proj!!bang!foo.c" )
111 "List of different file names to test.
112 Each entry is a cons cell of ( FNAME . CONVERTED )
113 where FNAME is some file name, and CONVERTED is what it should be
116 (defun cedet-files-utest ()
117 "Test out some file name conversions."
121 (dolist (FT cedet-files-utest-list
)
125 (let ((dir->file
(cedet-directory-name-to-file-name (car FT
) t
))
126 (file->dir
(cedet-file-name-to-directory-name (cdr FT
) t
))
129 (unless (string= (cdr FT
) dir-
>file
)
130 (error "Failed: %d. Found: %S Wanted: %S"
131 idx dir-
>file
(cdr FT
))
134 (unless (string= file-
>dir
(car FT
))
135 (error "Failed: %d. Found: %S Wanted: %S"
136 idx file-
>dir
(car FT
))
144 ;; replace-regexp-in-string is in subr.el in Emacs 21. Provide
145 ;; here for compatibility.
147 (when (not (fboundp 'replace-regexp-in-string
))
149 (defun replace-regexp-in-string (regexp rep string
&optional
150 fixedcase literal subexp start
)
151 "Replace all matches for REGEXP with REP in STRING.
153 Return a new string containing the replacements.
155 Optional arguments FIXEDCASE, LITERAL and SUBEXP are like the
156 arguments with the same names of function `replace-match'. If START
157 is non-nil, start replacements at that index in STRING.
159 REP is either a string used as the NEWTEXT arg of `replace-match' or a
160 function. If it is a function it is applied to each match to generate
161 the replacement passed to `replace-match'; the match-data at this
162 point are such that match 0 is the function's argument.
164 To replace only the first match (if any), make REGEXP match up to \\'
165 and replace a sub-expression, e.g.
166 (replace-regexp-in-string \"\\(foo\\).*\\'\" \"bar\" \" foo foo\" nil nil 1)
169 ;; To avoid excessive consing from multiple matches in long strings,
170 ;; don't just call `replace-match' continually. Walk down the
171 ;; string looking for matches of REGEXP and building up a (reversed)
172 ;; list MATCHES. This comprises segments of STRING which weren't
173 ;; matched interspersed with replacements for segments that were.
174 ;; [For a `large' number of replacements it's more efficient to
175 ;; operate in a temporary buffer; we can't tell from the function's
176 ;; args whether to choose the buffer-based implementation, though it
177 ;; might be reasonable to do so for long enough STRING.]
178 (let ((l (length string
))
182 (while (and (< start l
) (string-match regexp string start
))
183 (setq mb
(match-beginning 0)
185 ;; If we matched the empty string, make sure we advance by one char
186 (when (= me mb
) (setq me
(min l
(1+ mb
))))
187 ;; Generate a replacement for the matched substring.
188 ;; Operate only on the substring to minimize string consing.
189 ;; Set up match data for the substring for replacement;
190 ;; presumably this is likely to be faster than munging the
191 ;; match data directly in Lisp.
192 (string-match regexp
(setq str
(substring string mb me
)))
194 (cons (replace-match (if (stringp rep
)
196 (funcall rep
(match-string 0 str
)))
197 fixedcase literal str subexp
)
198 (cons (substring string start mb
) ; unmatched prefix
201 ;; Reconstruct a string from the pieces.
202 (setq matches
(cons (substring string start l
) matches
)) ; leftover
203 (apply #'concat
(nreverse matches
)))))
207 (provide 'cedet-files
)
209 ;;; cedet-files.el ends here