:- module(system, 
        [
            pause/1,
            time/1,
            datime/1,
            datime/9,
            datime_struct/1,
            getenvstr/2,
            setenvstr/2,
            extract_paths/2,
            get_pid/1,
            current_host/1,
            current_executable/1,
            umask/2,
            make_directory/2,
            make_directory/1,
	    make_dirpath/2,
	    make_dirpath/1,
            working_directory/2,
            cd/1,
            shell/0,
            shell/1,
            shell/2,
            system/1,
            system/2,
            popen/3,
            popen_mode/1,
            exec/4,
            exec/3,
            exec/8,
            wait/3,
            directory_files/2,
            mktemp/2,
            file_exists/1,
            file_exists/2,
            file_property/2,
            file_properties/6,
            modif_time/2,
            modif_time0/2,
            fmode/2,
            chmod/2,
            chmod/3,
            delete_file/1,
            delete_directory/1,
            rename_file/2,
	    cyg2win/3
        ],
	[assertions, isomodes, regtypes]).

:- comment(title, "Operating system utilities").

:- comment(author, "Daniel Cabeza").
:- comment(author, "Manuel Carro").

:- comment(module, "This module contains predicates for invoking
   services which are typically provided by the operating system.
   Note that the predicates which take names of files or directories
   as arguments in this module expect atoms, not @concept{path
   alias}es. I.e., generally these predicates will not call
   @pred{absolute_file_name/2} on names of files or directories taken
   as arguments.").

:- use_module(engine(internals), ['$unix_popen'/3, '$exec'/8]).
:- use_module(library(lists), [append/3]).

:- impl_defined([
        working_directory/2, directory_files/2, pause/1, time/1, datime/9,
        current_host/1, getenvstr/2, setenvstr/2, get_pid/1, 
        current_executable/1,
        shell/0, shell/2, system/2, mktemp/2, file_exists/2,
        wait/3,
        file_properties/6, chmod/2, umask/2, 
        delete_file/1, delete_directory/1, rename_file/2, make_directory/2]).


:- multifile define_flag/3.

%% This flag determines which path style is returned by the system
%% predicates which can return paths.  'os_dependent' means paths 
%% will be returned in the forma0-t the OS accepts it.  'posix' means
%% using a POSIX-style, with slashes, no drive letters or UNC paths,
%% and semicolons as separators.

define_flag(path_style, [posix, os_dependent], os_dependent).


:- comment(pause(Seconds), "Make this thread sleep for some @var{Seconds}.").

:- true pred pause(+int).

:- comment(time(Time), "@var{Time} is unified with the number of seconds
     elapsed since January, 1, 1970 (UTC).").

:- true pred time(?int).

 %% :- comment(walltime(Time),"@var{Time} is unified with the time in
 %%      milliseconds elapsed in the real world since the last call to
 %%      @pred{walltime/1}. The first call returns a meaningless number.").
 %% 
 %% :- true pred walltime(?int).

:- comment(datime(Datime), "@var{Datime} is unified with a term of the
     form @tt{datime(Year,Month,Day,Hour,Minute,Second)} which contains
     the current date and time.").

:- true pred datime(?datime_struct).

datime(datime(Year,Month,Day,Hour,Min,Sec)) :-
        datime(_, Year, Month, Day, Hour, Min, Sec, _, _).

:- true regtype datime_struct/1.

datime_struct(datime(Year,Month,Day,Hour,Min,Sec)) :-
        int(Year), int(Month), int(Day), int(Hour), int(Min), int(Sec).

:- comment(datime(Time,Year,Month,Day,Hour,Min,Sec,WeekDay,YearDay),
	"@var{Time} is as in @pred{time/1}. @var{WeekDay} is the number
	of days since Sunday, in the range 0 to 6.  @var{YearDay} is the
	number of days since January 1, in the range 0 to 365.").

:- true pred datime(+int,?int,?int,?int,?int,?int,?int,?int,?int)
        # "If @var{Time} is given, the rest of the arguments are unified
        with the date and time to which the @var{Time} argument refers.".

:- true pred datime(-int,?int,?int,?int,?int,?int,?int,?int,?int)
	# "Bound @var{Time} to current time and the rest of the
	arguments refer to current time.".

:- comment(getenvstr(Name, Value), "The environment variable @var{Name}
    has @var{Value}.  Fails if variable @var{Name} is not defined.").

:- true pred getenvstr(+atm, ?string).

:- comment(setenvstr(Name, Value), "The environment variable @var{Name}
    is assigned @var{Value}.").

:- true pred setenvstr(+atm, +string).


:- comment(extract_paths(String, Paths), "Interpret @var{String} as the
   value of a UNIX environment variable holding a list of paths and
   return in @var{Paths} the list of the paths.  Paths in @var{String}
   are separated by colons, and an empty path is considered a shorthand
   for '.' (current path).  The most typical environment variable with this
   format is PATH.  For example, this is a typical use:
@begin{verbatim}
?- set_prolog_flag(write_strings, on).

yes
?- getenvstr('PATH', PATH), extract_paths(PATH, Paths).

PATH = "":/home/bardo/bin:/home/clip/bin:/opt/bin/:/bin"",
Paths = [""."",""/home/bardo/bin"",""/home/clip/bin"",""/opt/bin/"",""/bin""] ?

yes
?- 
@end{verbatim}
").

:- true pred extract_paths(+string, ?list(string)).

extract_paths([], ["."]).
extract_paths([C|Cs], [Path|Paths]) :-
        extract_path(C, Cs, ".", Path, Cs_),
        extract_paths_(Cs_, Paths).

extract_paths_([], []).
extract_paths_([_|Cs], Paths) :- % skip ":"
        extract_paths(Cs, Paths).

extract_path(0':, Cs, Path, Path, [0':|Cs]) :- !.
extract_path(C, [], _, [C], []) :- !.
extract_path(C, [D|Cs], _, [C|Path], Cs_) :-
        extract_path(D, Cs, [], Path, Cs_).


:- comment(get_pid(Pid), "Unifies @var{Pid} with the process
     identificator of the current process or thread.").

:- true pred get_pid(?int).

:- comment(current_host(Hostname), "@var{Hostname} is unified with the
        fully qualified name of the host.").

:- true pred current_host(?atm).

:- comment(current_executable(Path), "Unifies @var{Path} with the path
        to the current executable.").

:- true pred current_executable(?atm).

:- comment(umask(OldMask, NewMask), "The process file creation mask was
    @var{OldMask}, and it is changed to @var{NewMask}.").

:- true pred umask(?int, +int).

:- true pred umask(OldMask, NewMask)
        : (var(OldMask), var(NewMask), OldMask == NewMask)
       => (int(OldMask), int(NewMask))
        # "Gets the process file creation mask without changing it.".

:- comment(working_directory(OldDir, NewDir),"Unifies current working
     directory with @var{OldDir}, and then changes the working
     directory to @var{NewDir}. Calling
     @tt{working_directory(Dir,Dir)} simply unifies @tt{Dir} with the
     current working directory without changing anything else.").

:- true pred working_directory(?atm, +atm).

:- true pred working_directory(OldDir, NewDir)
        : (var(OldDir), var(NewDir), OldDir == NewDir) => atm * atm
        # "Gets current working directory.".

:- comment(cd(Path), "Changes working directory to @var{Path}.").

:- true pred cd(+atm).

cd(Dir) :- working_directory(_, Dir).

:- true pred shell # "Execs the shell specified by the environment
   variable @tt{SHELL}. When the shell process terminates, control is
   returned to Prolog.".

:- comment(bug, "@pred{shell/n} commands have a bug in Windows: if the
   environment variable SHELL is instantiated to some Windows shell
   implementation, then it is very possible that shell/@{1,2@} will not
   work, as it is always called with the -c flag to start the user
   command.  For example, COMMAND.COM @bf{might} need the flag /C -- but
   there is no way to know a priori which command line option is
   necessary for every shell!  It does not seems usual that Windows sets
   the SHELL environment variable: if it is not set, we set it up at
   startup time to point to the @tt{sh.exe} provided with Ciao, which is
   able to start Windows aplications.  Therefore, @tt{?-
   shell('command.com').} just works.").

:- comment(shell(Command), "@var{Command} is executed in the shell
    specified by the environment variable @tt{SHELL}. It succeeds if
    the exit code is zero and fails otherwise.").

%%      On MSDOS or Windows, if `SHELL' is defined it is expected to
%%      name a UNIX like shell which will be invoked with the argument `-c
%%      COMMAND'. If `SHELL' is undefined, the shell named by `COMSPEC'
%%      will be invoked with the argument `/C COMMAND'.

:- true pred shell(+atm).

shell(Path) :- shell(Path, 0).

:- comment(shell(Command, ReturnCode), "Executes @var{Command} in the
     shell specified by the environment variable @tt{SHELL} and stores
     the exit code in @var{ReturnCode}.").

:- true pred shell(+atm, ?int).

:- comment(system(Command), "Executes @var{Command} using the shell
        @apl{/bin/sh}.").

:- true pred system(+atm).

system(Path) :- system(Path, _Status).

:- comment(system(Command, ReturnCode), "Executes @var{Command} in the
     @apl{/bin/sh} shell and stores the exit code in @var{ReturnCode}.").

:- true pred system(+atm, ?int).

:- true pred split_atom(+Atom, ?AtomList) : 
                  atm * list(atm) #
  "This predicate decomposes a non-null atom which can have blank spaces 
   into a list of atoms, each one corresponding to the non-blank
   characters in the initial atom.".

split_atom(CommandAndArgs, Split):- 
        name(CommandAndArgs, ComandAndArgsStr),
        split_in_atoms(ComandAndArgsStr, Split).

split_in_atoms("", []).
split_in_atoms(" "||Rest, Atoms):-
        split_in_atoms(Rest, Atoms).
split_in_atoms(StartArg, [Atom|Atoms]):-
        StartArg = [NonBlank|_],
        NonBlank \== 0' ,
        extract_atom(StartArg, RestArgs, AtomStr), 
        name(Atom, AtomStr),
        split_in_atoms(RestArgs, Atoms).

extract_atom("", "", "").
extract_atom(" "||Rest, Rest, "").
extract_atom([Char|Chars], Rest, [Char|RestAtom]):-
        Char \== 0' ,
        extract_atom(Chars, Rest, RestAtom).


:- comment(exec(Command, StdIn, StdOut, StdErr), "Starts the process
   @var{Command} and returns the standart I/O streams of the process in
   @var{StdIn}, @var{StdOut}, and @var{StdErr}.  If @var{Command}
   contains blank spaces, these are taken as separators between a program
   name (the first chunk of contiguous non-blank characters) and options
   for the program (the subsequent contiguous pieces of non-blank
   characters), as in @pred{exec('ls -lRa ../sibling_dir', In, Out, Err)}.").

:- comment(bug,
   "If @pred{exec/4} does not find the command to be executed, there is 
    no visible error message: it is sent to a error output which has already 
    been assigned to a different stream, disconnected from the one the 
    user sees.").

:- true pred exec(+atm, -stream, -stream, -stream).

exec(Command, StdIn, StdOut, StdErr):- 
        split_atom(Command, [Program|Arguments]),
        exec(Program, Arguments, StdIn, StdOut, StdErr, true, _, _).


:- true pred exec(+Command, 
                  +Arguments, 
                  ?StdIn, 
                  ?StdOut,
                  ?StdErr,
                  +Background,
                  -PID,
                  -ErrCode) : atm * list(atm) * 
                              stream * stream * stream *
                              atm * int * int

# "@pred{exec/8} gives a finer control for launching external
processes.  @var{Command} is the command to be executed and
@var{Arguments} is a list of atoms to be passed as arguments to the
command.  When called with free variables, @var{StdIn}, @var{StdOut},
and @var{StdErr} are instantiated to streams connected to the standard
output, input, and error of the created process. @var{Background}
controls whether the caller waits for @var{Command} to finish, or if
the process executing @var{Command} is completely detached (it can be
waited for using @pred{wait/3}). @var{ErrCode} is the error code
returned by the lower-level @tt{exec()} system call (this return code
is system-dependent, but a non-zero value usually means that something
has gone wrong).  If @var{Command} does not start by a slash,
@pred{exec/8} uses the environment variable @tt{PATH} to search for
it.  If @tt{PATH} is not set, @tt{/bin} and @tt{/usr/bin} are
searched.".

exec(Comm, Args, StdIn, StdOut, StdErr, Background, PID, ErrCode) :-
     '$exec'(Comm, Args, StdIn, StdOut, StdErr, Background, PID, ErrCode).


:- comment(exec(Command, StdIn, StdOut), "Starts the process
   @var{Command} and returns the standart I/O streams of the process in
   @var{StdIn} and @var{StdOut}. @tt{Standard error} is connected to
   whichever the parent process had it connected to.  @var{Command} is
   treated and split in components as in @pred{exec/4}.").

:- true pred exec(+atm, -stream, -stream).

exec(Command, StdIn, StdOut):- 
        exec(Command, StdIn, StdOut, []).

 %% :- comment(bug, "When reading from a exec'ed process, the 'end of
 %% file' condition (when the launched process finishes) somehow
 %% propagates to the standard input of the Ciao Prolog process, thus
 %% causing subsequent Ciao Prolog reads to return the 'end of file'
 %% condition.").


:- true pred wait(+Pid, -RetCode, -Status) : int * int * int # "wait/3
waits for the process numbered @var{Pid}.  If @var{PID} equals
-1, it will wait for any children process.  @var{RetCode} is usually
the PID of the waited-for process, and -1 in case in case of error.
@var{Status} is related to the exit value of the process in a
system-dependent fashion.".


:- comment(popen(Command, Mode, Stream), "Open a pipe to process
@var{Command} in a new shell with a given @var{Mode} and return a
communication @var{Stream} (as in UNIX @tt{popen(3)}). If @var{Mode}
is @tt{read} the output from the process is sent to @var{Stream}. If
@var{Mode} is @tt{write}, @tt{Stream} is sent as input to the
process. @var{Stream} may be read from or written into using the
ordinary stream I/O predicates. @var{Stream} must be closed explicitly
using @pred{close/1}, i.e., it is not closed automatically when the
process dies.  Note that @pred{popen/2} is defined in ***x as using
@tt{/bin/sh}, which usually does not exist in Windows systems.  In
this case, a @tt{sh} shell which comes with Windows is used.

").

:- true pred popen(+atm, +popen_mode, -stream).

 %% popen(Command, Mode, S) :-
 %% 	nonvar(Mode),
 %% 	popen_mode(Mode),
 %% 	atom(Command), !,
 %% 	'$unix_popen'(Command, Mode, S0), !, S=S0.

popen(Command, Mode, S):-
        nonvar(Mode), 
        popen_mode(Mode),
        atom(Command), !,
        (
            get_os('Win32') ->
            getenvstr('SHELL', WindowsShell),
            atom_codes(Shell, WindowsShell)
        ;
            Shell = '/bin/sh'
        ),
        (
            Mode = read ->
            exec(Shell, ['-c', Command], [], S, [], true, _, _)
        ;
            exec(Shell, ['-c', Command], S, [], [], true, _, _)
        ).

:- regtype popen_mode(M)
  # "@var{M} is 'read' or 'write'.".

popen_mode(read).
popen_mode(write).

:- comment(directory_files(Directory, FileList), "@var{FileList} is the
        unordered list of entries (files, directories, etc.) in
        @var{Directory}.").

:- true pred directory_files(+atm,?list(atm)).

:- comment(mktemp(Template, Filename), "Returns a unique @var{Filename}
    based on @var{Template}: @var{Template} must be a valid file name
    with six trailing X, which are substituted to create a new file
    name.").

:- true pred mktemp(+atm, ?atm).

:- comment(file_exists(File), "Succeeds if @var{File} (a file or
        directory) exists (and is accessible).").

:- true pred file_exists(+atm).

file_exists(Path) :- file_exists(Path, 0).

:- comment(file_exists(File, Mode), "@var{File} (a file or directory)
        exists and it is accessible with @var{Mode}, as in the Unix
        call @tt{access(2)}. Typically, @var{Mode} is 4 for read
        permission, 2 for write permission and 1 for execute
        permission.").

:- true pred file_exists(+atm, +int).

:- comment(file_property(File, Property), "@var{File} has the property
   @var{Property}. The possible properties are:

@begin{description}

@item{type(@var{Type})} @var{Type} is one of @tt{regular}, @tt{directory},
      @tt{symlink}, @tt{fifo}, @tt{socket} or @tt{unknown}.

@item{linkto(@var{Linkto})} If @var{File} is a symbolic link,
      @var{Linkto} is the file pointed to by the link (and the other
      properties come from that file, not from the link itself).

@item{mod_time(@var{ModTime})} @var{ModTime} is the time of last
      modification (seconds since January, 1, 1970).

@item{mode(@var{Protection})} @var{Protection} is the protection mode.

@item{size(@var{Size})} @var{Size} is the size.

@end{description}

   If @var{Property} is uninstantiated, the predicate will enumerate the
   properties on backtracking.").

:- true pred file_property(+atm, ?struct).

file_property(Path, Property) :-
        file_property_(Property, Path).

file_property_(Property, Path) :-
        var(Property), !,
        file_properties(Path, Type, Linkto, Time, Protection, Size),
        ( Property = type(Type)
        ; Linkto \== '', Property = linkto(Linkto)
        ; Property = mod_time(Time)
        ; Property = mode(Protection)
        ; Property = size(Size)
        ).
file_property_(type(Type), Path) :- !,
        file_properties(Path, Type0, [], [], [], []),
        Type = Type0.
file_property_(linkto(File), Path) :- !,
        file_properties(Path, [], File0, [], [], []),
        File0 \== '',
        File = File0.
file_property_(mod_time(Time), Path) :- !,
        file_properties(Path, [], [], Time, [], []).
file_property_(mode(Protection), Path) :- !,
        file_properties(Path, [], [], [], Protection, []).
file_property_(size(Size), Path) :- !,
        file_properties(Path, [], [], [], [], Size).
file_property_(Other, _) :-
        throw(error(domain_error(file_property_type,Other),
                    file_property/2-2)).

:- comment(file_properties(Path, Type, Linkto, Time, Protection, Size),
        "The file @var{Path} has the following properties:

@begin{itemize} 

@item File type @var{Type} (one of @tt{regular}, @tt{directory},
      @tt{symlink}, @tt{fifo}, @tt{socket} or @tt{unknown}).

@item If @var{Path} is a symbolic link, @var{Linkto} is the file pointed
      to.  All other properties come from the file pointed, not the
      link.  @var{Linkto} is '' if @var{Path} is not a symbolic link.

@item Time of last modification @var{Time} (seconds since January, 1,
      1970).

@item Protection mode @var{Protection}.

@item Size in bytes @var{Size}.

@end{itemize}
").

:- true pred file_properties(+atm, ?atm, ?atm, ?int, ?int, ?int).

:- comment(modif_time(File, Time), "The file @var{File} was last
     modified at @var{Time}, which is in seconds since January, 1,
     1970. Fails if @var{File} does not exist.").

:- true pred modif_time(+atm, ?int).

modif_time(Path, Time) :-
        prolog_flag(fileerrors, OldFE, off),
        ( file_properties(Path, [], [], Time, [], []) ->
            set_prolog_flag(fileerrors, OldFE)
        ; set_prolog_flag(fileerrors, OldFE),
          fail
        ).

:- comment(modif_time0(File, Time), "If @var{File} exists, @var{Time} is
      its latest modification time, as in @pred{modif_time/2}.
      Otherwise, if @var{File} does not exist, @var{Time} is zero.").


:- true pred modif_time0(+atm, ?int).

modif_time0(Path, Time) :-
        prolog_flag(fileerrors, OldFE, off),
        ( file_properties(Path, [], [], T, [], []), !
        ; T = 0
        ),
        set_prolog_flag(fileerrors, OldFE),
        Time = T.

:- comment(fmode(File, Mode), "The file @var{File} has protection mode
        @var{Mode}.").

:- true pred fmode(+atm, ?int).

fmode(Path, Mode) :-
        file_properties(Path, [], [], [], Mode, []).

:- comment(chmod(File, NewMode), "Change the protection mode of file
        @var{File} to @var{NewMode}.").

:- true pred chmod(+atm, +int).

:- comment(chmod(File, OldMode, NewMode), "The file @var{File} has
    protection mode @var{OldMode} and it is changed to @var{NewMode}.").

:- true pred chmod(+atm, ?int, +int).

:- true pred chmod(File, OldMode, NewMode)
        : (atm(File), var(OldMode), var(NewMode), OldMode == NewMode)
        => atm * atm * atm
        # "Equivalent to fmode(@var{File},@var{OldMode})".

chmod(Path, OldMode, NewMode) :-
        OldMode == NewMode, !,
        fmode(Path, OldMode).
chmod(Path, OldMode, NewMode) :-
        fmode(Path, OldMode),
        chmod(Path, NewMode).


:- comment(delete_directory(File), "Delete the directory @var{Directory}.").

:- pred delete_directory(+atm).


:- comment(delete_file(File), "Delete the file @var{File}.").

:- pred delete_file(+atm).


:- comment(rename_file(File1, File2), 
        "Change the name of  @var{File1} to @var{File2}.").

:- pred rename_file(+atm,+atm).

:- comment(make_directory(DirName, Mode), 
        "Creates the directory @var{DirName} with a given @var{Mode}.  This is, as usual, operated against the current umask value.").

:- pred make_directory(+atm, +int).

:- comment(make_directory(DirName),
        "Equivalent to @tt{make_directory(D,0o777)}.").

:- pred make_directory(+atm).

make_directory(D) :-
        make_directory(D,0o777).

:- pred make_dirpath(+sourcename, +int).

:- comment(make_dirpath(Path, Mode),
        "Creates the whole @var{Path} for a given directory with a given @var{Mode}. As an example, @tt{make_dirpath('/tmp/var/mydir/otherdir')}."). 

% We should take care of correct instantiation modes, types, etc. here.
% We are however delegating it to make_directory/2 and absolute_file_name/7
% (called below).
make_dirpath(Path, Mode) :-
%	absolute_file_name(Path, '', '', '.', AbsolutePath, _, _),
%	atom_codes(AbsolutePath, AbsPathCodes),
         atom_codes(Path, PathCodes),
         (          % If relative, transform it into absolute
             PathCodes = "/"||_ ->
             AbsPathCodes = PathCodes
         ;
             working_directory(CurrentDir, CurrentDir),
             atom_codes(CurrentDir, CurrentDirCodes),
             append(CurrentDirCodes, "/"||PathCodes, AbsPathCodes)
         ),
        make_abs_dir(AbsPathCodes, '', Mode).
% 
% Making the intermediate directories: instead of cd'ing to
% directories (which is something which can break and modify the
% program state), we construct incrementally the intermediate directories.
% The idea here is to traverse the absolute path and construct 
% partial paths, which are appended to an atom which represents the
% (initially empty) path.  Depending on the implementation of
% atom_concat/3 (and of the atoms), this can be done quite fast if the
% first argument does not need to be completely traversed.

% End of path
make_abs_dir("", _, _Mode):- !.
% The recursive case: perform a step in the recursion and construct the
% intermediate directory.
make_abs_dir(Path, IncPath, Mode):-
        decompose(Path, RestPath, Component-[]),
        % Transform into atom and add to partial path
        atom_codes(PartComp, Component),
        atom_concat(IncPath, PartComp, NewPath),
        (
            file_exists(NewPath) ->
            true
        ;
            make_directory(NewPath, Mode)
        ),
        make_abs_dir(RestPath, NewPath, Mode).


% decompose("//"||PathWOSlash, RestPath, Queue):- !, 
%         decompose("/"||PathWOSlash, RestPath, Queue).
decompose("/"||PathWOSlash, RestPath, "/"||Queue-TailQ):-
        decompose_aux(PathWOSlash, RestPath, Queue-TailQ).
decompose_aux("", "", Q-Q).
decompose_aux("/"||P, "/"||P, Q-Q):- !.
decompose_aux([P|Ps], RestP, [P|RestQ]-TailQ):- 
        decompose_aux(Ps, RestP, RestQ-TailQ).



 %% make_dirpath(Path, Mode) :-
 %% 	working_directory(CurrentDir, CurrentDir),
 %% 	make_dirpath_aux(Path, Mode),
 %% 	working_directory(_, CurrentDir).
 %% 
 %% make_dirpath_aux(Path, Mode) :-
 %% 	atom_concat(Head, Tail, Path),
 %% 	atom_concat('/', SubTail, Tail), !,
 %% 	(Head = '' ->
 %% 	 working_directory(CurrentDir, '/')
 %% 	;
 %% 	 ((file_exists(Head), file_property(Head, type(directory)))
 %% 	  ;
 %% 	   make_directory(Head, Mode) 
 %% 	 ),
 %% 	 working_directory(CurrentDir, Head)),
 %% 	make_dirpath_aux(SubTail, Mode).
 %% make_dirpath_aux(Dir, Mode) :-
 %% 	make_directory(Dir, Mode).
 %% 	 



:- pred make_dirpath(+atm).

:- comment(make_dirpath(Path),
                "Equivalent to @tt{make_dirpath(D,0o777)}.").

:- pred make_dirpath(+atm).
	  
make_dirpath(Path) :-
        make_dirpath(Path, 0o777) .

:- pred cyg2win(CygWinPath, WindowsPath, SwapSlash) : 
        string * var * atom => string * string * atom
#  "Converts a path in the CygWin style to a Windows-style path,
    rewriting the driver part.  If @var{SwapSlash} is @tt{swap},
    slashes are converted in to backslash.  If it is @tt{noswap}, they
    are preserved.".

:- comment(bug,
   "If the arguments to cyg2win/3 are not strings, strange results
    appear, as a very mild type checking is performed.").

cyg2win("/cygdrive/"||[D,0'/ | Dir], [D,0':| Path],Swap) :- !,
						             % New Drive notat.
        swapslash(Swap,[0'/ | Dir],Path).
cyg2win("//"||[D,0'/ | Dir], [D,0':,0'\\ | Path],Swap) :- !, % Drive letter
        swapslash(Swap,Dir,Path).
cyg2win("//"||Dir, "\\\\"||Path,Swap) :- !,                  % Network drive
        swapslash(Swap,Dir,Path).
cyg2win(Dir,  [D,0': |Path],Swap) :-                         % Default drive
        swapslash(Swap,Dir,Path),
        default_drive(D).

default_drive(D) :-
        getenvstr('COMSPEC',[D|_]), !. % Shell command interpreter' drive
default_drive(0'C).                    % If not defined, assume C

swapslash(noswap,Dir,Dir) :-
	!.
swapslash(swap,Dir,Path) :-
	do_swapslash(Dir,Path).

do_swapslash([],[]).
do_swapslash([0'/|D],[0'\\|ND]) :- !,
        do_swapslash(D,ND).
do_swapslash([C|D],[C|ND]) :-
        do_swapslash(D,ND).


:- comment(version_maintenance,dir('../version')).

:- comment(version(1*9+365,2004/07/28,01:10*31+'CEST'), "exec/3 and
exec/4 now split the Command into a program name and its argument when
the Command is an atom with blanks inside.  Blanks are taken as
separators.  ()").

:- comment(version(1*9+342,2004/04/24,20:30*39+'CEST'),
"absolute_file_name screwed up make_dirpath/2.  Corrected (by
reverting to a previous version).  (Manuel Carro)").

:- comment(version(1*9+340,2004/04/22,16:22*25+'CEST'), "exec/3-4
   should not wait until the process terminates.  (Jesus Correas
   Fernandez)").

:- comment(version(1*9+338,2004/04/22,06:11*20+'CEST'), "Implemented
wait/3 to wait for the end of processes.  (Manuel Carro)").

:- comment(version(1*9+337,2004/04/22,05:47*28+'CEST'), "popen/3
reimplemented in Prolog based on exec/8.  This should be more
portable.  (Manuel Carro)").

:- comment(version(1*9+336,2004/04/22,05:21*27+'CEST'), "Implemented
exec/8 to base other exec's and popen/3 on it (Manuel Carro)").

:- comment(version(1*9+331,2004/03/26,17:39*47+'CET'), "Errors in
   exec(Manuel Carro) caught; processes killed in this case.  This affects shell/n
   and exec/{3,4} (MCL)").

:- comment(version(1*9+326,2004/03/16,16:18*08+'CET'), "Corrected
   make_dirpath/2 to make directories taking into account several
   useful UNIX conventions (e.g, ~ to design home directories).  Made
   through a call to absolute_file_name/7, so everything supported by
   the latter should also be by the former.  (Jesus Correas
   Fernandez & Manuel Carro)").

:- comment(version(1*7+211,2002/04/30,20:40*19+'CEST'), "setenvstr/2
        added (Jesus needed it).  (MCL)").

:- comment(version(1*7+181,2002/01/25,20:14*06+'Hora estándar
   romance'), "cyg2win/3 moved here.  (Manuel Carro)").

:- comment(version(1*7+169,2002/01/03,17:57*18+'CET'), "Changed
   make_dirpath to make it completely deterministic.  (MCL)").

:- comment(version(1*7+158,2001/11/27,10:12*07+'CET'), "make_dirpath/1 and
   make_dirpath/2 added to system (Jose Manuel Gomez Perez)").

:- comment(version(1*3+50,1999/09/08,22:35*15+'MEST'), "Commented and
   exported. Other minor changes in comments. @prop{datime_struct/1}.
   (Manuel Hermenegildo)").

:- comment(version(1*3+34,1999/07/14,20:09*38+'MEST'), "Added datime/1
   and datime/9.  (Daniel Cabeza Gras)").

:- comment(version(0*9+73,1999/04/30,13:27*15+'MEST'), "Fixed minor
   bug in comment.  (Manuel Hermenegildo)").

:- comment(version(0*9+54,1999/04/21,21:54*24+'MEST'), "Added
   extract_paths/2 predicate.  (Daniel Cabeza Gras)").

:- comment(version(0*9+43,1999/04/08,20:26*58+'MEST'), "Added
   file_property/2 and file_properties/6 predicates, last one
   implemented in emulator (Daniel Cabeza Gras)").

:- comment(version(0*7+24,1998/10/22,15:50*41+'MEST'), "Implemented
   delete_file/1 in emulator.  (Daniel Cabeza Gras)").

:- comment(version(0*7+23,1998/10/22,13:31*37+'MEST'), "Added chmod/2
   and fmode/2 and changed chmod/3 to avoid a bug when getting modes
   from other users files.  (Daniel Cabeza Gras)").

:- comment(version(0*7+1,1998/09/15,16:00*12+'MEST'), "Added
   documentation for all the predicates in the module (MCL).").

:- comment(version(0*4+5,1998/2/24), "Synchronized file versions with
   global CIAO version.  (Manuel Hermenegildo)").

