PreviousUpNext

15.4.104  src/app/makelib/stuff/autodir.pkg

## Author: Matthias Blume (blume@cs.princeton.edu)

# Compiled by:
#     src/app/makelib/stuff/makelib-stuff.sublib

# Opening files for output while automagically creating any
# necessary directories.

stipulate
    package fil =  file__premicrothread;                                # file__premicrothread  is from   src/lib/std/src/posix/file--premicrothread.pkg
    package p   =  winix__premicrothread::path;                         # winix__premicrothread is from   src/lib/std/winix--premicrothread.pkg
    package f   =  winix__premicrothread::file;
herein

    package autodir
    :
    api {
        open_binary_output:            String -> data_file__premicrothread::Output_Stream;
        open_text_output:              String -> fil::Output_Stream;
        make_all_directories_on_path:  String -> Void;
    }
    {
        fun file_exists n
            =
            f::access (n, [])
            except
                _ = FALSE;

        fun open_either  fileopener  p
            =
            {   fun make_directory d
                    =
                    f::make_directory d
                    except
                        exn =   if (not (file_exists d))
                                    #
                                    raise exception exn;
                                fi;


                fun generic' (maker, pmaker, p)
                    =
                    maker p
                    except
                        exn
                        =
                        {   dir = p::dir p;

                            # If the parent dir exists, then we must consider
                            # these cases:
                            #   - non-parallel: we should signal an error
                            #   - parallel: somebody else may have made this dir
                            #      in the meantime, so we should try again
                            # Both cases can be handled by simply calling maker
                            # again.  (It will fail in the non-parallel case, but
                            # that's actually what we want.)

                            if   (dir == ""  or  file_exists dir)

                                 maker p;
                            else
                                 pmaker dir;
                                 maker p;
                            fi;
                        };


                fun makedirs dir
                    =
                    generic' (make_directory, makedirs, dir);


                fun advertisemakedirs dir
                    =
                    {   fil::say .{ cat ["\n                                        autodir.pkg:   Creating  directory     ", dir, "\n\n"]; };
                        #
                        makedirs dir;
                    };

                generic' (fileopener, advertisemakedirs, p);
            };


        # In the open-for-output case we first
        # get rid of the file if it
        # already existed...

        fun open  fileopener
            =
            open_either (fn n = {   if (file_exists n)
                                        #
                                          f::remove_file n
                                          except
                                              _ = ();
                                    fi;

                                    fileopener n;
                                }
                        );

        open_text_output   =  open                        fil::open_for_write;
        open_binary_output =  open  data_file__premicrothread::open_for_write;

        # make_all_directories_on_path is supposed to make all directories
        # leading up to a given file.  The file itself is supposed to be
        # left alone if it already existed.  The trick here is to (ab)use
        # our open function with a "maker" parameter set to "data_file__premicrothread::open_for_read".
        # This is pretty hack-ish, but it allows us to reuse the existing logic.
        #
        stipulate
            exception NONEXISTENT_FILE;
            #
            fun binary_open_for_input f
                =
                data_file__premicrothread::open_for_read f
                except _
                    =
                    raise exception NONEXISTENT_FILE;
        herein
            fun make_all_directories_on_path f
                =
                data_file__premicrothread::close_input  (open_either  binary_open_for_input  f)
                except
                    NONEXISTENT_FILE
                    =
                    ();
        end;
    };
end;


Comments and suggestions to: bugs@mythryl.org

PreviousUpNext