info-sather
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[info-sather] Recursive copy (was: How's Sather coming along?)


From: Sather User
Subject: [info-sather] Recursive copy (was: How's Sather coming along?)
Date: Thu, 13 Dec 2012 17:23:30 +1030 (CST)

On Fri, 7 Dec 2012, Duke Normandin wrote:

> Whatever you do, don't rush it! There's Christmas shopping to do,
> etc etc. Priorities man!! Priorities!!

Okay, I think you're right, it's time to knock off for a while.  But
here is a little example of a Sather program, a cp -aR in Sather.
Dart (directory and rest to...)  does not have different options, but
always copies a directory tree to a directory and always preserves
atimes and mtimes.

Unfortunately its runtime is 125% as long as that of cp when copying
the Linux kernel source.  On the other hand it preserves the mtimes of
symlinks.  I haven't seen a cp option that does that.  Result of
"dart i j" followed by "find -type l | ls ... --full-time":

lrwxrwxrwx 1 33 2012-12-13 16:21:13.992247352 +1030
./i/linux-3.6.9/arch/microblaze/boot/dts/system.dts ->
../../platform/generic/system.dts

lrwxrwxrwx 1 33 2012-12-13 16:21:13.992247352 +1030
./j/i/linux-3.6.9/arch/microblaze/boot/dts/system.dts ->
../../platform/generic/system.dts

lrwxrwxrwx 1 33 2012-12-13 16:13:31.720854961 +1030
./linux/linux-3.6.9/arch/microblaze/boot/dts/system.dts ->
../../platform/generic/system.dts



All the LSTAT attributes have to be shareds because they are populated
by a C callback provoked by LSTAT::get_stat(name:STR) (overloads with
and without a return value).  SEMILSTAT::create copies some of the
LSTAT shareds into SEMILSTAT object attributes.

I hope you are not irritated by the quirky terminology likening a file
path to a spear with shaft and tip in place of dirname and basename.

-- directory and rest to ...
--
-- Only 2 args, so simple processing.
-- Only copy files.
-- Anything else, symlinks, directories, fifos etc., create anew in the
-- dest location.
-- But in the case of a directory, after creating it, recurse into the
-- subject src directory, and after leaving it set the mtime and atime
-- of the dest directory.

class DART is
   include PATH;
   const noerr, helped, few_args, src_not_dir, dst_shaft_not_dir,
         dst_exists, no_src, no_dst, bad_mkdir;
   const base_mode:INT:=0o777;
   const path_separator:CHAR:='/';
   shared me:STR;
   shared src_stub, dst_stub:STR;

   err(retval:INT, s:STR):INT is
      #ERR+me+": "+s.nl;
      return retval
   end;

   mkdir(s:STR, mode:INT) is
      RUNTIME::mkdir(s,mode);
      e::=ERRNO::errno;
      if e = noerr then return end;
      msg ::= "mkdir("+s+", ...) failed: ";
      case e
      when ERRNO::enoent then raise msg+"missing path component or dangle"
      when ERRNO::eacces then raise msg+"access denied"
      when ERRNO::eexist then raise msg+"file or directory exists"
      when ERRNO::enospc then raise msg+"no space for this directory"
      when ERRNO::erofs then raise msg+"read-only file system"
      else
         raise e
      end
   end;

   make_symlink(oldpath,newpath:STR) is
      ERRNO::reset;
      i::=SYMLINK::make_symlink(oldpath,newpath);
      if i = 0 then return end;
      msg::="write_symlink("+oldpath+","+newpath+"): ";
      e::=ERRNO::errno;
      case e
      when ERRNO::eacces then raise msg+"access denied"
      when ERRNO::eexist then raise msg+newpath+" already exists"
      when ERRNO::enoent then raise msg+"empty oldpath or missing path 
component or dangle"
      when ERRNO::enospc then raise msg+"no space to write link"
      when ERRNO::enotdir then raise msg+"non-directory component in "+newpath
      when ERRNO::erofs then raise msg+newpath+"is on a read-only file system"
      else
         raise e
      end
   end;

   main(args:ARRAY{STR}):INT is
      me:=pathchew(args[0]).tip;
      if args.size /= 3 then
         return err(few_args,"Need 2 args, src fullpath"
                    " (not a symlink) and dest shaft")
      end;
      dst:STR:=args[2];
      if LSTAT::get_stat(dst) = -1 then
         return err(no_dst, "dest shaft does not exist")
      end;
      if LSTAT::st_mode.band(LSTAT::s_fmt) /= LSTAT::s_ifdir then
         return err(src_not_dir, "second arg must be directory")
      end;

      src:STR:=args[1];
      if LSTAT::get_stat(src) = -1 then
         return err(no_src, "first arg (src) does not exist")
      end;
      if LSTAT::st_mode.band(LSTAT::s_fmt) /= LSTAT::s_ifdir then
         return err(src_not_dir, "first arg must be directory")
      end;
      src_dir_stat::=#SEMILSTAT;

      if LSTAT::get_stat(dst+path_separator+pathchew(src).tip) /= -1 then
         return err(dst_exists, "dest tip must not exist")
      end;
      ERRNO::reset;

      newdir::=dst+path_separator+pathchew(src).tip;

      protect
         mkdir(newdir,base_mode);
      when INT then
         msg::="mkdir("+newdir+", ...) failed: ";
         return err(bad_mkdir, msg+"ERRNO::errno="+exception.str)
      when STR then
         return err(bad_mkdir, exception)
      end;

      src_stub:=pathchew(src).shaft;
      dst_stub:=dst;

      protect
         delve(src);
      when $STR then
         #OUT+"delve exception "+exception.str+"\n";
               return 0
      end;

      src_dir_stat.store_times(dst, pathchew(src).tip);

      return noerr
   end;

   delve(s:STR) is
      src_stub:=src_stub+path_separator+s;
      dst_stub:=dst_stub+path_separator+s;
      dd::=#DIR(src_stub, false);          --whether to sort is false
      loop
         n::=dd.elt!;
         if n /= "." and n /= ".." then
            newdest::=dst_stub+path_separator+n;
            LSTAT::get_stat(src_stub+path_separator+n);
            case LSTAT::st_mode.band(LSTAT::s_fmt)
            when LSTAT::s_ifreg then
               -- regular file.  copy it.
               f1::=FILE::open_for_read(src_stub+path_separator+n);
               if f1.error then
                  emsg ::= newdest+": open_for_read failed, errno="
                        +ERRNO::errno.str;
                  raise emsg
               end;
               f2::=FILE::open_for_write(newdest);
               if f2.error then
                  emsg ::= newdest+": open_for_write failed, errno="
                        +ERRNO::errno.str;
                  raise emsg
               end;
               f2+f1.fstr;
               f1.close;
               f2.close;
               times_dest::=pathchew(newdest);
               LSTAT::store_times(times_dest.shaft,times_dest.tip)
            when LSTAT::s_ifdir then
               -- directory.  create it, then call this routine with
               thisstat::=#SEMILSTAT;
               mkdir(newdest, base_mode);
               old_src_stub::=src_stub;
               old_dst_stub::=dst_stub;
               delve(n);
               times_dest::=pathchew(newdest);
               thisstat.store_times(times_dest.shaft,times_dest.tip);
               src_stub := old_src_stub;
               dst_stub := old_dst_stub;
            when LSTAT::s_iflnk then
               -- symlink.
               SYMLINK::read_symlink(src_stub+path_separator+n,
                                     LSTAT::st_size_lobits);
               make_symlink(SYMLINK::val, dst_stub+path_separator+n);
               spear::=pathchew(dst_stub+path_separator+n);
               LSTAT::store_times(spear.shaft, spear.tip)
            else
               #ERR+src_stub+path_separator+n
                     +": unhandled file type "
                     +LSTAT::st_mode.band(LSTAT::s_fmt).nl
            end
         end
      end;
      dd.close
   end
end;


-- 
Michael Talbot-Wilson



reply via email to

[Prev in Thread] Current Thread [Next in Thread]