# -*- mode: org -*-
#+title: f.el

[[https://github.com/rejeep/f.el/actions/workflows/workflow.yml][file:https://github.com/rejeep/f.el/actions/workflows/workflow.yml/badge.svg]]
[[https://coveralls.io/r/rejeep/f.el][file:https://img.shields.io/coveralls/rejeep/f.el.svg]]
[[https://melpa.org/#/f][file:https://melpa.org/packages/f-badge.svg]]
[[https://stable.melpa.org/#/f][file:https://stable.melpa.org/packages/f-badge.svg]]

Much inspired by [[https://github.com/magnars][@magnars]]s excellent [[https://github.com/magnars/s.el][s.el]] and [[https://github.com/magnars/dash.el][dash.el]], [[https://github.com/rejeep/f.el][f.el]] is a
modern API for working with files and directories in Emacs.

* Installation                                                     :noexport:
It’s available on [[https://melpa.org/#/f][Melpa]] and [[https://stable.melpa.org/#/f][Melpa Stable]].
#+begin_src text
M-x package-install f
#+end_src

Or you can just dump ~f.el~ in your load path somewhere.

* Table of Contents                                                :TOC_3_gh:
- [[#contributing][Contributing]]
- [[#documentation-and-examples][Documentation and examples]]
  - [[#paths][Paths]]
    - [[#f-join][f-join]]
    - [[#f-split][f-split]]
    - [[#f-expand][f-expand]]
    - [[#f-filename][f-filename]]
    - [[#f-dirname][f-dirname]]
    - [[#f-common-parent][f-common-parent]]
    - [[#f-ext][f-ext]]
    - [[#f-no-ext][f-no-ext]]
    - [[#f-swap-ext][f-swap-ext]]
    - [[#f-base][f-base]]
    - [[#f-relative][f-relative]]
    - [[#f-short][f-short]]
    - [[#f-long][f-long]]
    - [[#f-canonical][f-canonical]]
    - [[#f-slash][f-slash]]
    - [[#f-full][f-full]]
    - [[#f-uniquify][f-uniquify]]
    - [[#f-uniquify-alist][f-uniquify-alist]]
  - [[#io][I/O]]
    - [[#f-read-bytes][f-read-bytes]]
    - [[#f-write-bytes][f-write-bytes]]
    - [[#f-append-bytes][f-append-bytes]]
    - [[#f-read-text][f-read-text]]
    - [[#f-write-text][f-write-text]]
    - [[#f-append-text][f-append-text]]
  - [[#destructive][Destructive]]
    - [[#f-mkdir][f-mkdir]]
    - [[#f-mkdir-full-path][f-mkdir-full-path]]
    - [[#f-delete][f-delete]]
    - [[#f-symlink][f-symlink]]
    - [[#f-move][f-move]]
    - [[#f-copy][f-copy]]
    - [[#f-copy-contents][f-copy-contents]]
    - [[#f-touch][f-touch]]
  - [[#predicates][Predicates]]
    - [[#f-exists-p][f-exists-p]]
    - [[#f-directory-p][f-directory-p]]
    - [[#f-file-p][f-file-p]]
    - [[#f-symlink-p][f-symlink-p]]
    - [[#f-readable-p][f-readable-p]]
    - [[#f-writable-p][f-writable-p]]
    - [[#f-executable-p][f-executable-p]]
    - [[#f-absolute-p][f-absolute-p]]
    - [[#f-relative-p][f-relative-p]]
    - [[#f-root-p][f-root-p]]
    - [[#f-ext-p][f-ext-p]]
    - [[#f-same-p][f-same-p]]
    - [[#f-parent-of-p][f-parent-of-p]]
    - [[#f-child-of-p][f-child-of-p]]
    - [[#f-ancestor-of-p][f-ancestor-of-p]]
    - [[#f-descendant-of-p][f-descendant-of-p]]
    - [[#f-hidden-p][f-hidden-p]]
    - [[#f-empty-p][f-empty-p]]
    - [[#f-newer-p][f-newer-p]]
    - [[#f-older-p][f-older-p]]
    - [[#f-same-time-p][f-same-time-p]]
  - [[#stats][Stats]]
    - [[#f-size][f-size]]
    - [[#f-depth][f-depth]]
    - [[#f-change-time][f-change-time]]
    - [[#f-modification-time][f-modification-time]]
    - [[#f-access-time][f-access-time]]
  - [[#misc][Misc]]
    - [[#f-this-file][f-this-file]]
    - [[#f-path-separator][f-path-separator]]
    - [[#f-glob][f-glob]]
    - [[#f-entries][f-entries]]
    - [[#f-directories][f-directories]]
    - [[#f-files][f-files]]
    - [[#f-root][f-root]]
    - [[#f-traverse-upwards][f-traverse-upwards]]
    - [[#f-with-sandbox][f-with-sandbox]]
- [[#example][Example]]
  - [[#using-standard-emacs-builtin-functions][Using standard Emacs builtin functions]]
  - [[#using-fel][Using f.el]]

* Contributing
Check [[file:./CONTRIBUTING.org]]

* Documentation and examples
** Paths
*** f-join
#+begin_example
(f-join &rest args)

Join ARGS to a single path.

Be aware if one of the arguments is an absolute path, ‘f-join’
will discard all the preceeding arguments and make this absolute
path the new root of the generated path.
#+end_example

#+begin_src emacs-lisp
(f-join "path") ;; => "path"
(f-join "path" "to") ;; => "path/to"
(f-join "/" "path" "to" "heaven") ;; => "/path/to/heaven"
(f-join "path" "/to" "file") ;; => "/to/file"
#+end_src

*** f-split
#+begin_example
(f-split path)

Split PATH and return list containing parts.
#+end_example

#+begin_src emacs-lisp
(f-split "path") ;; => '("path")
(f-split "path/to") ;; => '("path" "to")
(f-split "/path/to/heaven") ;; => '("/" "path" "to" "heaven")
(f-split "~/back/to/earth") ;; => '("~" "back" "to" "earth")
#+end_src

*** f-expand
#+begin_example
(f-expand path &optional dir)

Expand PATH relative to DIR (or ‘default-directory’).
PATH and DIR can be either a directory names or directory file
names.  Return a directory name if PATH is a directory name, and
a directory file name otherwise.  File name handlers are
ignored.
#+end_example

#+begin_src emacs-lisp
(f-expand "name") ;; => "/default/directory/name"
(f-expand "name" "other/directory") ;; => "other/directory/name"
#+end_src

*** f-filename
#+begin_example
(f-filename path)

Return the name of PATH.
#+end_example

#+begin_src emacs-lisp
(f-filename "path/to/file.ext") ;; => "file.ext"
(f-filename "path/to/directory") ;; => "directory"
#+end_src

*** f-dirname
#+begin_example
(f-dirname path)

Return the parent directory to PATH.
#+end_example

Alias: ~f-parent~

#+begin_src emacs-lisp
(f-dirname "path/to/file.ext") ;; => "path/to"
(f-dirname "path/to/directory") ;; => "path/to"
(f-dirname "/") ;; => nil
#+end_src

*** f-common-parent
#+begin_example
(f-common-parent paths)

Return the deepest common parent directory of PATHS.
#+end_example

#+begin_src emacs-lisp
(f-common-parent '("foo/bar/baz" "foo/bar/qux" "foo/bar/mux")) ;; => "foo/bar/"
(f-common-parent '("/foo/bar/baz" "/foo/bar/qux" "/foo/bax/mux")) ;; => "/foo/"
(f-common-parent '("foo/bar/baz" "quack/bar/qux" "lack/bar/mux")) ;; => ""
#+end_src

*** f-ext
#+begin_example
(f-ext path)
#+end_example

Alias of ~file-name-extension~

#+begin_src emacs-lisp
(f-ext "path/to/file") ;; => nil
(f-ext "path/to/file.txt") ;; => txt
(f-ext "path/to/file.txt.org") ;; => org
#+end_src

*** f-no-ext
#+begin_example
(f-no-ext path)
#+end_example

Alias of ~file-name-sans-extension~

#+begin_src emacs-lisp
(f-no-ext "path/to/file") ;; => path/to/file
(f-no-ext "path/to/file.txt") ;; => path/to/file
(f-no-ext "path/to/file.txt.org") ;; => path/to/file.txt
#+end_src

*** f-swap-ext
#+begin_example
(f-swap-ext path ext)

Return PATH but with EXT as the new extension.
EXT must not be nil or empty.
#+end_example

#+begin_src emacs-lisp
(f-swap-ext "path/to/file.ext" "org") ;; => "path/to/file.org"
(f-swap-ext "path/to/file.ext" "") ;; => error
#+end_src

*** f-base
#+begin_example
(f-base path)

Return the name of PATH, excluding the extension of file.
#+end_example

#+begin_src emacs-lisp
(f-base "path/to/file.ext") ;; => "file"
(f-base "path/to/directory") ;; => nil
#+end_src

*** f-relative
#+begin_example
(f-relative path &optional dir)

Convert FILENAME to be relative to DIRECTORY (default: ‘default-directory’).
This function returns a relative file name that is equivalent to FILENAME
when used with that default directory as the default.
If FILENAME is a relative file name, it will be interpreted as existing in
‘default-directory’.
If FILENAME and DIRECTORY lie on different machines or on different drives
on a DOS/Windows machine, it returns FILENAME in expanded form.

(fn FILENAME &optional DIRECTORY)
#+end_example

#+begin_src emacs-lisp
(f-relative "/some/path/relative/to/my/file.txt" "/some/path/") ;; => relative/to/my/file.txt
(f-relative "/default/directory/my/file.txt") ;; => my/file.txt
#+end_src

*** f-short
#+begin_example
(f-short path)
#+end_example

Alias of ~abbreviate-file-name~

Alias: ~f-abbrev~

#+begin_src emacs-lisp
(f-short "/Users/foo/Code/bar") ;; => ~/Code/bar
(f-short "/path/to/Code/bar") ;; => /path/to/Code/bar
#+end_src

*** f-long
#+begin_example
(f-long path)

Return long version of PATH.
#+end_example

#+begin_src emacs-lisp
(f-long "~/Code/bar") ;; => /Users/foo/Code/bar
(f-long "/path/to/Code/bar") ;; => /path/to/Code/bar
#+end_src

*** f-canonical
#+begin_example
(f-canonical path)
#+end_example

Alias of ~file-truename~

#+begin_src emacs-lisp
(f-canonical "/path/to/real/file") ;; => /path/to/real/file
(f-canonical "/link/to/file") ;; => /path/to/real/file
#+end_src

*** f-slash
#+begin_example
(f-slash path)

Append slash to PATH unless one already.

Some functions, such as ‘call-process’ requires there to be an
ending slash.
#+end_example

#+begin_src emacs-lisp
(f-slash "/path/to/file") ;; => /path/to/file
(f-slash "/path/to/dir") ;; => /path/to/dir/
(f-slash "/path/to/dir/") ;; => /path/to/dir/
#+end_src

*** f-full
#+begin_example
(f-full path)

Return absolute path to PATH, with ending slash.
#+end_example

#+begin_src emacs-lisp
(f-full "~/path/to/file") ;; => /home/foo/path/to/file
(f-full "~/path/to/dir") ;; => /home/foo/path/to/dir/
(f-full "~/path/to/dir/") ;; => /home/foo/path/to/dir/
#+end_src

*** f-uniquify
#+begin_example
(f-uniquify paths)

Return unique suffixes of FILES.

This function expects no duplicate paths.
#+end_example

#+begin_src emacs-lisp
(f-uniquify '("/foo/bar" "/foo/baz" "/foo/quux")) ;; => '("bar" "baz" "quux")
(f-uniquify '("/foo/bar" "/www/bar" "/foo/quux")) ;; => '("foo/bar" "www/bar" "quux")
(f-uniquify '("/foo/bar" "/www/bar" "/www/bar/quux")) ;; => '("foo/bar" "www/bar" "quux")
(f-uniquify '("/foo/bar" "/foo/baz" "/home/www/bar" "/home/www/baz" "/var/foo" "/opt/foo/www/baz")) ;; => '("foo/bar" "www/bar" "foo/baz" "home/www/baz" "foo/www/baz" "foo")
#+end_src

*** f-uniquify-alist
#+begin_example
(f-uniquify-alist paths)

Return alist mapping FILES to unique suffixes of FILES.

This function expects no duplicate paths.
#+end_example

#+begin_src emacs-lisp
(f-uniquify-alist '("/foo/bar" "/foo/baz" "/foo/quux")) ;; => '(("/foo/bar" . "bar") ("/foo/baz" . "baz") ("/foo/quux" . "quux"))
(f-uniquify-alist '("/foo/bar" "/www/bar" "/foo/quux")) ;; => '(("/foo/bar" . "foo/bar") ("/www/bar" . "www/bar") ("/foo/quux" . "quux"))
(f-uniquify-alist '("/foo/bar" "/www/bar" "/www/bar/quux")) ;; => '(("/foo/bar" . "foo/bar") ("/www/bar" . "www/bar") ("/www/bar/quux" . "quux"))
(f-uniquify-alist '("/foo/bar" "/foo/baz" "/home/www/bar" "/home/www/baz" "/var/foo" "/opt/foo/www/baz")) ;; => '(("/foo/bar" . "foo/bar") ("/home/www/bar" . "www/bar") ("/foo/baz" . "foo/baz") ("/home/www/baz" . "home/www/baz") ("/opt/foo/www/baz" . "foo/www/baz") ("/var/foo" . "foo"))
#+end_src

** I/O
*** f-read-bytes
#+begin_example
(f-read-bytes path)

Read binary data from PATH.

Return the binary data as unibyte string.  The optional second
and third arguments BEG and END specify what portion of the file
to read.
#+end_example

#+begin_src emacs-lisp
(f-read-bytes "path/to/binary/data")
#+end_src

*** f-write-bytes
#+begin_example
(f-write-bytes data path)

Write binary DATA to PATH.

DATA is a unibyte string.  PATH is a file name to write to.
#+end_example

#+begin_src emacs-lisp
(f-write-bytes (unibyte-string 72 101 108 108 111 32 119 111 114 108 100) "path/to/binary/data")
#+end_src

*** f-append-bytes
#+begin_example
(f-append-bytes text coding path)

Append binary DATA to PATH.

If PATH does not exist, it is created.
#+end_example

#+begin_src emacs-lisp
(f-append-bytes "path/to/file" (unibyte-string 72 101 108 108 111 32 119 111 114 108 100))
#+end_src

*** f-read-text
#+begin_example
(f-read-text path &optional coding)

Read text with PATH, using CODING.

CODING defaults to ‘utf-8’.

Return the decoded text as multibyte string.
#+end_example

Alias: ~f-read~

#+begin_src emacs-lisp
(f-read-text "path/to/file.txt" 'utf-8)
(f-read "path/to/file.txt" 'utf-8)
#+end_src

*** f-write-text
#+begin_example
(f-write-text text coding path)

Write TEXT with CODING to PATH.

TEXT is a multibyte string.  CODING is a coding system to encode
TEXT with.  PATH is a file name to write to.
#+end_example

Alias: ~f-write~

#+begin_src emacs-lisp
(f-write-text "Hello world" 'utf-8 "path/to/file.txt")
(f-write "Hello world" 'utf-8 "path/to/file.txt")
#+end_src

*** f-append-text
#+begin_example
(f-append-text text coding path)

Append TEXT with CODING to PATH.

If PATH does not exist, it is created.
#+end_example

Alias: ~f-append~

#+begin_src emacs-lisp
(f-append-text "Hello world" 'utf-8 "path/to/file.txt")
(f-append "Hello world" 'utf-8 "path/to/file.txt")
#+end_src

** Destructive
*** f-mkdir
#+begin_example
(f-mkdir &rest dirs)

Create directories DIRS.

DIRS should be a successive list of directories forming together
a full path.  The easiest way to call this function with a fully
formed path is using ‘f-split’ alongside it:

    (apply #'f-mkdir (f-split "path/to/file"))

Although it works sometimes, it is not recommended to use fully
formed paths in the function. In this case, it is recommended to
use ‘f-mkdir-full-path’ instead.
#+end_example

#+begin_src emacs-lisp
(f-mkdir "dir") ;; creates /default/directory/dir
(f-mkdir "other" "dir") ;; creates /default/directory/other/dir
(f-mkdir "/" "some" "path") ;; creates /some/path
(f-mkdir "~" "yet" "another" "dir") ;; creates ~/yet/another/dir
#+end_src

*** f-mkdir-full-path
#+begin_example
(f-mkdir-full-path dir)

Create DIR from a full path.

This function is similar to ‘f-mkdir’ except it can accept a full
path instead of requiring several successive directory names.
#+end_example

#+begin_src emacs-lisp
(f-mkdir-full-path "dir") ;; creates /default/directory/dir
(f-mkdir-full-path "other/dir") ;; creates /default/directory/other/dir
(f-mkdir-full-path "/some/path") ;; creates /some/path
(f-mkdir-full-path "~/yet/another/dir") ;; creates ~/yet/another/dir
#+end_src

*** f-delete
#+begin_example
(f-delete path &optional force)

Delete PATH, which can be file or directory.

If FORCE is t, a directory will be deleted recursively.
#+end_example

#+begin_src emacs-lisp
(f-delete "dir")
(f-delete "other/dir" t)
(f-delete "path/to/file.txt")
#+end_src

*** f-symlink
#+begin_example
(f-symlink source path)

Create a symlink to SOURCE from PATH.
#+end_example

#+begin_src emacs-lisp
(f-symlink "path/to/source" "path/to/link")
#+end_src

*** f-move
#+begin_example
(f-move from to)

Move or rename FROM to TO.
If TO is a directory name, move FROM into TO.
#+end_example

#+begin_src emacs-lisp
(f-move "path/to/file.txt" "new-file.txt")
(f-move "path/to/file.txt" "other/path")
#+end_src

*** f-copy
#+begin_example
(f-copy from to)

Copy file or directory FROM to TO.
If FROM names a directory and TO is a directory name, copy FROM
into TO as a subdirectory.
#+end_example

#+begin_src emacs-lisp
(f-copy "path/to/file.txt" "new-file.txt")
(f-copy "path/to/dir" "other/dir")
#+end_src

*** f-copy-contents
#+begin_example
(f-copy-contents from to)

Copy contents in directory FROM, to directory TO.
#+end_example

#+begin_src emacs-lisp
(f-copy-contents "path/to/dir" "path/to/other/dir")
#+end_src

*** f-touch
#+begin_example
(f-touch path)

Update PATH last modification date or create if it does not exist.
#+end_example

#+begin_src emacs-lisp
(f-touch "path/to/existing/file.txt")
(f-touch "path/to/non/existing/file.txt")
#+end_src

** Predicates
*** f-exists-p
#+begin_example
(f-exists-p path)
#+end_example

Alias of ~file-exists-p~

Alias: ~f-exists?~

#+begin_src emacs-lisp
(f-exists-p "path/to/file.txt")
(f-exists-p "path/to/dir")
#+end_src

*** f-directory-p
#+begin_example
(f-directory-p path)
#+end_example

Alias of ~file-directory-p~

Aliases:
- ~f-directory?~
- ~f-dir-p~
- ~f-dir?~

#+begin_src emacs-lisp
(f-directory-p "path/to/file.txt") ;; => nil
(f-directory-p "path/to/dir") ;; => t
#+end_src

*** f-file-p
#+begin_example
(f-file-p path)
#+end_example

Alias of ~file-regular-p~

Alias: ~f-file?~

#+begin_src emacs-lisp
(f-file-p "path/to/file.txt") ;; => t
(f-file-p "path/to/dir") ;; => nil
#+end_src

*** f-symlink-p
#+begin_example
(f-symlink-p path)

Return t if PATH is symlink, false otherwise.
#+end_example

Alias: ~f-symlink?~

#+begin_src emacs-lisp
(f-symlink-p "path/to/file.txt") ;; => nil
(f-symlink-p "path/to/dir") ;; => nil
(f-symlink-p "path/to/link") ;; => t
#+end_src

*** f-readable-p
#+begin_example
(f-readable-p path)
#+end_example

Alias of ~file-readable-p~

Alias: ~f-readable?~

#+begin_src emacs-lisp
(f-readable-p "path/to/file.txt")
(f-readable-p "path/to/dir")
#+end_src

*** f-writable-p
#+begin_example
(f-writable-p path)
#+end_example

Alias of ~file-writable-p~

Alias: ~f-writable?~

#+begin_src emacs-lisp
(f-writable-p "path/to/file.txt")
(f-writable-p "path/to/dir")
#+end_src

*** f-executable-p
#+begin_example
(f-executable-p path)
#+end_example

Alias of ~file-executable-p~

Alias: ~f-executable?~

#+begin_src emacs-lisp
(f-executable-p "path/to/file.txt")
(f-executable-p "path/to/dir")
#+end_src

*** f-absolute-p
#+begin_example
(f-absolute-p path)
#+end_example

Alias of ~file-name-absolute-p~

Alias: ~f-absolute?~

#+begin_src emacs-lisp
(f-absolute-p "path/to/dir") ;; => nil
(f-absolute-p "/full/path/to/dir") ;; => t
#+end_src

*** f-relative-p
#+begin_example
(f-relative-p path)

Return t if PATH is relative, false otherwise.
#+end_example

Alias: ~f-relative?~

#+begin_src emacs-lisp
(f-relative-p "path/to/dir") ;; => t
(f-relative-p "/full/path/to/dir") ;; => nil
#+end_src

*** f-root-p
#+begin_example
(f-root-p path)

Return t if PATH is root directory, false otherwise.
#+end_example

Alias: ~f-root?~

#+begin_src emacs-lisp
(f-root-p "/") ;; => t
(f-root-p "/not/root") ;; => nil
#+end_src

*** f-ext-p
#+begin_example
(f-ext-p path ext)

Return t if extension of PATH is EXT, false otherwise.

If EXT is nil or omitted, return t if PATH has any extension,
false otherwise.

The extension, in a file name, is the part that follows the last
’.’, excluding version numbers and backup suffixes.
#+end_example

Alias: ~f-ext?~

#+begin_src emacs-lisp
(f-ext-p "path/to/file.el" "el") ;; => t
(f-ext-p "path/to/file.el" "txt") ;; => nil
(f-ext-p "path/to/file.el") ;; => t
(f-ext-p "path/to/file") ;; => nil
#+end_src

*** f-same-p
#+begin_example
(f-same-p path-a path-b)

Return t if PATH-A and PATH-B are references to same file.
#+end_example

Aliases:
- ~f-same?~
- ~f-equal-p~
- ~f-equal?~

#+begin_src emacs-lisp
(f-same-p "foo.txt" "foo.txt") ;; => t
(f-same-p "/path/to/foo.txt" "/path/to/bar.txt") ;; => nil
(f-same-p "foo/bar/../baz" "foo/baz") ;; => t
#+end_src

*** f-parent-of-p
#+begin_example
(f-parent-of-p path-a path-b)

Return t if PATH-A is parent of PATH-B.
#+end_example

Alias: ~f-parent-of?~

#+begin_src emacs-lisp
(f-parent-of-p "/path/to" "/path/to/dir") ;; => t
(f-parent-of-p "/path/to/dir" "/path/to") ;; => nil
(f-parent-of-p "/path/to" "/path/to") ;; => nil
#+end_src

*** f-child-of-p
#+begin_example
(f-child-of-p path-a path-b)

Return t if PATH-A is child of PATH-B.
#+end_example

Alias: ~f-child-of?~

#+begin_src emacs-lisp
(f-child-of-p "/path/to" "/path/to/dir") ;; => nil
(f-child-of-p "/path/to/dir" "/path/to") ;; => t
(f-child-of-p "/path/to" "/path/to") ;; => nil
#+end_src

*** f-ancestor-of-p
#+begin_example
(f-ancestor-of-p path-a path-b)

Return t if PATH-A is ancestor of PATH-B.
#+end_example

Alias: ~f-ancestor-of?~

#+begin_src emacs-lisp
(f-ancestor-of-p "/path/to" "/path/to/dir") ;; => t
(f-ancestor-of-p "/path" "/path/to/dir") ;; => t
(f-ancestor-of-p "/path/to/dir" "/path/to") ;; => nil
(f-ancestor-of-p "/path/to" "/path/to") ;; => nil
#+end_src

*** f-descendant-of-p
#+begin_example
(f-descendant-of-p path-a path-b)

Return t if PATH-A is desendant of PATH-B.
#+end_example

Alias: ~f-descendant-of?~

#+begin_src emacs-lisp
(f-descendant-of-p "/path/to/dir" "/path/to") ;; => t
(f-descendant-of-p "/path/to/dir" "/path") ;; => t
(f-descendant-of-p "/path/to" "/path/to/dir") ;; => nil
(f-descendant-of-p "/path/to" "/path/to") ;; => nil
#+end_src

*** f-hidden-p
#+begin_example
(f-hidden-p path)

Return t if PATH is hidden, nil otherwise.

BEHAVIOR controls when a path should be considered as hidden
depending on its value.  Beware, if PATH begins with "./", the
current dir "." will not be considered as hidden.

When BEHAVIOR is nil, it will only check if the path begins with
a dot, as in .a/b/c, and return t if there is one.  This is the
old behavior of f.el left as default for backward-compatibility
purposes.

When BEHAVIOR is ANY, return t if any of the elements of PATH is
hidden, nil otherwise.

When BEHAVIOR is LAST, return t only if the last element of PATH
is hidden, nil otherwise.

TODO: Hidden directories and files on Windows are marked
differently than on *NIX systems.  This should be properly
implemented.
#+end_example

Alias: ~f-hidden?~

#+begin_src emacs-lisp
(f-hidden-p "path/to/foo") ;; => nil
(f-hidden-p ".path/to/foo") ;; => t
(f-hidden-p "path/.to/foo") ;; => nil
(f-hidden-p "path/to/.foo") ;; => nil
(f-hidden-p ".path/to/foo" 'any) ;; => t
(f-hidden-p "path/.to/foo" 'any) ;; => t
(f-hidden-p "path/to/.foo" 'any) ;; => t
(f-hidden-p ".path/to/foo" 'last) ;; => nil
(f-hidden-p "path/.to/foo" 'last) ;; => nil
(f-hidden-p "path/to/.foo" 'last) ;; => t
#+end_src

*** f-empty-p
#+begin_example
(f-empty-p path)

If PATH is a file, return t if the file in PATH is empty, nil otherwise.
If PATH is directory, return t if directory has no files, nil otherwise.
#+end_example

Alias: ~f-empty?~

#+begin_src emacs-lisp
(f-empty-p "/path/to/empty-file") ;; => t
(f-empty-p "/path/to/file-with-contents") ;; => nil
(f-empty-p "/path/to/empty-dir/") ;; => t
(f-empty-p "/path/to/dir-with-contents/") ;; => nil
#+end_src

*** f-newer-p
#+begin_example
(f-newer-p file other &optional method)

Compare if FILE is newer than OTHER.

For more info on METHOD, see ‘f--date-compare’.
#+end_example

Alias: ~f-newer?~

#+begin_src emacs-lisp
(f-newer-p "newer.txt" "older.txt") ;; t
(f-newer-p "older.txt""newer.txt" ) ;; nil
(f-newer-p "same1.txt" "same2.txt") ;; nil
#+end_src

*** f-older-p
#+begin_example
(f-older-p file other &optional method)

Compare if FILE is older than OTHER.

For more info on METHOD, see ‘f--date-compare’.
#+end_example

Alias: ~f-older?~

#+begin_src emacs-lisp
(f-older-p "older.txt" "newer.txt") ;; t
(f-older-p "newer.txt""older.txt" ) ;; nil
(f-older-p "same1.txt" "same2.txt") ;; nil
#+end_src

*** f-same-time-p
#+begin_example
(f-same-time-p file other &optional method)

Check if FILE and OTHER share the same access or modification time.

For more info on METHOD, see ‘f--date-compare’.
#+end_example

Alias: ~f-same-time?~

#+begin_src emacs-lisp
(f-same-time-p "same1.txt" "same2.txt") ;; t
(f-same-time-p "newer.txt" "older.txt") ;; nil
(f-same-time-p "older.txt" "newer.txt") ;; nil
#+end_src

** Stats
*** f-size
#+begin_example
(f-size path)

Return size of PATH.

If PATH is a file, return size of that file.  If PATH is
directory, return sum of all files in PATH.
#+end_example

#+begin_src emacs-lisp
(f-size "path/to/file.txt")
(f-size "path/to/dir")
#+end_src

*** f-depth
#+begin_example
(f-depth path)

Return the depth of PATH.

At first, PATH is expanded with ‘f-expand’.  Then the full path is used to
detect the depth.
’/’ will be zero depth,  ’/usr’ will be one depth.  And so on.
#+end_example

#+begin_src emacs-lisp
(f-depth "/") ;; 0
(f-depth "/var/") ;; 1
(f-depth "/usr/local/bin") ;; 3
#+end_src

*** f-change-time
#+begin_example
(f-change-time path &optional timestamp-p)

Return the last status change time of PATH.

The status change time (ctime) of PATH in the same format as
‘current-time’.  For details on TIMESTAMP-P and the format of the
returned value, see ‘f--get-time’.
#+end_example

#+begin_src emacs-lisp
(f-change-time "path/to/file.txt")         ;; (25517 48756 26337 111000)
(f-change-time "path/to/dir")              ;; (25517 57887 344657 210000)
(f-change-time "path/to/file.txt" t)       ;; (1672330868026337111 . 1000000000)
(f-change-time "path/to/dir" t)            ;; (1672339999344657210 . 1000000000)
(f-change-time "path/to/file.txt"'seconds) ;; 1672330868
(f-change-time "path/to/dir"'seconds)      ;; 1672339999
#+end_src

*** f-modification-time
#+begin_example
(f-modification-time path &optional timestamp-p)

Return the last modification time of PATH.
The modification time (mtime) of PATH in the same format as
‘current-time’.  For details on TIMESTAMP-P and the format of the
returned value, see ‘f--get-time’.
#+end_example

#+begin_src emacs-lisp
(f-modification-time "path/to/file.txt")          ;; (25517 48756 26337 111000)
(f-modification-time "path/to/dir")               ;; (25517 57887 344657 210000)
(f-modification-time "path/to/file.txt" t)        ;; (1672330868026337111 . 1000000000)
(f-modification-time "path/to/dir" t)             ;; (1672339999344657210 . 1000000000)
(f-modification-time "path/to/file.txt" 'seconds) ;; 1672330868
(f-modification-time "path/to/dir" 'seconds)      ;; 1672339999
#+end_src

*** f-access-time
#+begin_example
(f-access-time path &optional timestamp-p)

Return the last access time of PATH.
The access time (atime) of PATH is in the same format as
‘current-time’.  For details on TIMESTAMP-P and the format of the
returned value, see ‘f--get-time’.
#+end_example

#+begin_src emacs-lisp
(f-access-time "path/to/file.txt")          ;; (25517 48756 26337 111000)
(f-access-time "path/to/dir")               ;; (25517 57887 344657 210000)
(f-access-time "path/to/file.txt" t)        ;; (1672330868026337111 . 1000000000)
(f-access-time "path/to/dir" t)             ;; (1672339999344657210 . 1000000000)
(f-access-time "path/to/file.txt" 'seconds) ;; 1672330868
(f-access-time "path/to/dir" 'seconds)      ;; 1672339999
#+end_src

** Misc
*** f-this-file
#+begin_example
(f-this-file)

Return path to this file.
#+end_example

#+begin_src emacs-lisp
(f-this-file) ;; => /path/to/this/file
#+end_src

*** f-path-separator
#+begin_example
(f-path-separator)

Return path separator.
#+end_example

#+begin_src emacs-lisp
(f-path-separator) ;; => /
#+end_src

*** f-glob
#+begin_example
(f-glob pattern &optional path)

Find PATTERN in PATH.
#+end_example

#+begin_src emacs-lisp
(f-glob "path/to/*.el")
(f-glob "*.el" "path/to")
#+end_src

*** f-entries
#+begin_example
(f-entries path &optional fn recursive)

Find all files and directories in PATH.

FN - called for each found file and directory.  If FN returns a thruthy
value, file or directory will be included.
RECURSIVE - Search for files and directories recursive.
#+end_example

#+begin_src emacs-lisp
(f-entries "path/to/dir")
(f-entries "path/to/dir" (lambda (file) (s-matches? "test" file)))
(f-entries "path/to/dir" nil t)
(f--entries "path/to/dir" (s-matches? "test" it))
#+end_src

*** f-directories
#+begin_example
(f-directories path &optional fn recursive)

Find all directories in PATH.  See ‘f-entries’.
#+end_example

#+begin_src emacs-lisp
(f-directories "path/to/dir")
(f-directories "path/to/dir" (lambda (dir) (equal (f-filename dir) "test")))
(f-directories "path/to/dir" nil t)
(f--directories "path/to/dir" (equal (f-filename it) "test"))
#+end_src

*** f-files
#+begin_example
(f-files path &optional fn recursive)

Find all files in PATH.  See ‘f-entries’.
#+end_example

#+begin_src emacs-lisp
(f-files "path/to/dir")
(f-files "path/to/dir" (lambda (file) (equal (f-ext file) "el")))
(f-files "path/to/dir" nil t)
(f--files "path/to/dir" (equal (f-ext it) "el"))
#+end_src

*** f-root
#+begin_example
(f-root)

Return absolute root.
#+end_example

#+begin_src emacs-lisp
(f-root) ;; => "/"
#+end_src

*** f-traverse-upwards
#+begin_example
(f-traverse-upwards fn &optional path)

Traverse up as long as FN return nil, starting at PATH.

If FN returns a non-nil value, the path sent as argument to FN is
returned.  If no function callback return a non-nil value, nil is
returned.
#+end_example

#+begin_src emacs-lisp
(f-traverse-upwards
 (lambda (path)
   (f-exists? (f-expand ".git" path)))
 start-path)

(f--traverse-upwards (f-exists? (f-expand ".git" it)) start-path) ;; same as above
#+end_src

*** f-with-sandbox
#+begin_example
(f-with-sandbox path-or-paths &rest body)

Only allow PATH-OR-PATHS and descendants to be modified in BODY.
#+end_example

#+begin_src emacs-lisp
(f-with-sandbox foo-path
  (f-touch (f-expand "foo" foo-path)))
(f-with-sandbox (list foo-path bar-path)
  (f-touch (f-expand "foo" foo-path))
  (f-touch (f-expand "bar" bar-path)))
(f-with-sandbox foo-path
  (f-touch (f-expand "bar" bar-path))) ;; "Destructive operation outside sandbox"
#+end_src

* Example

Here's an example of a function that finds the Git project root.

** Using standard Emacs builtin functions
#+begin_src emacs-lisp
(defun find-git-root (&optional dir)
  (unless dir (setq dir (expand-file-name (file-name-directory (buffer-file-name)))))
  (let ((parent (expand-file-name ".." dir)))
    (unless (equal parent dir)
      (if (file-exists-p (expand-file-name ".git" dir))
          dir
        (find-git-root parent)))))
#+end_src

** Using f.el
#+begin_src emacs-lisp
(defun find-git-root (&optional dir)
  (interactive)
  (unless dir (setq dir (f-dirname (buffer-file-name))))
  (let ((parent (f-parent dir)))
    (unless (f-root? parent)
      (if (f-exists? (f-expand ".git" dir))
          dir
        (find-git-root parent)))))
#+end_src

Now, try writing it even simpler yourself. Hint, check out ~f-traverse-upwards~.
