--- /dev/null
+;;; cedet-files.el --- Common routines dealing with file names.
+
+;;; Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc.
+
+;; Author: Eric M. Ludlam <eric@siege-engine.com>
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+;; Various useful routines for dealing with file names in the tools
+;; which are a part of CEDET.
+
+;;; Code:
+(defvar cedet-dir-sep-char (if (boundp 'directory-sep-char)
+ (symbol-value 'directory-sep-char)
+ ?/)
+ "Character used for directory separation.
+Obsoleted in some versions of Emacs. Needed in others.")
+
+
+(defun cedet-directory-name-to-file-name (referencedir &optional testmode)
+ "Convert the REFERENCEDIR (a full path name) into a filename.
+Converts directory seperation characters into ! characters.
+Optional argument TESTMODE is used by tests to avoid conversion
+to the file's truename, and dodging platform tricks."
+ (let ((file referencedir)
+ dir-sep-string)
+ ;; Expand to full file name
+ (when (not testmode)
+ (setq file (file-truename file)))
+ ;; If FILE is a directory, then force it to end in /.
+ (when (file-directory-p file)
+ (setq file (file-name-as-directory file)))
+ ;; Handle Windows Special cases
+ (when (or (memq system-type '(windows-nt ms-dos)) testmode)
+ ;; Replace any invalid file-name characters (for the
+ ;; case of backing up remote files).
+ (when (not testmode)
+ (setq file (expand-file-name (convert-standard-filename file))))
+ (setq dir-sep-string (char-to-string cedet-dir-sep-char))
+ ;; Normalize DOSish file names: convert all slashes to
+ ;; directory-sep-char, downcase the drive letter, if any,
+ ;; and replace the leading "x:" with "/drive_x".
+ (if (eq (aref file 1) ?:)
+ (setq file (concat dir-sep-string
+ "drive_"
+ (char-to-string (downcase (aref file 0)))
+ (if (eq (aref file 2) cedet-dir-sep-char)
+ ""
+ dir-sep-string)
+ (substring file 2)))))
+ ;; Make the name unique by substituting directory
+ ;; separators. It may not really be worth bothering about
+ ;; doubling `!'s in the original name...
+ (setq file (subst-char-in-string
+ cedet-dir-sep-char ?!
+ (replace-regexp-in-string "!" "!!" file)))
+ file))
+
+(defun cedet-file-name-to-directory-name (referencefile &optional testmode)
+ "Reverse the process of `cedet-directory-name-to-file-name'.
+Convert REFERENCEFILE to a directory name replacing ! with /.
+Optional TESTMODE is used in tests to avoid doing some platform
+specific conversions during tests."
+ (let ((file referencefile))
+ ;; Replace the ! with /
+ (setq file (subst-char-in-string ?! ?/ file))
+ ;; Occurances of // meant there was once a single !.
+ (setq file (replace-regexp-in-string "//" "!" file))
+
+ ;; Handle Windows special cases
+ (when (or (memq system-type '(windows-nt ms-dos)) testmode)
+
+ ;; Handle drive letters from DOSish file names.
+ (when (string-match "^/drive_\\([a-z]\\)/" file)
+ (let ((driveletter (match-string 1 file))
+ )
+ (setq file (concat driveletter ":"
+ (substring file (match-end 1))))))
+
+ ;; Handle the \\file\name nomenclature on some windows boxes.
+ (when (string-match "^!" file)
+ (setq file (concat "//" (substring file 1))))
+ )
+
+ file))
+
+;;; Tests
+;;
+(defvar cedet-files-utest-list
+ '(
+ ( "/home/me/src/myproj/src/foo.c" . "!home!me!src!myproj!src!foo.c" )
+ ( "c:/work/myproj/foo.el" . "!drive_c!work!myproj!foo.el" )
+ ( "//windows/proj/foo.java" . "!!windows!proj!foo.java" )
+ ( "/home/me/proj!bang/foo.c" . "!home!me!proj!!bang!foo.c" )
+ )
+ "List of different file names to test.
+Each entry is a cons cell of ( FNAME . CONVERTED )
+where FNAME is some file name, and CONVERTED is what it should be
+converted into.")
+
+(defun cedet-files-utest ()
+ "Test out some file name conversions."
+ (interactive)
+
+ (let ((idx 0))
+ (dolist (FT cedet-files-utest-list)
+
+ (setq idx (+ idx 1))
+
+ (let ((dir->file (cedet-directory-name-to-file-name (car FT) t))
+ (file->dir (cedet-file-name-to-directory-name (cdr FT) t))
+ )
+
+ (unless (string= (cdr FT) dir->file)
+ (error "Failed: %d. Found: %S Wanted: %S"
+ idx dir->file (cdr FT))
+ )
+
+ (unless (string= file->dir (car FT))
+ (error "Failed: %d. Found: %S Wanted: %S"
+ idx file->dir (car FT))
+ )
+
+ ))))
+
+
+;;; Compatibility
+;;
+;; replace-regexp-in-string is in subr.el in Emacs 21. Provide
+;; here for compatibility.
+
+(when (not (fboundp 'replace-regexp-in-string))
+
+(defun replace-regexp-in-string (regexp rep string &optional
+ fixedcase literal subexp start)
+ "Replace all matches for REGEXP with REP in STRING.
+
+Return a new string containing the replacements.
+
+Optional arguments FIXEDCASE, LITERAL and SUBEXP are like the
+arguments with the same names of function `replace-match'. If START
+is non-nil, start replacements at that index in STRING.
+
+REP is either a string used as the NEWTEXT arg of `replace-match' or a
+function. If it is a function it is applied to each match to generate
+the replacement passed to `replace-match'; the match-data at this
+point are such that match 0 is the function's argument.
+
+To replace only the first match (if any), make REGEXP match up to \\'
+and replace a sub-expression, e.g.
+ (replace-regexp-in-string \"\\(foo\\).*\\'\" \"bar\" \" foo foo\" nil nil 1)
+ => \" bar foo\""
+
+ ;; To avoid excessive consing from multiple matches in long strings,
+ ;; don't just call `replace-match' continually. Walk down the
+ ;; string looking for matches of REGEXP and building up a (reversed)
+ ;; list MATCHES. This comprises segments of STRING which weren't
+ ;; matched interspersed with replacements for segments that were.
+ ;; [For a `large' number of replacements it's more efficient to
+ ;; operate in a temporary buffer; we can't tell from the function's
+ ;; args whether to choose the buffer-based implementation, though it
+ ;; might be reasonable to do so for long enough STRING.]
+ (let ((l (length string))
+ (start (or start 0))
+ matches str mb me)
+ (save-match-data
+ (while (and (< start l) (string-match regexp string start))
+ (setq mb (match-beginning 0)
+ me (match-end 0))
+ ;; If we matched the empty string, make sure we advance by one char
+ (when (= me mb) (setq me (min l (1+ mb))))
+ ;; Generate a replacement for the matched substring.
+ ;; Operate only on the substring to minimize string consing.
+ ;; Set up match data for the substring for replacement;
+ ;; presumably this is likely to be faster than munging the
+ ;; match data directly in Lisp.
+ (string-match regexp (setq str (substring string mb me)))
+ (setq matches
+ (cons (replace-match (if (stringp rep)
+ rep
+ (funcall rep (match-string 0 str)))
+ fixedcase literal str subexp)
+ (cons (substring string start mb) ; unmatched prefix
+ matches)))
+ (setq start me))
+ ;; Reconstruct a string from the pieces.
+ (setq matches (cons (substring string start l) matches)) ; leftover
+ (apply #'concat (nreverse matches)))))
+
+)
+
+(provide 'cedet-files)
+
+;;; cedet-files.el ends here