#line 18 "/home/travis/build/felix-lang/felix/src/packages/filesystem.fdoc"
  
  Operations on filenames.
  class Filename_class[os] {
  
    The path separator.
    virtual fun sep: 1 -> string;
    virtual fun is_absolute_filename : string -> bool;
    virtual fun root_subdir : string -> string;
  
    virtual fun executable_extension : 1 -> string;
    virtual fun static_object_extension: 1 -> string;
    virtual fun dynamic_object_extension: 1 -> string;
    virtual fun static_library_extension: 1 -> string;
    virtual fun dynamic_library_extension: 1 -> string;
  
  
  
    split1 returns a pair consisting of a directory name and basename
    with the separator between them lost except in the special case
    "/x" where the "/" is kept as the directory name.
    If there is no separator, the path is the basename and
    the directory name is the empty string (NOT . !!!)
  
    fun split1(s:string)=> match find_last_of(s,#sep) with
      | Some pos =>
        if pos==0uz then #sep else s.[to pos] endif,
        s.[pos+#sep.len to]
      | #None => "",s
      endmatch
    ;
  
    private fun split(s:string, acc:List::list[string]):List::list[string]=>
      let d,b = split1 s in
      if d == "" then List::Cons(b,acc)
      elif d == #sep then List::Cons(d, List::Cons(b,acc))
      else split (d, List::Cons (b, acc))
      endif
    ;
  
    split a filename into a list of components.
    fun split(s:string)=> split (s, List::Empty[string]);
  
    Join two pathnames into a single pathname.
    split and join are logical inverses, however join is not
    not associative: join("x", join("","y")) = "x/y"
    whereas join(join("x",""),"y") = "x//y"
    since split pulls components off from the RHS we have to
    fold them back from the left
  
    fun join(p:string, b:string)=>
      if p == "" then b
      elif p == #sep then p+b
      elif p.[-1] == #sep.[0] then p+b
      else p+#sep+b
      endif
    ;
  
    Get the basename of a path (last component).
    fun basename(s:string)=> match split1(s) with | _,b => b endmatch;
  
    Get the directory name of a path (all but the last component).
    fun dirname(s:string)=> match split1(s) with | d,_ => d endmatch;
  
    Return a list of all the directory names in a path.
    For example a/b/c gives "a", "a/b"
    fun directories (s:string) : list[string] =>
       let d,b = split1 s in
       if d == "" then Empty[string]
       elif d == #sep then Empty[string]
       else directories d + d
    ;
  
    Join 3 and 4 strings into a pathname.
    fun join(a:string, b:string, c:string)=> join(join(a,b),c);
    fun join(a:string, b:string, c:string,d:string)=> join(join(join(a,b),c),d);
  
    Join 2 strings into a pathname (curried form).
    fun join(x:string) (y:string) => join(x,y);
  
    Join all the strings in a list into a pathname.
    fun join(ps: List::list[string])=> List::fold_left Filename::join of (string) "" ps;
  
    Split off extension. Includes the dot.
    Invariant: input = basename + extension.
    Works backwards until it hits a dot, path separator,
    or end of data. If a dot, strip it and the tail of the string,
    otherwise return the original string.
    fun split_extension (s:string): string * string = {
       var n = s.len;
       if n > 0uz do
         for var i in s.len - 1uz downto 0uz do
           var ch = s.[i];
           if ch == char "." return s.[to i],s.[i to];
           if ch == char #sep return s,"";
         done
       done
       return s,"";
    }
  
    Remove an extension from a filename if there is one.
    fun strip_extension (s:string) => s.split_extension.0;
  
    Get extension if there is one. Includes the dot.
    fun get_extension (s:string) => s.split_extension.1;
  
  }
  
  Windows Filenames
  class Win32Filename
  {
    inherit Filename_class[Win32];
    instance Filename_class[Win32] {
      fun sep() => "\\";
      fun executable_extension ()=> ".exe";
      fun static_object_extension() => ".obj";
      fun dynamic_object_extension() => ".obj";
      fun static_library_extension() => ".lib";
      fun dynamic_library_extension() => ".dll";
      fun is_absolute_filename (f:string) =>
        f.[0] == "\\".char or // no drive letter
        f.[1] == ":".char and f.[2] == "\\".char // with drive letter
      ;
     fun root_subdir (s:string) => "C:\\"+s;
  
    }
  }
  
  OSX Filenames
  class OsxFilename
  {
    inherit Filename_class[Osx];
    instance Filename_class[Osx] {
      fun sep() => "/";
      fun executable_extension ()=> "";
      fun static_object_extension() => ".o";
      fun dynamic_object_extension() => ".os";
      fun static_library_extension() => ".a";
      fun dynamic_library_extension() => ".dylib";
      fun is_absolute_filename (f:string) => f.[0] == "/";
      fun root_subdir (s:string) => "/"+s;
  
    }
  }
  
  Posix Filenames
  class PosixFilename
  {
    inherit Filename_class[Posix];
    instance Filename_class[Posix] {
      fun sep() => "/";
      fun executable_extension ()=> "";
      fun static_object_extension() => ".o";
      fun dynamic_object_extension() => ".os";
      fun static_library_extension() => ".a";
      fun dynamic_library_extension() => ".so";
      fun is_absolute_filename (f:string) => f.[0] == "/";
      fun root_subdir (s:string) => "/"+s;
    }
  }
  
  Host Filenames.
  class Filename
  {
  if PLAT_WIN32 do
    inherit Win32Filename;
  elif PLAT_MACOSX do
    inherit OsxFilename;
  else
    inherit PosixFilename;
  done
  }