3

Q:  How to test whether an absolute path is a root file/directory (e.g., at the first level of the hard drive)?

I see a few possibilities depending upon whether the file is on Windows or Unix/Linux. Here is my first attempt:

(equal "/bar" (concat (file-name-directory "/bar") (file-name-nondirectory "/bar")))
lawlist
  • 18,826
  • 5
  • 37
  • 118
  • There are some problems with this. For instance: should it follow symlinks / hardlinks or not? And what about path like this: `/foo/../bar`? – wvxvw May 27 '18 at 05:29
  • @wvxvw -- thank you for taking a look at this thread. I had not contemplated those possibilities. I believe a good night sleep and a cup of coffee in the morning will be needed ... – lawlist May 27 '18 at 05:57
  • @wvxvw -- after a good night of sleep and a cup of coffee in the morning, I have decided that the feature I am implementing will be limited to simple cases until there is a need for something more complex such as dealing with symlinks / hardlinks and special paths such as `/foo/../bar`. Thank you for opening my eyes regarding more complex possibilities. I will place a note in the code I am writing up to consider future applications. – lawlist May 27 '18 at 16:16

2 Answers2

4

Not sure what you're asking, but perhaps this is it (from Dired+):

(defun diredp-root-directory-p (file)
  "Return non-nil if FILE is a root directory."
  (if (fboundp 'ange-ftp-root-dir-p)
      (ange-ftp-root-dir-p (file-name-as-directory file))
    ;; This is essentially `ange-ftp-root-dir-p' applied to `file-name-as-directory'.
    ;; If `ange-ftp-root-dir-p' changes, update this code.
    (or (and (eq system-type 'windows-nt)
             (string-match-p "\\`[a-zA-Z]:[/\\]\\'" (file-name-as-directory file)))
        (string= "/" file))))

Or if you really mean a child of the root then use (diredp-root-directory-p (diredp-parent-dir FILE)). Here's diredp-parent-dir (from Dired+):

(defun diredp-parent-dir (file &optional relativep)
  "Return the parent directory of FILE, or nil if none.
Optional arg RELATIVEP non-nil means return a relative name, that is,
just the parent component."
  (let ((parent  (file-name-directory (directory-file-name (expand-file-name file))))
        relparent)
    (when relativep
      (setq relparent  (file-name-nondirectory (directory-file-name parent))))
    (and (not (equal parent file))  (or relparent  parent))))
Drew
  • 75,699
  • 9
  • 109
  • 225
  • `diredp-parent-dir` seems to do the job quite nicely: `(equal (diredp-parent-dir "/bar") "/")`. Thank you. – lawlist May 27 '18 at 05:52
  • But `(equal (diredp-parent-dir "c:/bar") "/")` is `nil`. Did you try `(diredp-root-directory-p (diredp-parent-dir FILE))`? That should work generally. – Drew May 27 '18 at 13:38
  • Thank you for explaining that both functions needed to be used in conjunction with each other in order to obtain the correct result on both platforms. On a Windows machine, `(diredp-root-directory-p (diredp-parent-dir "c:/bar"))` returns non-nil. And, on a Unix flavored machine (e.g., OSX), `(diredp-root-directory-p (diredp-parent-dir "/bar"))` returns non-nil. Greatly appreciated! – lawlist May 27 '18 at 16:12
0

An absolute path is a root file or directory if and only if it is not a root directory, but its parent is. A path is a root directory if and only if it is equal to its parent. So calculate the parent directory and the parent's parent and compare them.

(defun file-name-is-in-root-directory (filename)
  (setq filename (directory-file-name filename))
  (and (file-name-absolute-p filename)
       (let ((dir (directory-file-name (file-name-directory filename))))
     (and (not (equal filename dir))
          (equal dir (file-name-directory dir))))))

This is a purely textual check. It doesn't take symbolic links or .. into account. If you want that, call expand-file-name on the path first. This resolves symbolic links, simplifies .. (note that you can't do this accurately without resolving symbolic links), and also expands initial ~ abbreviations to home directories.