#line 106 "/home/travis/build/felix-lang/felix/src/packages/filetools.fdoc"
  class CopyFiles {
    proc processfiles
      (var process: string * string -> bool)
      (basedir:string, re:RE2, tpat:string, live:bool, verbose:bool)
    {
       var ds = StrDict::strdict[string] ();
       var sd = StrDict::strdict[string] ();
       var dirs = StrDict::strdict[bool] ();
       var n = re.NumberOfCapturingGroups;
       var v = varray[StringPiece]$ (n+1).size, StringPiece "";
  //println$ "flx_cp:CopyFiles:processfiles regexp= " + re.pattern;
       // Process a single filename and add it to the pending copy queue
       proc addfile(f:string)
       {
          if Re2::Match(re, StringPiece f, 0, ANCHOR_BOTH, v.stl_begin, v.len.int)
          do
            var src = Filename::join (basedir, f);
            var replacements = Empty[string * string];
            for var k in 0 upto n do
              replacements = Cons (("${" + str k + "}",v.k.string), replacements);
            done
            dst := search_and_replace replacements tpat;
  
            //println$ "Copy " + src + " -> " + dst;
            sd.add src dst;
  
            if ds.haskey dst do
              eprintln$ "Duplicate target " + dst;
              System::exit(1);
            done
            ds.add dst src;
            iter
              (proc (x:string) { dirs.add x true; })
              (Filename::directories dst)
            ;
          done
       }
  
       // Recursively collect files within dir to be copied. dir is relative to basedir.
       proc rfi(dir: string)
       {
         if dir != "." and dir != ".." do
         match Directory::filesin(Filename::join (basedir,dir)) with
         | #None  => ;
         | Some files =>
           List::iter
             (proc (f:string)
             { if f != "." and f != ".." do
                 var d = Filename::join (dir,f);
                 val t = FileStat::filetype (Filename::join (basedir,d));
                 match t with
                   | #REGULAR => addfile d;
                   | #DIRECTORY => rfi d;
                   | _ => ;
                 endmatch;
               done
             }
             )
             files
           ;
         endmatch;
         done
       }
       rfi ("");
  
       // Check that no source file is clobbered
       match src, dst in sd.iterator do
         if sd.haskey dst do
           eprintln$ "Target clobbers src: " + dst;
           System::exit(1);
         done
       done
  
       // Create target directories
       match dir, _ in dirs.iterator do
         if verbose do println$ "mkdir " + dir; done
         if live do
           err:=Directory::mkdir(dir);
           if err !=0 do
             if errno != EEXIST do
               eprintln$ "Mkdir, err=" + strerror() + " .. ignoring";
             done
           done
         done
       done
  
       // And finally, do the actual copying
       match src, dst in sd.iterator do
         if verbose do print$ "cp " + src + "  " + dst; done
         if live do
           if process(src, dst) do
             if verbose do println " #done"; done
           else
             eprintln "COPY FAILED";
             System::exit 1;
           done
         else
           if verbose do println$ "  #proposed"; done
         done
       done
    }
  
    proc copyfiles(basedir:string, re:RE2, tpat:string, live:bool, verbose:bool) =>
      processfiles (FileSystem::filecopy) (basedir, re, tpat, live, verbose)
    ;
  
    proc copyfiles(basedir:string, re:string, tpat:string, live:bool, verbose:bool) =>
      copyfiles(basedir, RE2 re, tpat, live, verbose)
    ;
  }