[m-users.] file_exists? directory_exists?

Julian Fondren jfondren at minimaltype.com
Sat Jun 29 19:03:34 AEST 2019

On 2019-06-29 02:02, emacstheviking wrote:
> Hi,
> All I have found is see/seen to see if a file exists. Have I missed
> something totally obvious ?
> And how about checking the existence of a directory too?

The filesystem can change at any time, including after any "does this
dir exist?" check and before you go on try and use the directory, so
usually what you want to do is behave as if the directory exists and
react gracefully to errors.

Even so, here's a program with two different ways to test if a
directory exists. The first variant will report that extant but
unsearchable (non-executable in Unix) directories do not exist. The
second variant will report that the root directory doesn't exist.

The first works by trying to search a directory and then immediately
ending the search, and saying the directory exists if this didn't
result in an error. The second works by searching the path's parent
directory for an entry named after the basename of the path, which is
also a directory.

The first variant might take a very long time if the directory you're
checking for contains a huge (many millions) of immediate files. The
second variant might take a very long time if this is true of the
parent directory.

I haven't checked how these deal with symbolic links; the latter
probably reports that /path/to/dir doesn't exist if 'dir' is a
symbolic link.

For maximum paranoia in Linux, you'll want to not use any of this, but
instead open a directory with the O_DIRECTORY flag, and then use the
*at syscalls so that all of your filesystems are relative to an opened
directory rather than to paths that might change underneath you
(maliciously or accidentally). This sort of thing is where Mercury's
very flexible FFI comes in handy.

I'd appreciate any nicer ways to define the closures:

:- module opendir.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
:- import_module dir, list, string, bool.

:- pred dir_readable(string::in, bool::out, io::di, io::uo) is det.
dir_readable(Path, Exists, !IO) :-
     dir.foldl2(P, Path, 0, Res, !IO),
     ( Res = ok(_) -> Exists = yes ; Exists = no ),
     P = (pred(_::in, _::in, _::in, no::out, !.D::in, !:D::out, !.IO::di, 
!:IO::uo) is det).

:- pred dir_exists(string::in, bool::out, io::di, io::uo) is det.
dir_exists(Path, Exists, !IO) :-
         split_name(Path, Dir, Base)
         dir.foldl2(P, Dir, no, Res, !IO),
         ( Res = ok(yes) -> Exists = yes ; Exists = no ),
         P = (pred(_::in, BaseName::in, Type::in, Continue::out, !.D::in, 
!:D::out, !.IO::di, !:IO::uo) is det :-
                 ( BaseName = Base, Type = directory -> !:D = yes, 
Continue = no; Continue = yes ))
         Exists = no

main(!IO) :-
     io.command_line_arguments(Args, !IO),
         Args = [Path]
         dir_readable(Path, R, !IO),
         dir_exists(Path, E, !IO),
         io.print({R, E}, !IO),
         io.progname("opendir", Name, !IO),
         io.format(io.stderr_stream, "%s <path>\n", [s(Name)], !IO),
         io.set_exit_status(1, !IO)

> It feels bad to have to use "see" and then "seen" (to undo see) just
> to test a file exists.
> I am sure I have not found the docs but TBH they doc are just the code
> for the module and when you are learning this stuff it makes it pretty
> hard to find anything at times.

The library docs are indeed just the interface section of the module,
but that includes a lot of comments. It's not nearly as bad as bare
"here are function names and function types" kind of documentation.

It's definitely missing examples, though, and that's usually what I
most miss when I'm unfamiliar with something. It's especially painful
to not have even really trivial examples when you're not real solid on
some part of the language, as you can get into a loop of

   1. try to compile, get error
   2. change something that you're confused about, because you're
      confused about it, even though it has no actual bearing on the
      error at hand.
   3. get a new error because you broke the unrelated thing. Hooray!
      This is progress! Now you just have to keep fiddling with that
      unrelated thing and the program will surely compile!
   4. GOTO 2

I recommend having the Mercury source code handy and diving it for
examples. There's sample code, and there's the library code itself.

The language manual can also help you break out of loops.

> Thanks,
> Sean.
> _______________________________________________
> users mailing list
> users at lists.mercurylang.org
> https://lists.mercurylang.org/listinfo/users

More information about the users mailing list