" > TreeCopy 1.63a 20-Oct-2018 I v1.52 23-May-2005: Does multiple files, reads params, memory bugfix & v1.55 15-Aug-2006: Added -?, fx1 (9 v1.56 19-Nov-2006: *MOUNT on ADFS leaves files open 22 v1.57 15-Jan-2007: GetEnv works on ARM CoPro <= v1.58 29-Jun-2009: Patched for DFS-like non-DFS systems FL v1.59 25-Jan-2012: Full dates copied, relocates above BASIC on 65-Tube PF prepends "@." on reading, dest set to WR/wr before openout Z@ metadata set on directory after *dir ^ if "@" exists d: v1.59a 02-Mar-2014: Creating dir displays "CDir ..." nK v1.59b 09-Mar-2015: Writing to NetFS: directories only have attrs set xH v1.59c 20-Jun-2015: Fixed reading SJ dates and checking for non-SJ < v1.59d 05-Feb-2016: X%/Y% restored after error handler H v1.60 05-Feb-2016: (F)orce option, Overwrite Y/N/A, -debug option 7 GBPB unlocks destination file D v1.60a 10-Feb-2016: Disk full aborts, all,sub,ex options work. c Bug: PROCGBPB Skip/Abort doesn't work. CLOSE# tried to close closed file. b v1.60b 12-Feb-2016: PROCGBPB unlocks DFS files. GBPB Skip/Abort CLOSE only closes dest file. Y v1.61 12-Feb-2016: Remove FNpath/FNlibpath usage. Experiment: single-file copying. K v1.61a 22-Jun-2016: Fix reading access from DFS, writing to non-HADFS S v1.62 08-Feb-2017: Bugfix for running on ARM BASIC 1.00 with no SYS command. ; v1.63 18-Jan-2018: Added experimental Update option. M v1.63a 20-Oct-2018: GBPB uses faster OPENUP if dest file suitable size. : Z&87:=himem0:23;2,53;0;0;0:A$=OS_GetEnv+" ": 13"TreeCopy 1.63a"'1314,"="):*FX1 : P."PAGE=&";~PAGE;" LOMEM=&";~LOMEM;" HIMEM=&";~HIMEM F ctrl% 31,name% 127:X%=ctrl%:Y%=X%256: ::Q$=13:=himem2: J Syntax: (fs:)src (fs:)dest ACEFPRSU (-dest) (-debug) (-quit (*)name) "\Q$="":S$="":D$="":pa%=1:cf%=1:rc%=1:ex%=1:fc%=1:all%=1:sub%=1:upd%=:vb%=:dst%=:dbg%= , P.">"A$"<" 6=A$>1:pa%=:cf%=:rc%=:fc%=:ex%=:all%=:sub%=:upd%= @3 I%=0: A$,6)="-quit ":Q$=A$,7, A$-7):A$="" J- A$,4)="-deb":dbg%=:A$=A$,A$," ")+1) T+ A$,2)="-d":dst%=:A$=A$,A$," ")+1) ^G A$: S$="" A$,1)<>"-":I%=A$+" "," "):S$=A$,I%-1):A$=A$,I%) hG A$: D$="" A$,1)<>"-":I%=A$+" "," "):D$=A$,I%-1):A$=A$,I%) r, S$: D$:I%=A$,1)="~": I%:A$=A$,2) |% S$: D$: A$,1)="A":all%=I% $ S$: D$: A$,1)="C":cf%=I% $ S$: D$: A$,1)="E":ex%=I% $ S$: D$: A$,1)="F":fc%=I% $ S$: D$: A$,1)="P":pa%=I% $ S$: D$: A$,1)="R":rc%=I% % S$: D$: A$,1)="S":sub%=I% % S$: D$: A$,1)="U":upd%=I% $ S$: D$: A$,1)="V":vb%=I% ` A$="-? "::"Syntax: TreeCopy (fs:)src (fs:)dest"'" ACEFPRSUV (-dest) (-quit (*)name)":q @A$=A$,2): IF INSTR(" ACDEFPRSU",LEFT$(A$,1)):A$=MID$(A$,2) O A$="":maxlength%=((--2200)-256)+2560*(dbg%>&FFFF): data% maxlength% * maxlength%=maxlength%-256:REM Bugfix 1:FS1$=S$,I%-1):D1$=S$,I%+1) 4 :: -1:Q$=13:=himem2: "FX1,"+ß:q & S$: I%<2:FS1$="":D1$=S$ 0Z S$=""::FS1$=inp("Source filing system: "): FS1$:D1$=inp("Source dir: ") FS1$ :Lfs1%=fs:at$="@.",info("@")=2):up%=info("^")=2: info("$")<>2:fs1%=4 DG IF fs1%=4:type%=(FNinfo(D1$+".$")):type%=2 ELSE type%=FNinfo(D1$) NH fs1%=4:D1$=58:D1$=2:type%=(info(D1$+".$")) type%=info(D1$) X=one%=type%=1: type%=0:fs1%>4: D1$" does not exist.":q b2 IF type%=0:PRINT D1$" does not exist.":PROCq l7 IF type%<>2:PRINT D1$" is not a directory.":PROCq v. pa%>0:'"Pause to change disks";:pa%=yn 8 D$:I%=D$,":"): I%>1:FS2$=D$,I%-1):d$=D$,I%+1)  D$: I%<2:FS2$="":d$=D$ H D$="":':FS2$=inp("Dest. filing system: "):d$=inp("Dest. dir: ")  FS2$="":FS2$=FS1$ 2mnt1$="":mnt2$="":d2$="":Dest:fs2%=fs:d2$=d$  fs2%>4 d2$,"$")+d2$,"%")+d2$,"&")+d2$,":")+d2$,"@")=0:"Dest. dir must be an absolute pathname, must contain one of $,%,&,@ or :":q = fs1%<>fs2%:D1$=58:D1$,3)=46:D1$,3)=d2$,3):pa%=  fs1%=fs2%:FS1$="":FS2$="" ' fs1%<>fs2% fs1%=8:mnt1$="Mount" ' fs1%<>fs2% fs2%=8:mnt2$="Mount" | fs1%=fs2% pa% (fs1%=8 fs1%=16):mnt1$="Mount "+drv:mnt2$=mnt1$: mnt2$: D1$,1)<>"$" D1$,1)<>":":D1$="$."+D1$ , fs2%>4:one%:cd(d2$) fs2%=4:*DIR$ % cf%>0:pr:"Confirm";:cf%=yn: 0 rc%>0:rc%=: fs1%>4 "Recurse";:rc%=yn:  + fc%>0:"Force overwriting";:fc%=yn: & fs1%=5:mask%=&FFFFFF3B mask%=  = ex%>0:ex%=: fs2%=4 "Expand into DFS dirs";:ex%=yn: *( ex%d2$,2)=".$":d2$=d2$, d2$-2) 4*dcd%=:dest$=d2$+".":ddir$="":dpfx$="" >&:: Remove use of FNpath/FNlibpath HL:: IF fs1%<>fs2%:IF FNinfo("^")=2:ddir$="DIR ":dest$="":oldpath$=FNpath Rw:: IF fs1%=fs2% ANDfs1%>4 ANDup% AND NOTpa%:IF FNinfo("%")=2:ddir$="LIB ":dpfx$="%.":dest$="%.":oldpath$=FNlibpath \#:: IF LENddir$:OSCLI ddir$+d2$ f-Src:olddir$=""::: IF up%:olddir$=FNpath pfall%>0:all%=: fs=4:"Do all DFS dirs";:all%=yn:: all%:sub%>1:"Put in subdirs";:sub%=yn: z sub%:sub$="." sub$="/" 0one%:'"Dir. ";D1$:"DIR "+D1$:d2$=d2$+"."  Cur.Dir=D1$, src Yout$="":Z%=0:Ptr%=data%:done%=:?Ptr%=0:F$="":index%=0:level%=0:dir%=33: all%:*DIR !  err:"FX1,"+ß:q 9X%=ctrl%:Y%=X%256:one%:rd:F$=D1$:out$=d2$:GBPB:q >:: REPEAT PROCDo:UNTIL done% AND Z%=0:PROCend:PRINT:PROCq  Do: done% Z%=0::q : q: dst%:Dest =himem2: Q$="":Q$=13:  Q$,1)="*": Q$:Q$=13:  Q$ : :: DEFPROCend 1:: IF LENddir$:PROCDest:OSCLI ddir$+oldpath$ :: IF FNfs<>fs1%:PROCSrc $):: IF LENolddir$:OSCLI"DIR "+olddir$ .:: ENDPROC 8: B/ݤinp(A$): A$;: ""B$: B$,1)="*": B$ L B$,1)<>"*":=B$ V: ` ݤerr j, =195:(Z%&C)=4:fc%:"ACCESS "+d$:= tpr:11:: <128::= ~ =198:(Z%12)=12:#c2%  =198:dst%:Src  =198::=: Disk full k =195 (Z%&C)=4:". Overwrite";:笤yna(1):Ptr%=P%+length%:= =195 (Z%&C)=4:"ACCESS "+d$::= " <128:<>17:" at line ";; %". Skip";:sk%=yn: sk% Z%=0: # Z%=0 sk%:F$=file$:T%=A%:= )(Z%&C)=4:: sk%:Ptr%=P%+length%:= 8(Z%&C)=12 sk%::Ptr%=data%:?Ptr%=0:F$="":ErrGBPB 9 IF(Z%AND&C)=12:PRINT"d$:"d$'"in$:"in$'"file$:"file$ 8 PRINT"Type: ";T%;" File: ";file$'"Operation: ";Z% F$="":=<128 -1 : H Note! Has to do different things if source or dest causes an error  < Handle will only be open on the selected filing system  ErrGBPB !(Z%&100):#c2%:Src #c1% ( Z%=0: 2: < Z% holds: F 0 - reading files P) 1 - source file too big for buffer ZE 2 - buffer full, adding source file will go past end of buffer d< 2 - buffer full, adding source file has filled buffer n. 3 - single file in buffer with upd% set x 4 - writing files  8 $ &00C - source selected in GBPB ) &10C - destination selected in GBPB : Do: Z%=0:Read: "(Z%&C)=0 ?data%=0:Z%=Z%+12 '(Z%&C)=0:wr:Z%=Z%+4:Ptr%=data%: (Z%&C)=4:Write: Z%<8: .(Z%&C)=8:rd:Z%=Z%+4:Ptr%=data%: pa%: (Z%3)<>1:Z%=0:?Ptr%=0: 3(Z%&C)=12:GBPB:Z%=0:F$="":Ptr%=data%:?Ptr%=0  : >Read: F$:file$=F$:A%=info(at$+file$):F$="":DoFile2: 0:X%!1=name%:X%!5=1:X%!9=index%:A%=8: &FFD1 +ret%=X%!5:index%=X%!9: ret%=0:DoFile1 " ret%<>0 Z%<>0: ret%=0: ,, all%:A%=3:file$="":X%!10=0:DoFile2: 6Rindex%=0:dir%=dir%+1: dir%=34 dir%=":" dir%="*" dir%="|":dir%=dir%+1 @ dir%="a":dir%="{" JD dir%<127:"DIR "+dir% A%=3:file$="":X%!10=0:DoFile2:*DIR $ T ^: h DoFile1 rV?(name%+?name%+1)=13:file$=$(name%+1)+" ":file$=file$,file$," ")-1):file$=46: |&A%=info(at$+file$): A%=2 rc%: #pr:A%=2:"Dir. "; "File "; D1$;:D1$:46  all% dir%<>"$": dir%,46 ; file$;: IF A%<1 OR A%>3:PRINT" not loadable":ENDPROC  cf%: yna(0):: A%=((A%-1)1)+1:DoFile2: : DoFile2:type%=A% O type%<>3:ReadAttr at$:A%=info("@"):ReadAttr:length%=0 length%=0 R PROCpr:P.~!0 AND &FFFF;" ";~!2 AND &FFFF;" ";~!4 AND &FFFF;" ";~!6 AND &FFFF  diff: = length%+2+file$>maxlength%-48:F$=file$:T%=type%:Z%=1: : # P.">DoFile2:";file$;":";type% [$(Ptr%+1)=in(file$):P%=Ptr%+2+$(Ptr%+1): P%+length%+48>end%:F$=file$:T%=type%:Z%=2: 8 type%=3 end%=data%+maxlength%:done%=:Z%=2:11: &= type%=3:DirUp:index%=!end%:end%=end%+4:level%=level%-1 0 Should check no 'R' :a!P%=load%:P%!4=exec%:P%!8=length%:P%!12=attr%:P%!15=mtime%:P%!18=cdate%:P%!20=ctime%:P%=P%+24 D type%<>3:pr length%=0 N type%=1:"LOAD "+at$+file$+" "+~P% type%=2:"DIR "+file$:end%=end%-4:!end%=index%:index%=0:D1$=D1$+"."+file$:level%=level%+1 X Reset no 'R' b2?Ptr%=type%:Ptr%=P%+length%:?Ptr%=0:upd%:Z%=3 l v: ݤin(F$): fs1%>4:=F$ + all% dir%<>"$":=dir%+sub$+F$ :=F$ : ?DirUp::D1$=D1$, D1$-1): D1$,1)=".":D1$=D1$, D1$-1)  up%:"DIR ^" "DIR "+D1$  : @Dest: pa% pr:"Insert dest. and press SPACE";: <65:  FS2$: FS2$ 8 mnt2$: mnt2$: d2$:one%:"DIR "+d2$, d2$-1) M(mnt2$<>"" d2$<>"")(FS1$+FS2$="" up%):one%:"DIR "+d2$, d2$-1)  : @Src: pa%:pr:"Insert source and press SPACE";: <65:   FS1$: FS1$ ' mnt1$: mnt1$:one%:"DIR "+D1$  : mnt1$<>"" (FS1$+FS2$="" up%):one%:"DIR "+D1$ * 4: >Write:?Ptr%=0:Z%=Z%+4: H& P.">Write:";?Ptr%;" ";~!Ptr%;"<" R-A%=?Ptr%:file$=$(Ptr%+1):P%=Ptr%+2+file$ \N P.;~P%!0;" ";~P%!4;" ";~P%!8;" ";~P%!12;" ";~P%!15;" ";~P%!18;" ";~P%!20 faload%=!P%:exec%=P%!4:length%=P%!8:attr%=P%!12:mtime%=P%!15:cdate%=P%!18:ctime%=P%!20:P%=P%+24 p, A%=3:file$="@":SetAttr:DUp:Ptr%=P%: z A%=2:DDir:Ptr%=P%: HCheck: "Save ";d2$;file$;: dpfx$="":d$=d2$+file$ d$=dpfx$+file$ `"SAVE "+d$+" "+~P%+"+"+~length%+" "+~exec%+" "+~load%:A%=1:SetAttr:Ptr%=P%+length%:: : (ReadAttr: OSFILE 5 already called  PRINT"file$=>"file$"<" Dload%=X%!2:exec%=X%!6:length%=X%!10 (A%=1):X%!17=0:X%!20=X%!15  fs1%=5:$(name%+8)=file$:oswD(&14,&12000A00+256*file$,&40000000): name%?3=0:X%!15=name%!10:X%!17=name%!12:X%!20=name%!5:X%!22=name%!7: SJ dates wattr%=X%!14: fs1%=16:!X%=name%:$name%=at$,2+(file$=""))+file$:A%=&FD: &FFDD:X%!17=X%!6:X%!21=X%!10: HADFS dates ,mtime%=X%!17:cdate%=X%!20:ctime%=X%!22: : SetAttr: fs2%<4:  fs2%=8: mnt2$ P IF ddir$="":A$=d2$+file$ ELSE IF LENdpfx$:A$=dpfx$+file$ ELSE A$=at$+file$ LA$=d2$+file$: file$="@":A$=A$,A$-2): Stamping dest dir before *DIR ^ _$name%=A$:!X%=name%:X%!2=load%:X%!6=exec%:X%!14=attr%mask%:A%=1+3*(A%<>1)*(fs2%=5): &FFDD y fs2%=16:A%=&FD: &FFDD:X%!6=mtime%:X%!9=cdate%:X%!11=ctime%:A%=&FC: &FFDD:: HADFS dates, keeping account numbers $ fs2%<>5: . fs1%<>16 fs1%<>5: 8dname%!7=attr%:$(name%+10)=A$:oswD(&14,&13000C00+256*A$,&05000000): Write Acorn mdate/SJ cdate Battr%=(attr%&FFFF00)256:name%!8=cdate%:name%!10=ctime%:name%!13=attr%:name%!15=mtime%:$(name%+18)=A$:oswD(&14,&13001400+256*A$,&40000000):: Write SJ full dates L: V $(name%+10)=A$:name%!7=attr%:name%?10=?name%:PROCoswD(&14,&13000C00+256*LEN$(name%+10),&05000000):REM Write Acorn mdate/SJ cdate ` attr%=(attr%AND&FFFF00)DIV256:$(name%+18)=A$:name%!8=cdate%:name%!10=ctime%:name%!14=(mtime%AND&FFFFFF)*256:name%?13=attr%:name%?14=attr%DIV256:PROCoswD(&14,&13001400+256*LEN$(name%+18),&40000000):ENDPROC:REM Write SJ full dates j: tK Needs restructuring of main code to cope with dest name being changed ~4 Convert to dest name before storing in buffer? 9 Dest dir will not always be at same point as source ? Doing one object at a time keeps directories synchronised : ( Info on source in load%/exec%/etc. +ݤdiff: upd%=0 type%=2 file$="":= "Dest:A%=info(d2$+file$):Src ; A%=type%: X%!2=load%: X%!6=exec%: X%!10=length%:= = / PRINT'"Compare ";file$;" with ";d2$;file$ 4 PRINT;type%;" ";~load%;" ";~exec%;" ";~length% - PRINT;A%;" ";~X%!2;" ";~X%!6;" ";~X%!10 P IF A%=type%:IF X%!2=load%:IF X%!6=exec%:IF X%!10=length%:PRINT"SAME":=TRUE  PRINT"DIFF":=FALSE :  Check 7 ex%file$,2,1)="/":file$=file$,1)+"."+file$,3) (D fs1%=4:attr%=(attr%3)(attr%4)15:attr%=attr%((attr%7)*16) 2> fs2%=4:file$=file$,7-2*(file$,2,1)=".")):attr%=attr%8 <* fs2%<>16:(attr%5)=5:attr%=attr%-5 FK all%sub%dcd%<>file$file$,2,1)=".":cd(d2$+file$,1)):dcd%=file$ P Z: d+DUp: d2$=d2$, d2$-1): d2$,1)="." nP IF LENddir$:OSCLI ddir$+dpfx$+"^":REM ELSE OSCLI"DIR "+LEFT$(d2$,LENd2$-1) x : [DDir:"CDir ";: dpfx$="":d2$;file$;:cd(d2$+file$) dpfx$;file$;:cd(dpfx$+file$) UA%=2:d2$=d2$+file$: IF LENddir$:OSCLI ddir$+dpfx$+file$:REM ELSE OSCLI"DIR "+d2$ d2$=d2$+".":: : 6GBPB:file$=in(F$):in$=F$: IFdbg%:PRINT">GBPB"; KA%=info(at$+in$):max%=(end%-data%-48)&FFFF00:max%>X%!10:max%=X%!102 /ReadAttr:out$:d2$=out$:file$="":dpfx$="" diff: 9c2%=0:c1%=(at$+in$): c1%=0:"No input available.": Ngbpb(4,c1%,max%):#c1%:wr:Z%=Z%&100:Check: dpfx$="":d$=d2$ d$=dpfx$ 8'"Copy ";d2$;file$;:d$=d$+file$:$name%=d$:!X%=name% WA%=5:A%=(&FFDD)&FF:fc%=0:A%:(X%?14)8:'"Entry locked. Overwrite";:笤yna(1): AA%:X%!14=(fs2%>4)&33:A%=4:&FFDD : Unlock destination file DX%!2=load%:X%!6=exec%:X%!10=0:X%!14=length%:X%?17:X%!14=&400000 TA%=7:&FFDD : Create empty destination file "KX%?14=(fs2%>4)&33:A%=4:&FFDD : Ensure access is WR/wr ,[(X%!10>length%)X%!10=0:c2%=(d$) c2%=(d$): OPENUP faster if existing not too long 6*c2%=0:'"No output available.":Src: @/gbpb(2,c2%,max%):ptr%=max%:b%=max%::#c2% J):rd:Z%=Z%&FF:c1%=(in$):#c1%=ptr% T' ptr%+max%>length%:b%=length%-ptr% ^=gbpb(4,c1%,b%):#c1%:wr:Z%=Z%&100:c2%=(d$):#c2%=ptr% h&gbpb(2,c2%,b%)::ptr%=#c2%:#c2% r! ptr%>=length%:A%=1:SetAttr |rd:Z%=Z%&FF:: : 8gbpb(A%,c%,b%):?X%=c%:X%!1=data%:X%!5=b%: &FFD1: : Vcd(F$): fs>4: info("$")=2: info(F$)<>2:"CDIR "+F$: +LEFT$(" 255",fs2%=5)  : Dݤinfo(F$):?X%=name%:X%?1=name%256:$name%=F$:A%=5:=(&FFDD)&FF : "rd:Src:pr:"Reading...";: #wr:Dest:pr:"Writing...";: pr: :  :  ݤyn: A% *"? (Y/N)";::A%= &DF: A%=89 A%=78 1 5,8);: A%=89:"Yes ";:= "No ";:= &: 0ݤyna(B%): A% :. IFB%=1:IFfc%=0:=0 ELSE IFB%=3:IFsk%=0:=0 D3"? (Y/N/A)";::A%= &DF: A%=89 A%=78 A%=65 N; 7,127);: A%=89:"Yes ";:= A%=78:"No ";:= X8"All ";: B%=0:cf%= B%=1:fc%= B%=3:sk%= b= l: v!ݤfs: A%,E%,Y%:=(&FFDA)&FF :  REM Corrupt data area: 8 DEFFNpath:LOCAL A%,n$,p$:A%=FNinfo("@"):REM Bugfix ' A%=6:REPEAT:X%!1=data%:CALL &FFD1 , ?(data%+2+?data%+?(data%+?data%+1))=13 ' n$=$(data%+2+?data%):OSCLI"DIR ^" & n$=LEFT$(n$,INSTR(n$+" "," ")-1) ' p$=n$+"."+p$:UNTIL n$="$"ORn$="&"  p$=LEFT$(p$,LEN p$-1) * OSCLI"DIR "+p$:X%!1=data%:CALL &FFD1 ( ?(data%+1+?data%)=13:n$=$(data%+1)  IF LENn$:=":"+n$+"."+p$ s X%!1=data%:A%=5:CALL &FFD1:?(data%+1+?data%)=13:n$=$(data%+1):n$=LEFT$(n$,INSTR(n$+" "," ")-1):=":"+n$+"."+p$  DEFFNlibpath ( LOCAL a$,b$:a$=FNpath:OSCLI"DIR %" " b$=FNpath:OSCLI"DIR "+a$:=b$ : *!ݤdrv:A%=6:X%!1=data%: &FFD1 4$?(data%+1+?data%)=13:=$(data%+1) >: HGoswD(A%,D%,E%): X%,Y%:X%=name%:Y%=X%256:!X%=D%:X%!4=E%:&FFF1: R: \ݤOS_GetEnv fKA%=(>&FFFF)&900:A%=((>&9FFF)A%)((&1400-)(A%=0)):?(P-3)A%=&B00 piA$=$(-&E00+A%):A%=0:run$=A$:ș16A$,,A%:ș72,"",A%:A$=A$,1+A$+" "," ",1+A$," "))):A$=0:A$=run$ z*Y%=-10:A$=" "+A$:A$=A$,2):A$<>32 6Y%:A$=34:A%=A$,"""",2)+1 Y%:A%=A$+" "," ") CY%:run$=A$,1-(A$=34),A%-1+2*(A$=34)):run$<>"":A$=A$,A%+1) :=A$ : ' DEFPROCadfs:B$="":IFA$="":ENDPROC M FORA%=1TOLENA$:B%=ASCMID$(A$,A%,1):B$=B$+CHR$(B%AND((B%<64)OR&DF)):NEXT : IFB$="A."ORB$="AD."ORB$="ADF."ORB$="ADFS":A$="FADFS" ENDPROC : "ݤhimem0:=&B800 >&FFFF:= 7A%=130:((&FFF4)&FFFF00)=&FFFF00 ?&FFF7<>&6C:= =&C000:=&800:=&8000 4A%=0P-+34:A%!&C000=A%!::=&C000:=&F800: ݤhimem1:<:=&F800 = )ݤhimem2:<:=&800:?&801=-1:= = :