::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:
class Program.Dual.Public  public Gilda:  Paths that work on Windows and Unix

  type Find.Path  is            &Access directories using Dual path notation.
      Id         Directory.Id,  &Set by Open.Multi; used by Next.Multi.
      Partial    Queue..string, &Directory tree walk state
      First      Bit,           &First element found
      Directory  string,        &Os path of a walk directory (trailing slash)
      Prefix     string,        &Os path for the prefixed directory
      Tail       string         :Last component in the path (may be wild)

  type Directory.Member  in     &Type of an element in a directory
      None,                     &Missing or unknown element
      Head,                     &Iterator starting a new directory
      Directory,                &Subdirectory
      File,                     &Data file
      Executable,               &Program file
      Link                      :Unix link

import Io.File,
       Io.Directory,
       Program.Environment.Public

global +File.List       Map,    &Files in a directory scan
       +Directory.List  Map,    &Subdirectories in a directory scan
       +Max.Width       word    :Maximum width of file and directory names
:
:...............................................................................



::::::::::::::::::::::::  program/dual/path/Dual.Path  :::::::::::::::::::::::::
:
method DIRECTORY.TAIL  pure:  Divide a Dual path (with wildcards) into a directory
                           :  and the last component.

 entry Dual   string       :Dual path to divide (wild, forward slashes)

  exit Lead   string,      &Dual path directory components.  Ends in slash.
       Tail   string       :Last path component.  If a directory, ends in slash.
:
:...............................................................................
:
method TAME.COMPONENT:  Convert a component to a tame component.

 entry Tail   string    :Dual path component (may contain quotes)

  exit Tame   string    :Empty if any wildcards (*?!#); else unquoted component
:
: Ticks are treated as literals.
:...............................................................................
:
method TO.OS.PATH:  Convert a Tame Dual path to an OS path.

 entry Dual    string    :Dual path (tame, forward slash)

  exit Os      string    :Os path (relative, tame, forward slash)
:
:...............................................................................
:
function TO.DUAL.PATH:  Convert a Windoze specific path to a Dual path.

 entry Os      string    :Raw OS path name (forward slashes)

  exit Dual    string    :Dual path (lower or exact; path.slash; trail / if Os does).
                         :May be absolute or reference relative to home (-).
:
:...............................................................................
:
method TO.DUAL.FORM:  Convert an OS specific path to a Dual path.

 entry Os      string    :OS path name (tame, forward slashes)

  exit Form    string    :Formatted Dual path name (proper, exect or lower case)

 entry Lower = 0  Bit    :0 - Format as a proper or exact name.
                         :1 - Lowercase the result.
:
:...............................................................................
:
method QUOTE.OS.PATH:  Validate an OS path and quote it if needed.

change Path   string     :OS path to prepare
:
:...............................................................................
:
method SINGLE.DIRECTORY:  Convert a wild path to a single directory.

 entry Wild   string     :Wild Dual path to search

  exit Path   string,    &The first match (forward slashes, trailing /)
                         &    Empty if no match.
       More   Bit        :Set if many; clear if none or one match.
:
:...............................................................................
:
method SINGLE.FILE:  Convert a wild path to a single file.

 entry Wild    string     :Wild Dual path to search

  exit Path    string,    &The first matching file; empty if no match.
       More    Bit        :Set if many; clear if none or one match.
:
:...............................................................................
:
method SINGLE.EXECUTABLE:  Convert a wild path to a single executable file.

 entry Wild    string     :Wild Dual path to search

  exit Path    string,    &The first matching file; empty if no match.
       More    Bit        :Set if many; clear if none or one match.
:
:...............................................................................
:
method SINGLE.TEXT:  Convert a wild path to a single text file.

 entry Wild   string    :Wild Dual path to search

  exit Path   string,   &The first matching file; empty if no match.
       More   Bit       :Set if many; clear if none or one match.
:
:...............................................................................
:
method FORM.PATH.NAME:  Format a file or directory name for display.

change Path    string                  :Directory element name

 entry Element = 0  Directory.Member   :0 - regular file; executable, directory
:
:...............................................................................
:
method FORM.PATH.SIZE:  Squeeze long path names for display purposes only.

change Path       string  : In - Tame
                          :Out - Display path.  May not be a valid path.

 entry Size       parcel  :Allowable path size (127 maximum)

  exit Truncate   Bit     :The last component was truncated; else left as is.

 entry Retain = 0 Bit     :0 - Discard alpha-numerics for special characters.
                          :1 - Retain alpha-numerics in component (reusable path).
:
:...............................................................................
:
method FORM.DUAL.WILD:  Make implicit wildcards explicit.

 entry Dual    string      :Any Dual path (forward slashes)

  exit Wild    string      :Dual path with stars and no ticks (OS slashes).
                           :No trailing slash unless a
                           :disk (<drive>:/), root(/), or net root (//).
:
:...............................................................................
:
method DUAL.ABSOLUTE:  Derive an absolute path from a base and Dual path.

 entry Dual    string,     &Dual path relative to the Root
       Root    string      :Relative directory (absolute, trailing slash)

  exit Path    string      :Absolute path (may or may not have a trailing slash)
:
:...............................................................................



:::::::::::::::::::  program/dual/directory/Dual.Directory  ::::::::::::::::::::
:
method COMPONENT.TAME:  If a component is tame, get its raw name.

 entry Component  string   :Component to be checked and transformed.


  exit Tame       string   :Null if the component is wild (unquoted *, ?, or !)
                           :else the raw tame text (unquoted, no right tick)
:
:  Faults if an improperly formed quote.
:...............................................................................
:
method COMPONENT.WILD:  Prepare a path component for Match.Wild.

 entry Component  string       :A right tick suppresses star.

  exit Wild       string       :Right tick removed; explicit star added.
:
:...............................................................................
:
method DI.PATH:  Save a directory element in sorted tables.

 entry Os       string,             &Directory element name (no trailing slash)
       Element  Directory.Member,   &Type of element
       Raw = 0  Bit                 :0 - Format unless Platform`Path.Case = 1
                                    :1 - List actual case
:
: Exit:  Directories are added to Directory.List.
:        Files are added to File.List.
:        Long listings use raw (actual) element casing.
:        Short Windows listings proper case directories and lower case files.
:        Ambiguous file names are quoted.  These contain a
:           leading dash, trailing tilda or tick, a quote, *, #, !, ?, or space.
:...............................................................................
:
method DI.PATH.ATTRIBUTE:  Save a directory element in sorted tables.

 entry Os       string,         &Directory element name (no trailing slash)
       Attribute                :Type of element
:
: Exit:  Directories are added to Directory.List.
:        Files are added to File.List.
:        Ambiguous file names are quoted.  These contain a
:           leading dash, trailing tilda or tick, a quote, *, #, !, ?, or space.
:...............................................................................
:
method FORM.PATH.LONG:  Format a file or directory.

 entry Lead    string,    &Os (relative; tame) or directory (trailing /)
       Tail    string     :Directory element name

  exit Long    string     :Formatted path

:
:...............................................................................
:
method FORM.PATH.MULTI:  Prepare a Dual path for scanning in Open.Multi.

 entry Dual   string     :Dual path name (wild; either slash; double quotes only).

  exit Path   string
:
:...............................................................................
:
method OS.PATH.PREFIX:  Extract the prefix fields from a Dual path and
                     :  translate them into an os dependent form.

 entry Dual    string      :Dual path (forward slashes) to extract the node
                           :and disk.  Upon exit, these fields are removed.

  exit Index    word,      &Index past the end of the prefix in Dual
       Prefix   string     :Os prefix (tame; trailing forward slash)

precondition
   Is.Dual.Lead( byte{ Dual })       &Invalid lead path character.
      fault Dual
.
:     Prefix = { (['-'] ('.'*  ['/']))*  |  {A-Z | a-z} ':' }
:...............................................................................
:
method OS.TO.DUAL.PROMPT:  Convert an os specific path to a prompt path.

 entry Os     string    :Raw OS path name (forward slashes)
                        :Ends in / if a directory.

  exit Dual   string    :Dual path (mixed; path.slash; trail slash if Os does)
                        :Paths with spaces are quoted with prefered slashes.
:
:...............................................................................
:
method TRIM.UP.PREFIX:  Remove trailing components from a prefix.

change Prefix   string    :Path to trim (tame, forward slash, trailing slash)

 entry Remove   parcel    :Number of components to remove
:
:...............................................................................
:
function IS.DUAL.LEAD:  Determine if an ascii character is alphanumeric.

 entry C       byte     :A character to test

  exit Result  Bit      :Set if:  A-Z  a-z  $ 0-9 _ . * ? / \ - + ! " ' `
:
:...............................................................................
:
function IS.DUAL.IN:  Determine if an ascii character is inside a Dual path.

 entry C     byte     :Character to test

  exit Pass  Bit      :Set if a lead character plus:  ` % @ ^ | # ~
:
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:
sequence WILD.PATH: Get a series of absolute path names.

 entry Wild   string     :Dual path (wild; any slash; double quotes only).
                         :End in slash to match to directories only;
                         :    otherwise files or directories will match.
                         :Forward or backslashes are okay.

  exit Tame   string     :OS path name (no wildcards)
                         :   Full path name if above the current directory or
                         :   relative to the current directory.
                         :   The path has only forward slashes.
                         :   Directories have a trailing slash.
                         :   Program files have a trailing backslash.
:...............................................................................
:
function IS.SINGLE..Wild.Path:  See if multiple paths matched.

 entry ~Wild.Path        :Iterator

  exit Single  Bit       :Set if only one element matches.
:
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:
sequence WILD.FILE:  Find files that match a pattern.

 entry Wild  string      :Dual path(s) to find
                         :Wild; Forward or backward slashes; Double quotes only

  exit Tame  string      :Relative tame file path (forward slashes)
:
:...............................................................................
:
function IS.SINGLE..Wild.File:  Check after the first match for a unique result.

 entry ~Wild.File        :Iterator

  exit Single  Bit       :Set if only one element matches.
:
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:
sequence WILD.DIRECTORY:  Find directories that match a pattern.

 entry Wild   string     :Dual directory path(s) to find
                         :Wild; forward or backward slashes; double quotes only

  exit Tame   string     :Relative tame directory OS path
                         :Forward slashes, trailing slash
:
:...............................................................................
:
function IS.SINGLE..Wild.Directory:  Check after the first match for a unique result.

 entry ~Wild.Directory   :Iterator

  exit Single  Bit       :Set if only one element matches.
:
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:
sequence WILD.TEXT:  Find text files whose names match a pattern.

 entry Wild    string    :Dual text file path(s) to find
                         :Wild; forward or backward slashes; double quotes only

  exit Tame    string    :Relative tame file path (forward slashes)
:
:...............................................................................
:
function IS.SINGLE..Wild.Text:  Check after the first match for a unique result.

 entry ~Wild.Text        :Iterator

  exit Single   Bit      :Set if only one element matches.
:
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:
sequence WILD.EXECUTABLE:  Find program files that match a pattern.

 entry Wild  string      :Dual file path(s) to find
                         :Wild; forward or backward slashes; double quotes only

  exit Tame  string      :Relative tame file path (forward slashes)
:
:...............................................................................
:
function IS.SINGLE..Wild.Executable:  Check after the first match for a unique result.

 entry ~Wild.Executable  :Iterator

  exit Single   Bit      :Set if only one element matches.
:
:...............................................................................



:::::::::::::::::::::::  program/dual/directory/Find.Path  :::::::::::::::::::::
:
method OPEN..Find.Path:  Begin retrieving a series of absolute path names.

change Find.Path            :Initialize the search state.

 entry Dual       string    :Dual path (wild; any slash; double quotes only)
                            :End in slash to match to directories only;
                            :    otherwise files or directories will match.
                            :Forward or backslashes are okay.

  exit Lead = ""  string,   &Null if no matching directory paths, or
                            &   the only OS directory to match (Single = 1),
                            &   or first OS directory path (Single = 0).
                            &      (trailing forward slash; actual case).
       Single = 0  Bit      :0 - Several matching path elements.
                            :1 - A single directory is scanned (or none).
:
:...............................................................................
:
method CLOSE..Find.Path  pure:  Terminate an incomplete directory scan.

change Find.Path            :Close the search state.
:
:...............................................................................
:
method NEXT..Find.Path:  Retrieve next full os path name in a series of multiples.
                      :  Call Open.Multi first and Lead must be non-null.

change Find.Path                 :Update the search state.

  exit Path     string,          &OS path (no wildcards) or "" (Element = None)
                                 &Full path name if above the current directory
                                 &or relative to the current directory
                                 &(forward slashes; no trailing slash).
                                 &
       Element  Directory.Member :None (Os = ""), Diretory, Executable, or File
:
:...............................................................................
:
method MATCH:  Match a relative path to the tail.

change Find.Path                  :Update the search state.

 entry Path    string             :Os path name (relative; tame)

  exit Element Directory.Member,  &0 (if no match), File, or Directory
       Size    cell               :File size
:
:...............................................................................
:
method OPEN.DI..Find.Path:  Access a path for the Directory command.

change Find.Path           :Update the search state.

 entry Dual     string     :Dual path name (may be wild)

  exit Lead     string,    :Lead path component
       Single   Bit        :Set if a single match.
:
:...............................................................................
:
method NEXT.DI..Find.Path:  Retrieve next path name is a series of multiples.

change Find.Path              :Update the search state.

 entry Hidden = 0  Bit        :1 - Include hidden files.

  exit Path        string,    &Os path name (relative; tame)
       Attribute              :Attribute of the component
:
:   The sequence returned is:
:         ( Relative directory, Head )
:                    ( Element, {File | Directory | Executable} )*
:                         ( "", None )
:...............................................................................
:
method MATCH.WILD:  Match an absolute string against a wild string.

 entry Tame    string,    &Absolute string (no wildcards, no nulls)
       Wild    string,    &Wild string (no auto-star nor trailing tick)
                          &  Quote literals and case sensitive characters.
                          &  Either single or double quotes may be used.
                          &  * - Match 0 or more characters.
                          &  ? - Match a single character.
                          &  ! - Match none or 1 character.
                          &  # - Match a single decimal digit.
                          &  Non-printable characters may be matched.
       Case = 0  Bit      :1 - for case sensitive matching.

  exit Match     Bit,     &1 - An exact match.
       Partial   word     :Number of tame characters that matched all wilds.
:
:...............................................................................
:
method NEXT.COMPONENT:  Parse the next component in a Dual path (left to right).

 entry Dual   string      :A Dual path (may contain wildcards, forward slash)

change Index  word        :Current position in the Dual path

  exit Name   string,     &The next component (may have wildcards).
       Dot    parcel      :Number of dots only; 0 if none.
:
:...............................................................................
:
method PATH.TAIL:  Finds the start of the last component in a Dual path.
                :  If the path ends in a slash, the last component will
                :  precede it.

 entry Dual   string   :Dual or Os path (forward slashes; wild)

change Index  word     : In - Past prefix
                       :Out - Index to the first character of the last component

  exit Tail   string   :Text from the index on; Null if Dual is all prefix.
:
:...............................................................................
:
method PATH.ELEMENT:  Determine if a path is a directory, file, or executable.

 entry Path     string               :Os path name (relative; tame)

  exit Element  Directory.Member,    &Executable, File, or Directory
       Size     cell                 :File size
:
:...............................................................................
:
method DUAL.TAIL:  Finds the last component in a Dual path.

 entry Dual     string    :A Dual or Os path (forward slashes; wildcards).

  exit Tail     string    :Has leading slash.  Null if all prefix.
:
:  If the path ends in a slash, the last component will precede it.
:...............................................................................
:
:
function UNQUOTE.LEAD.DUAL:  Unquote drives and lead slashes (make them forward)

 entry Dual   string     :Dual path name (may be wild)

  exit Path   string     :Unquoted path
:
:...............................................................................
:
method SINGLE.PATH:  Convert a wild path to a single file.

 entry Wild    string    :Wild Dual path to search

  exit Path   string,    &The first tame OS path to match (forward slashes)
                         &Directories have a trailing forward slash.
                         &Empty if no match.
       More   Bit        :Set if many; clear if none or one match.
:
:...............................................................................


end