############################################################################
#
#	File:     gdl2.icn
#
#	Subject:  Procedures to get directory lists
#
#	Authors:  Richard L. Goerwitz and Charles Shartsis
#
#	Date:	  August 14, 1995
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#
#   Version:  1.3+
#
############################################################################
#
#  Gdl returns a list containing everything in a directory (whose name
#  must be passed as an argument to gdl).  Nothing fancy.  I use this file
#  as a template, modifying the procedures according to the needs of the
#  program in which they are used.
#
# NOTE: MSDOS results are all in lower case
#
# Modifications:
# 1) Fixed MSDOS routines.
# 2) Added gdlrec which does same thing as gdl except it recursively descends
#   through subdirectories.  May choose which Unix utility to use by passing
#   in method parameter.  See below.
#
############################################################################
#
#  Requires:  UNIX or MS-DOS
#
############################################################################


procedure gdl(dir)

    local getdir

    getdir := set_getdir_by_os()
    return getdir(dir)

end

procedure gdlrec(dir, Method)

# Unix method to use:   &null for compatibility (uses "/bin/ls"), 
#                       not null for speed (uses "find")

    local getdir

    getdir := set_getdir_rec_by_os(Method)
    return getdir(dir)

end


procedure set_getdir_by_os()

    # Decide how to get a directory, based on whether we are running
    # under Unix or MS-DOS.

    if find("UNIX", &features)
    then return unix_get_dir
    else if find("MS-DOS", &features)
    then return msdos_get_dir
    else stop("Your operating system is not (yet) supported.")

end

procedure set_getdir_rec_by_os(Method)

    # Decide how to get a directory, based on whether we are running
    # under Unix or MS-DOS.

    if find("UNIX", &features) then {
        if /Method then
            return unix_get_dir_rec
        else
            return unix_get_dir_rec2
    }
    else if find("MS-DOS", &features) then
        return msdos_get_dir_rec
    else
        stop("Your operating system is not (yet) supported.")

end


procedure msdos_get_dir(dir)

    # Returns a sorted list of all filenames (full paths included) in
    # directory "dir."  The list is sorted.  Fails on invalid or empty
    # directory.  Aborts if temp file cannot be opened.
    #
    # Temp files can be directed to one or another directory either by
    # manually setting the variable temp_dir below, or by setting the
    # value of the environment variable TEMPDIR to an appropriate
    # directory name.

    local in_dir, filename_list, line, temp_name, filename
    static temp_dir
    initial {
        temp_dir := 
            (trim(map(getenv("TEMPDIR"), "/", "\\"), '\\') || "\\") |
                ".\\"
    }

    # Get name of tempfile to be used.
    temp_name := get_dos_tempname(temp_dir) |
    stop("No more available tempfile names!")
    
    ### Added by C. Shartsis 9/19/94
    # Make implicit current directory explicit
    # Otherwise current and root directory get mapped to same thing
    if  (dir == "") |
        (dir ? (tab(any(&letters)) & =":" & pos(0)) )
    then
        dir ||:= "."

    # Make sure we have an unambiguous directory name, with backslashes
    # instead of Unix-like forward slashes.
    dir := trim(map(dir, "/", "\\"), '\\')

    ### Added by C. Shartsis 9/19/94
    # Put backslash back on if dir is the root directory
    # Otherwise the current directory is returned
    if  (dir == "") |
        (dir ? (tab(any(&letters)) & =":" & pos(0)) )
    then
        dir ||:= "\\"

    # Put dir listing into a temp file.
    system("dir "||dir||" > "||temp_name)

    # Put tempfile entries into a list, removing blank- and
    # space-initial lines.  Exclude directories (i.e. return file
    # names only).
    in_dir := open(temp_name,"r") |
    stop("Can't open temp file in directory ",temp_dir,".")
    filename_list := list()
    every filename := ("" ~== !in_dir) do {
        match(" ",filename) | find(" <DIR>", filename) & next
    # Exclude our own tempfiles (may not always be appropriate).
    filename ?:= trim(trim(tab(10)) || "." || tab(13), '. ')
    ### Change: C. Shartsis - 4/9/95
    # Exclude tempfile
    if filename ? (
        ="ICONDIR." & tab(any(&digits)) & tab(any(&digits)) & tab(any(&digits))
    ) then
        next
    
    ### Change: C. Shartsis - 9/19/94
    # Otherwise file f in directory c:\d comes out as "c:\df" instead of "c:\d\f"
    #put(filename_list, map(dir || filename))
    put(filename_list, map(trim(dir, '\\') || "\\" || filename))
    }

    # Clean up.
    close(in_dir) & remove(temp_name)

    # Check to be sure we actually managed to read some files.
    if *filename_list = 0 then fail
    else return sort(filename_list)

end

procedure msdos_get_dir_rec(dir, level)

    # Returns a sorted list of all filenames (full paths included) in
    # directory "dir."  The list is sorted.  Fails on invalid or empty
    # directory.  Aborts if temp file cannot be opened.
    #
    # Temp files can be directed to one or another directory either by
    # manually setting the variable temp_dir below, or by setting the
    # value of the environment variable TEMPDIR to an appropriate
    # directory name.

    local in_dir, line, filename, raw_list
    local tmp_filelist, tmp_dirlist
    static temp_dir, temp_name, filename_list
    initial {
        temp_dir := 
            (trim(map(getenv("TEMPDIR"), "/", "\\"), '\\') || "\\") |
                ".\\"
    }
    
    # Establish recursion level
    /level := 0
    if level = 0 then {
        filename_list := list()
        # Get name of tempfile to be used.
        temp_name := get_dos_tempname(temp_dir) |
            stop("No more available tempfile names!")
    }
    
    # Make implicit current directory explicit
    # Otherwise current and root directory get mapped to same thing
    if  (dir == "") |
        (dir ? (tab(any(&letters)) & =":" & pos(0)) )
    then
        dir ||:= "."

    # Make sure we have an unambiguous directory name, with backslashes
    # instead of Unix-like forward slashes.
    dir := trim(map(dir, "/", "\\"), '\\')

    # Put backslash back on if dir is the root directory
    # Otherwise the current directory is returned
    if  (dir == "") |
        (dir ? (tab(any(&letters)) & =":" & pos(0)) )
    then
        dir ||:= "\\"

    # Put dir listing into a temp file.
    system("dir "||dir||" > "||temp_name)

    # Put tempfile entries into a list, removing blank- and
    # space-initial lines.  Exclude directories (i.e. return file
    # names only).
    in_dir := open(temp_name,"r") |
    stop("Can't open temp file in directory ",temp_dir,".")
    raw_list := []
    every put(raw_list, !in_dir)
    # Clean up.
    close(in_dir) & remove(temp_name)
    tmp_dirlist := []
    tmp_filelist := []
    every filename := ("" ~== !raw_list) do {
        match(" ",filename) | match(".",filename) & next
        # Process Directories
        if find(" <DIR>", filename) then {
            filename ?:= tab(many(~' \t'))
            put(tmp_dirlist, map(trim(dir, '\\') || "\\" || filename))
        }
        # Save files to list
        else {
            # extract the file name
            filename ?:= trim(trim(tab(10)) || "." || tab(13), '. ')
            # Exclude tempfile
            if not (filename ? (
                ="ICONDIR." & tab(any(&digits)) & tab(any(&digits)) & tab(any(&digits))
            )) then
                # Otherwise file f in directory c:\d comes out as "c:\df" instead of "c:\d\f"
                put(tmp_filelist, map(trim(dir, '\\') || "\\" || filename))
        }
    }
    
    # Add files to master list
    every put(filename_list, !sort(tmp_filelist))
    # Process remaining directories
    every msdos_get_dir_rec(!sort(tmp_dirlist), level + 1)

    # Check to be sure we actually managed to read some files.
    if level = 0 then {
        if *filename_list = 0 then fail
        else return filename_list
    }

end



procedure get_dos_tempname(dir)

    local temp_name, temp_file

    # Don't clobber existing files.  Get a unique temp file name for
    # use as a temporary storage site.

    every temp_name := dir || "icondir." || right(string(1 to 999),3,"0") do {
    temp_file := open(temp_name,"r") | break
        close(temp_file)
    }
    return \temp_name

end


procedure unix_get_dir(dir)

    local filename_list, in_dir, filename

    dir := trim(dir, '/') || "/"
    filename_list := list()
    in_dir := open("/bin/ls -F "||dir, "pr")
    every filename := ("" ~== !in_dir) do {
    match("/",filename,*filename) & next
    put(filename_list, trim(dir || filename, '*'))
    }
    close(in_dir)

    if *filename_list = 0 then fail
    else return filename_list

end


procedure unix_get_dir_rec(dir, level)

    # Returns a sorted list of all filenames (full paths included) in
    # directory "dir."  The list is sorted.  Fails on invalid or empty
    # directory.  Aborts if temp file cannot be opened.

    local in_dir, filename, raw_list, cmd
    local tmp_filelist, tmp_dirlist
    static filename_list
    
    # Establish recursion level
    /level := 0
    if level = 0 then
        filename_list := list()
    
    # Append trailing slash
    dir := trim(dir, '/') || "/"

    # Put tempfile entries into a list, removing blank- and
    # space-initial lines.  Exclude directories (i.e. return file
    # names only).
    cmd := "/bin/ls -FL " || dir
    in_dir := open(cmd,"pr") |
        stop(cmd, " will not run on this system")
    raw_list := []
    every put(raw_list, !in_dir)
    # Clean up.
    close(in_dir)
    tmp_dirlist := []
    tmp_filelist := []
    every filename := ("" ~== !raw_list) do {
        if match(" ",filename) | match(".",filename) | filename[-1] == "=" then
            next
        if filename[-1] == "*" then
            filename := filename[1:-1]
        # Process Directories
        if filename[-1] == "/" then
            put(tmp_dirlist, dir || filename)
        # Save files to list
        else
            put(tmp_filelist, dir || filename)
    }
    
    # Add files to master list
    every put(filename_list, !sort(tmp_filelist))
    # Process remaining directories
    every unix_get_dir_rec(!sort(tmp_dirlist), level + 1)

    # Check to be sure we actually managed to read some files.
    if level = 0 then {
        if *filename_list = 0 then fail
        else return filename_list
    }

end


# This works too.
# This routine is faster but depends on the Unix "find" program.
# Don't know if all Unixes have this.
procedure unix_get_dir_rec2(dir)

    local filename_list, in_dir, cmd

    dir := trim(dir, '/') || "/"
    filename_list := list()
    cmd := "find " || dir || " -type f -print"
    in_dir := open(cmd, "pr") |
        stop(cmd, " will not run on this system")
    every put(filename_list, !in_dir)
    close(in_dir)

    if *filename_list = 0 then fail
    else return filename_list

end
