PreviousUpNext

15.4.1255  src/lib/std/src/winix/winix-path-g.pkg

## winix-path-g.pkg

# Compiled by:
#     src/lib/std/src/standard-core.sublib



# A generic for the the winix__premicrothread::path package.
#
# NOTE: these operations are currently not very efficient, since they
# explode the path into its disk_volume and arcs.  A better implementation
# would work "in situ."
#
# XXX BUGGO FIXME Having the arcs in a list is so inconvenient
#                 (due to needing to add/remove from the end)
#                 that
#                    src/app/makelib/paths/anchor-dictionary.pkg
#                 winds up re-implementing them with reversed
#                 path ordering.
#
#                 It would probably be better to store the arcs
#                 in Chris Osaki's pure-functional double-ended
#                 queues.

stipulate
    package string= string_guts;        # string_guts   is from   src/lib/std/src/string-guts.pkg
herein
generic package winix_path_g (

    ospath_base:
        api {
            exception PATH;

            Arc_Kind = NULL | PARENT | CURRENT | ARC  String;

            ilkify:  String -> Arc_Kind;

            parent_arc:   String;
            current_arc:  String;

            volume_is_valid:  ((Bool, substring::Substring)) -> Bool;

            split_vol_path:  String -> ((Bool, substring::Substring, substring::Substring));

                # Split a string into the disk_volume part and arcs part and note whether it
                # is absolute.
                # Note: it is guaranteed that this is never called with "".

            join_vol_path: ((Bool, String, String)) -> String;          #  join a disk_volume and path; raise Path on invalid volumes 
            arc_sep_char:  Char;                                        #  the character used to separate arcs (e.g., '/' on UNIX) 
            same_vol:      (String, String) -> Bool;

        }
)
: (weak)
Winix_Path                                              # Winix_Path    is from   src/lib/std/src/winix/winix-path.api
{

    package p  =  ospath_base;
    package ss =  substring;                            # substring     is from   src/lib/std/src/substring.pkg

    exception PATH = p::PATH;

    arc_sep_string
        =
        string::from_char  p::arc_sep_char;

    parent_arc  =  p::parent_arc;
    current_arc =  p::current_arc;

    # meld_arcs is like list::@,
    # except that a trailing empty arc in the
    # first argument is dropped:
    #
    fun meld_arcs ([],      al2) =>  al2;
        meld_arcs ([""],    al2) =>  al2;
        meld_arcs (a ! al1, al2) =>  a ! meld_arcs (al1, al2);
    end;

    fun volume_is_valid { is_absolute, disk_volume }
        =
        p::volume_is_valid (is_absolute, ss::from_string disk_volume);

    fun from_string ""
            =>
            { is_absolute => FALSE,
              disk_volume => "",
              arcs        => []
            };

        from_string p
            =>
            {   fields
                    =
                    ss::fields
                        (\\ c =  (c == p::arc_sep_char));

                my (is_absolute, disk_volume, rest)
                    =
                    p::split_vol_path p;

                { is_absolute,
                  disk_volume =>  ss::to_string  disk_volume,
                  arcs        =>  list::map  ss::to_string  (fields rest)
                };
            };
    end;

    fun to_string { is_absolute=>FALSE, disk_volume, arcs=>"" ! _}
            =>
            raise exception PATH;

        to_string { is_absolute, disk_volume, arcs }
             =>
             {   fun f [] => [""];
                     f [a] => [a];
                     f (a ! al) => a ! arc_sep_string ! (f al);
                 end;

                 string::cat (p::join_vol_path (is_absolute, disk_volume, "") ! f arcs);
             };
    end;

    fun get_volume p
        =
        .disk_volume (from_string p);

    fun get_parent p
        =
        {   fun get_parent' []
                    =>
                    [parent_arc];

                get_parent' [a]
                     =>
                     case (p::ilkify a)
                       
                          p::CURRENT =>  [parent_arc];
                          p::PARENT  =>  [parent_arc, parent_arc];
                          p::NULL    =>  [parent_arc];
                          _          =>  [];
                     esac;

                get_parent' (a ! al)
                    =>
                    a ! get_parent' al;
            end;

            case (from_string p)
              
                 { is_absolute=>TRUE, disk_volume, arcs => [""] }
                     =>
                     p;

                 { is_absolute=>TRUE, disk_volume, arcs }
                     =>
                     to_string { is_absolute => TRUE, disk_volume, arcs => get_parent' arcs };

                 { is_absolute=>FALSE, disk_volume, arcs }
                     =>
                     case (get_parent' arcs)
                       
                          []  =>  to_string { is_absolute => FALSE, disk_volume, arcs => [current_arc] };
                          al' =>  to_string { is_absolute => FALSE, disk_volume, arcs => al' };
                     esac;
            esac;
        };

    fun split_path_into_dir_and_file p
        =
        {   my { is_absolute, disk_volume, arcs }
               =
               from_string p;

            fun split []  =>  ([], "");
                split [f] =>  ([], f);

                split (a ! al)
                     =>
                     {   my (d, f) = split al;

                         (a ! d,  f);
                     };
            end;

            fun split' p
                =
                {   my (d, f) = split p;

                    { dir  => to_string { is_absolute, disk_volume, arcs=>d },
                      file => f
                    };
                };

            split' arcs;
        };

    fun make_path_from_dir_and_file { dir=>"", file }
            =>
            file;

        make_path_from_dir_and_file { dir, file }
            =>
            {   my { is_absolute, disk_volume, arcs } = from_string dir;

                to_string { is_absolute, disk_volume, arcs => meld_arcs (arcs, [file]) };
            };
    end;

    fun dir  p =  .dir  (split_path_into_dir_and_file  p);
    fun file p =  .file (split_path_into_dir_and_file  p);
    
    fun split_base_ext p
        =
        {   my { dir, file }
                =
                split_path_into_dir_and_file  p;

            my (file', ext')
                =
                ss::split_off_suffix
                    {. #c != '.'; }
                    (ss::from_string file);

            file_len
                =
                ss::size  file';

            my (file, ext)
                =
                if   (file_len <= 1  or  ss::is_empty ext')
                    
                     (file, NULL);
                else
                     ( ss::to_string (ss::drop_last 1 file'),
                       THE (ss::to_string ext')
                     );
                fi;

            {   base => make_path_from_dir_and_file { dir, file },
                ext
            };
        };

    fun join_base_ext { base,  ext => NULL    } =>  base;
        join_base_ext { base,  ext => THE ""  } =>  base;

        join_base_ext { base,  ext => THE ext }
             =>
             {   my { dir, file }
                     =
                     split_path_into_dir_and_file  base;

                 make_path_from_dir_and_file {
                   dir,
                   file => string::cat [file, ".", ext]
                 };
             };
    end;

    fun base p =  .base (split_base_ext p);
    fun ext  p =  .ext  (split_base_ext p);

    fun make_canonical ""
            =>
            current_arc;

        make_canonical p
            =>
            {   fun scan_arcs ([],   []) =>  [p::CURRENT];
                    scan_arcs (l,    []) =>  list::reverse l;
                    scan_arcs ([], [""]) =>  [p::NULL];

                    scan_arcs (l,  a ! al)
                        =>
                        case (p::ilkify a)
                          
                             p::NULL    =>  scan_arcs (l, al);
                             p::CURRENT =>  scan_arcs (l, al);

                             p::PARENT
                                 =>
                                 case l
                                   
                                      (p::ARC _ ! r) =>   scan_arcs (r, al);
                                      _              =>   scan_arcs (p::PARENT ! l, al);
                                  esac;

                             a'  =>
                                 scan_arcs (a' ! l,  al);
                        esac;
                end;

                fun scan_path rel_path
                    =
                    scan_arcs([], rel_path);

                fun mk_arc (p::ARC a)  =>  a;
                    mk_arc (p::PARENT) =>  parent_arc;
                    mk_arc _           =>  raise exception DIE "make_canonical: impossible";
                end;

                fun filter_arcs (TRUE, p::PARENT ! r) =>  filter_arcs (TRUE, r);
                    filter_arcs (TRUE, [])            =>  [""];
                    filter_arcs (TRUE, [p::NULL])     =>  [""];
                    filter_arcs (TRUE, [p::CURRENT])  =>  [""];
                    filter_arcs (FALSE, [p::CURRENT]) =>  [current_arc];
                    filter_arcs (_, al)               =>  list::map mk_arc al;
                end;

                my { is_absolute, disk_volume, arcs }
                    =
                    from_string p;

                to_string { is_absolute,
                            disk_volume,
                            arcs =>  filter_arcs  (is_absolute,  scan_path arcs)
                          };
            };
    end;

    fun is_canonical p   =   (p == make_canonical p);
    fun is_absolute  p   =   .is_absolute (from_string p);
    fun is_relative  p   =   bool::not(.is_absolute (from_string p));

    fun make_absolute { path, relative_to }
        =
        case (from_string path, from_string relative_to)
          
             (_, { is_absolute=>FALSE, ... } ) =>  raise exception PATH;
             ( { is_absolute=>TRUE, ... }, _)  =>  path;

             ( { disk_volume=>v1, arcs=>al1, ... },
               { disk_volume=>v2, arcs=>al2, ... }
             )   =>
                 {   fun mk_canon  disk_volume
                         =
                         make_canonical (
                             to_string {
                               is_absolute =>  TRUE,
                               disk_volume,
                               arcs        =>  list::(@) (al2, al1)
                             }
                         );
                
                     if     (p::same_vol (v1, v2)   ) mk_canon v1;
                     elif   (v1 == ""               ) mk_canon v2;
                     elif   (v2 == ""               ) mk_canon v1;
                     else                             raise exception PATH;
                     fi;
                };
          esac;


    fun make_relative { path, relative_to }
        =
        if   (is_absolute  relative_to)
            
             if   (is_relative path)
                 
                  path;
             else
                  my { disk_volume=>v1, arcs=>al1, ... } =  from_string  path;
                  my { disk_volume=>v2, arcs=>al2, ... } =  from_string  (make_canonical relative_to);

                  fun strip (l, []) => mk_arcs l;
                      strip ([], l) => dot_dot([], l);

                      strip (l1 as (x1 ! r1), l2 as (x2 ! r2))
                          =>
                          if   (x1 == x2)
                               strip (r1, r2);
                          else dot_dot (l1, l2); fi;
                  end 

                  also
                  fun dot_dot (al,     []) =>  al;
                      dot_dot (al,  _ ! r) =>  dot_dot (parent_arc ! al, r);
                  end 

                  also
                  fun mk_arcs [] => [current_arc];
                      mk_arcs al => al;
                  end;

                  if   (not (p::same_vol (v1, v2)))
                      
                       raise exception PATH;
                  else
                       case (al1, al2)
                         
                            ([""], [""])
                                =>
                                current_arc;

                            ([""], _)
                                =>
                                to_string { is_absolute=>FALSE, disk_volume=>"", arcs=>dot_dot([], al2) };

                            _   =>
                                to_string { is_absolute=>FALSE, disk_volume=>"", arcs=>strip (al1, al2) };
                       esac;
                  fi;
             fi;
        else
            raise exception PATH;
        fi;

    fun is_root path
        =
        case (from_string path)
          
             { is_absolute => TRUE, arcs => [""], ... }
                =>
                TRUE;

             _   =>
                 FALSE;
        esac;

    fun cat (p1, p2)
        =
        case (from_string p1, from_string p2)
          
             (_, { is_absolute=>TRUE, ... } )
                 =>
                 raise exception PATH;

             ( { is_absolute, disk_volume=>v1, arcs=>al1 }, { disk_volume=>v2, arcs=>al2, ... } )
                 =>
                 if (p::same_vol (v2, "") or p::same_vol (v1, v2) )
                     to_string { is_absolute, disk_volume=>v1, arcs=>meld_arcs (al1, al2) };
                 else
                     raise exception PATH;
                 fi;
        esac;


    stipulate
        fun from_unix_path' up
            =
            {   fun tr "."  =>  p::current_arc;
                    tr ".." =>  p::parent_arc;
                    tr arc  =>  arc;
                end;

                case (string::fields (\\ c =  c == '/') up)
                  
                    "" ! arcs =>  { is_absolute => TRUE,  disk_volume => "", arcs => map tr arcs };
                         arcs =>  { is_absolute => FALSE, disk_volume => "", arcs => map tr arcs };
                esac;
            };

        fun to_unix_path' { is_absolute, disk_volume => "", arcs }
                =>
                {   fun tr arc
                        =
                        if   (arc == p::current_arc ) ".";
                        elif (arc == p::parent_arc  ) "..";
                        elif (char::contains arc '/') raise exception PATH;
                        else                          arc;
                        fi;

                    string::join
                        "/"
                        (is_absolute   ??   "" ! arcs           # Add a leading / to the result.
                                       ::        arcs);
                };

            to_unix_path' _
                =>
                raise exception PATH;
        end;
    herein
        from_unix_path =  to_string      o  from_unix_path';
        to_unix_path   =  to_unix_path'  o  from_string;
    end;
  };
end;



## COPYRIGHT (c) 1995 AT&T Bell Laboratories.
## Subsequent changes by Jeff Prothero Copyright (c) 2010-2015,
## released per terms of SMLNJ-COPYRIGHT.


Comments and suggestions to: bugs@mythryl.org

PreviousUpNext