10 REM > USBFiler 1.31
   20 REM 24-Nov-2009 v0.10 Initial version based on SJFiler
   30 REM 18-Nov-2010 v0.11 Uses fast machine code data transfer
   40 REM 05-Apr-2018 v1.30 Bring all Filers to same version number
   50 REM 31-Mar-2020 v1.31 Optimised Syntax checking
   60 :
   70 os%=FNfx(0,1)AND&FF:IFos%<6:IFFNfx(130,0)=&FFFF ELSE PRINT"Must run on BBC I/O processor":END
   80 VDU10,8:A%=POS:VDU13:IFA%<50:MODE&87:IFHIMEM>&7C00:MODE&83:IFHIMEM>&4000:MODE&80
   90 IF HIMEM<&FFFF:HIMEM=FNfx(132,0)
  100 PROCinit:PRINT"USBFiler v"ver$" by J.G.Harston"':ON ERROR IF FNerr:END
  110 REPEAT:X%=ctrl%:Y%=X%DIV256:D%=37:IFpath$="":path$="\"
  120   IFdrv$="":PRINTCHR$(D%+48)"> "ELSE PRINT"["drv$"] ";
  130   INPUTLINE""A$:PROCdo(FNs(A$))
  140 UNTIL0
  150 :
  160 DEFPROCinit:ver$="1.31"
  170 DIM ctrl% 127,name% 255,result% 63,data% &1FF:X%=ctrl%:Y%=X%DIV256
  180 DIM fn$(255):D%=0:drv$="":path$="\":title$="USB"
  190 fs%=FNfs:d$=".":s$="/":IFos%=32:d$="\":s$="."
  200 cmd$=":DIR:CD:TYPE:DUMP:COPY:INFO:BYE:HELP:QUIT:"
  210 hlp$="::<dir>:<file> [CTRLS]:<file> [7BIT]:<usb source> <dest>:<file>::::"
  220 PROCusb_Init:PROCusb_Sync:PROCusb_Flush:ENDPROC
  230 :
  240 DEFFNerr:OSCLI"FX229":REPORT:PROCusb_Flush
  250 PROCCloseAll:PRINTLEFT$(" "+STR$ERL,ERR<128 AND ERR<>17):=INKEY-1 AND ERR<>17
  260 DEFPROCCloseAll:in%=in%:IFin%:A%=in%:in%=0:CLOSE#A%
  270 out%=out%:IFout%:A%=out%:out%=0:CLOSE#A%
  280 ENDPROC
  290 :
  300 DEFPROCdo(A$):IFA$="?":A$="HELP"
  310 IF LEFT$(A$,1)=";" OR A$="":ENDPROC
  320 IF LEFT$(A$,1)="*" OSCLIA$:ENDPROC
  330 IF LEFT$(A$,1)="." A$="CAT "+MID$(A$,2)
  340 A%=INSTR(A$+" "," "):B$=FNuc(LEFT$(A$,A%-1)):A$=FNs(MID$(A$,A%+1))
  350 IF LENB$=1 AND INSTR("0123",B$):drv$="":D%=VALB$:csd%=-1:ENDPROC
  360 A%=INSTR(cmd$,":"+B$+":"):IF A%=0:PRINT"Bad command":ENDPROC
  370 A%=EVAL("FN_"+B$+"(A$)"):ENDPROC
  380 :
  390 DEFFNsyn(S$):IF A$="":PRINT "Syntax: "B$" "S$:=TRUE ELSE =FALSE
  400 :
  410 REM Commands
  420 REM ========
  430 :
  440 DEFFN_QUIT(A$):PRINT"Quit":A%=FN_BYE("")
  450 ON ERROR END
  460 IF os%>5:*QUIT
  470 END
  480 :
  490 DEFFN_HELP(A$):p%=2:q%=2:REPEAT
  500   A%=INSTR(cmd$,":",p%):PRINT SPC2MID$(cmd$,p%,A%-p%);:p%=A%+1
  510   A%=INSTR(hlp$,":",q%):PRINT TAB(8)MID$(hlp$,q%,A%-q%):q%=A%+1
  520 UNTIL p%>LENcmd$:=0
  530 :
  540 DEFFN_DIR(A$):PROCLstDir(0):=0
  550 DEFFN_EX(A$):PROCLstDir(1):=0
  560 :
  570 DEFFN_CD(A$):IFA$=".":=0
  580 IFA$="\":REPEATPROCusb_Cmd("CD .."):UNTILresult%?1<>ASC":":path$="\":=0
  590 A$=FNcl("",0):PROCusb_Cmd("CD "+A$):IFFNusb_Err:=TRUE
  600 IFA$<>"..":path$=path$+A$+"\":=0
  610 REPEATpath$=LEFT$(path$,LENpath$-1):UNTILRIGHT$("\"+path$,1)="\":=0
  620 :
  630 DEFFN_DUMP(A$):LOCAL ln%,p%,ptr%,cols%:cols%=16:IF FNsyn("<fsp>"):=TRUE
  640 A$=FNcl("",0):PROCusb_Sync:ln%=FNusb_Size(A$):IFln%=0 OR FNusb_Err:=TRUE
  650 PROCusb_WrStr("RD "+A$+CHR$13)
  660 FOR P%=0 TO ln%-1 STEP cols%:B$=""
  670   PRINTFNh0(P%,6)" ";:IF(P%AND&1FF)=0:PROCReadData(P%,ln%):O%=data%
  680   FOR Q%=P% TO P%+cols%-1
  690     IF Q%<ln%:PRINTFNh0(?O%,2)" "ELSE ?O%=32:PRINTSPC3;
  700     A$=CHR$(?O%AND&7F):IF A$>=" " AND A$<="~" B$=B$+A$ ELSE B$=B$+"."
  710 O%=O%+1:NEXT:PRINT B$:NEXT
  720 =0
  730 :
  740 DEFFN_TYPE(A$):LOCAL ln%,p%,ptr%:IF FNsyn("<fsp>"):=TRUE
  750 msk%=INSTR(A$," [")<>0:msk%=(msk%AND&80)OR&7F
  760 A$=FNcl("",0):PROCusb_Sync:ln%=FNusb_Size(A$):IFFNusb_Err:=TRUE
  770 PROCusb_WrStr("RD "+A$+CHR$13)
  780 last%=0:FOR P%=0 TO ln%-1:IF(P%AND&1FF)=0:PROCReadData(P%,ln%):O%=data%
  790   Q%=?O%ANDmsk%:IFmsk%=255:VDUQ% ELSE IFQ%=10 OR Q%=13 OR Q%>31 VDUQ%
  800   IF(Q%=10 OR Q%=13) AND Q%<>last%:VDU23-Q%
  810   IFQ%=9:PRINTSPC(8-(POS MOD 8));
  820 last%=?O%:O%=O%+1:NEXT:IFPOS:PRINT
  830 =0
  840 :
  850 DEFFN_INFO(A$):IF FNsyn("<fsp>"):=TRUE
  860 A$=FNcl("",0):PROCusb_Sync:A%=FNusb_Info(A$):IFX%!10=0 OR FNusb_Err:=TRUE
  870 PRINTA$;SPC(15-LENA$);FNh0(X%!2,8)" "FNh0(X%!6,8)" "FNh0(X%!10,8)
  880 =0
  890 :
  900 DEFFN_BYE(A$):PROCusb_Cmd("SUD"):PROCusb_Sync:PROCusb_Flush:=0
  910 :
  920 DEFFN_COPY(A$):IF FNsyn("<usb source> <dest>"):=TRUE
  930 src$=FNcl("",0):dst$=FNcl("",0)
  940 IFFNuc(LEFT$(dst$,4))="USB:":dst$=MID$(dst$,5):PROCCopyBBCtoUSB:=0
  950 IFFNuc(LEFT$(src$,4))="USB:":src$=MID$(src$,5)
  960 IFINSTR(dst$,"::")ORLEFT$(dst$,1)="-":PRINT"FS prefix unsupported":=TRUE
  970 PROCCopyUSBtoBBC:=0
  980 :
  990 REM COPY USB: <BBC path>      - copies all from current USB directory
 1000 REM COPY USB:\ <BBC path>     - copies all from USB root directory
 1010 REM COPY USB:<dir> <BBC path> - copies all from USB <dir> directory
 1020 REM COPY USB:<dir>            - copies all from USB <dir> to BBC current directory
 1030 :
 1040 DEFPROCCopyUSBtoBBC:IFsrc$="\":A%=FN_CD("\"):src$=""
 1050 IFsrc$<>"":IFFNusb_Info(src$)=1:PROCCopyOneFile(src$,dst$):ENDPROC
 1060 IFsrc$<>"":IFFN_CD(src$):ENDPROC
 1070 IFdst$<>"":PROCcdir(dst$):dst$=dst$+d$
 1080 PROCCopyDirectory(dst$)
 1090 ENDPROC
 1100 :
 1110 DEFPROCCopyBBCtoUSB:PRINT"BBCtoUSB: not implemented":ENDPROC
 1120 :
 1130 :
 1140 REM File copying code
 1150 REM =================
 1160 :
 1170 DEFPROCCopyDirectory(dst$):LOCAL fn%,n%:PROCusb_RdDir:IFfn%<2:ENDPROC
 1180 FOR n%=0 TO fn%-2:PROCCopyObject:NEXT:ENDPROC
 1190 :
 1200 DEFPROCCopyObject:leaf$=fn$(n%):IFleaf$=". DIR" OR leaf$=".. DIR":ENDPROC
 1210 fname$=LEFT$(FNundos(leaf$),10)
 1220 A%=INSTR(leaf$," "):IFA%=0:A%=FN_INFO(leaf$):PRINTCHR$11;STRING$(42,CHR$9);:PROCCopyOneFile(leaf$,dst$+fname$):ENDPROC
 1230 leaf$=LEFT$(leaf$,A%-1):fname$=LEFT$(fname$,A%-1)
 1240 IFFNfile(dst$+fname$,5)=0:OSCLI"CDir "+dst$+fname$
 1250 A%=FN_CD(leaf$):PROCCopyDirectory(dst$+fname$+d$):A%=FN_CD("..")
 1260 PROCusb_RdDir:ENDPROC
 1270 :
 1280 DEFPROCCopyOneFile(src$,dst$)
 1290 IFdst$="":PRINT"<dest> missing":ENDPROC
 1300 PRINT"Copying "src$" to "dst$"    ";
 1310 load%=X%!2:exec%=X%!6:ln%=X%!10:attr%=X%!14
 1320 IFFNfile(dst$,5):X%!14=&33:A%=FNfile(dst$,4)
 1330 X%!2=load%:X%!6=exec%:X%!10=0:X%!14=ln%:A%=FNfile(dst$,7)
 1340 IFln%:PROCCopyData:REM Copy if length<>0
 1350 PROCSetInfo(dst$):PRINTSTRING$(3,CHR$127):ENDPROC
 1360 :
 1370 DEFPROCCopyData
 1380 PROCusb_Sync:PROCusb_WrStr("RD "+src$+CHR$13)
 1390 out%=OPENOUT(dst$):P%=0:REPEAT
 1400   PROCReadData(P%,ln%):IF(P%AND1023)=0:PRINTSTRING$(3,CHR$8)FNd(100*P%DIVln%,2)"%";
 1410   num%=512:IFP%+num%>ln%:num%=ln%-P%
 1420 PROCgbpb(2,out%,data%,num%,0):P%=P%+512:UNTILP%>ln%
 1430 CLOSE#out%:out%=0:ENDPROC
 1440 :
 1450 DEFPROCSetInfo(dst$):X%!2=load%:X%!6=exec%:X%!10=ln%:X%!14=attr%:A%=FNfile(dst$,1)
 1460 ENDPROC
 1470 :
 1480 :
 1490 REM Object display routines
 1500 REM =======================
 1510 :
 1520 DEFPROCLstDir(cflg%):REM cflg%=0 - CAT, cflg%=1 - EX
 1530 PRINT "Path: "title$":"path$'
 1540 PROCusb_Sync:PROCusb_Cmd("DIR"):IFFNusb_Err:ENDPROC
 1550 x%=0:REPEATREPEATUNTIL(?usbS AND 128)=0
 1560   A%=?usbD:IF A%=13 PRINTSPC(20-(POS MOD 20)); ELSE VDUA%
 1570   IFA%=ASC":":x%=1
 1580 UNTILA%=13 AND x%=1:PRINTSTRING$(20,CHR$127);:IFPOS:PRINT
 1590 ENDPROC
 1600 :
 1610 DEFPROCReadData(ptr%,ext%):LOCAL p%
 1620 IFptr%+512<=ext%:CALL usbRdData:ENDPROC
 1630 REPEAT:data%?p%=USRusbRd:p%=p%+1:UNTILptr%+p%>=ext%ORp%>511
 1640 IFp%<512:REPEATdata%?p%=0:p%=p%+1:UNTILp%>511
 1650 ENDPROC
 1660 :
 1670 :
 1680 REM USB access routines
 1690 REM ===================
 1700 :
 1710 DEFPROCusb_Sync:REPEATA%=?usbD:UNTIL(?usbS AND 128):ENDPROC
 1720 :
 1730 DEFPROCusb_Flush:IF(?usbS AND 128):ENDPROC
 1740 PRINT'"Flushing USB";:REPEAT:REPEATA%=?usbD:UNTIL(?usbS AND 128)
 1750 A%=TIME+20:REPEATUNTILTIME>A%:UNTIL(?usbS AND 128):ENDPROC
 1760 :
 1770 DEFFNusb_Err:IF?result%=13 OR result%?1=ASC":":=0 ELSE PRINT"USB Error: "$result%:=TRUE
 1780 :
 1790 DEFPROCusb_Cmd(A$):PROCusb_WrStr(A$+CHR$13):PROCusb_RdStr:ENDPROC
 1800 :
 1810 DEFFNusb_Size(A$):PROCusb_Cmd("IPH"):PROCusb_Cmd("DIR "+A$)
 1820 REPEATUNTIL(?usbS AND 128)=0
 1830 A%=result%-1:REPEATA%=A%+1:?A%=?usbD:UNTIL(?usbS AND 128):IFA%?-1<>62:=0
 1840 A%=INSTR($result%," "):?result%=13:=result%!A%
 1850 :
 1860 DEFFNusb_Info(A$):LOCAL L%,X$:X%!2=0:X%!6=0:X%!14=&33:X%!10=FNusb_Size(A$)
 1870 IFINSTR(A$,"."OR ?result%<>13:=1
 1880 L%=FNusb_Size(A$+".INF"):IFL%>512 OR ?result%<>13:?result%=13:=1
 1890 PROCusb_WrStr("RD "+A$+".INF"+CHR$13)
 1900 PROCReadData(0,L%):data%?L%=13:A$=$data%:X$=FNcl("",0)
 1910 X$=FNcl("",0):IFX$<>"":X%!2=EVAL("&"+X$):X%!6=X%!2
 1920 X$=FNcl("",0):IFX$<>"":X%!6=EVAL("&"+X$)
 1930 =1
 1940 :
 1950 DEFPROCusb_RdDir:fn%=0:PROCusb_Sync:PROCusb_Cmd("DIR"):IFFNusb_Err:ENDPROC
 1960 REPEAT:REPEATPROCusb_RdStr:fn$(fn%)=$result%:fn%=fn%+1:UNTIL(?usbS AND 128)
 1970 A%=TIME+20:REPEATUNTILTIME>A%:UNTIL(?usbS AND 128):ENDPROC
 1980 :
 1990 DEFPROCusb_WrStr(A$):FOR A%=1 TO LEN A$:REPEATUNTIL(?usbS AND 64)=0
 2000 ?usbD=ASCMID$(A$,A%,1):NEXT:ENDPROC
 2010 :
 2020 DEFPROCusb_RdStr:A%=result%-1:REPEATA%=A%+1:?A%=FNusb_Rd:UNTIL?A%=13:ENDPROC
 2030 :
 2040 DEFFNusb_Rd:REPEATUNTIL(?usbS AND 128)=0:=?usbD
 2050 :
 2060 DEFPROCusb_Init
 2070 usbD=&FCF8:usbS=&FCF9:OSASCI=&FFE3:OSBPUT=&FFD4
 2080 DIM mc% 39:FOR P=0 TO 1:P%=mc%:[OPT P*2
 2090   .usbRdData:LDX #0:.usbRdLp1:JSR usbRd:STA data%,X:INX:BNE usbRdLp1
 2100   .usbRdLp2:JSR usbRd:STA data%+256,X:INX:BNE usbRdLp2:RTS
 2110   .usbRd:BIT usbS:BPL usbRd2:BIT &FF:BPL usbRd
 2120 .usbRd2:LDA usbD:RTS:]NEXT
 2130 ENDPROC
 2140 :
 2150 :
 2160 REM Translate leafname if not saving to DOS
 2170 REM =======================================
 2180 REM $ ^ # . \ ~ @ become < > ? / . \ =
 2190 DEFFNfn_undos(A$):LOCALB%:IF(os%AND-32):=A$
 2200 FORA%=1TOLEN A$:B%=INSTR("#$^&@%~",MID$(A$,A%,1)):IFB%:A$=LEFT$(A$,A%-1)+MID$("?<>+=;\",B%,1)+MID$(A$,A%+1)
 2210 NEXT:=A$
 2220 :
 2230 :
 2240 REM I/O routines
 2250 REM ============
 2260 DEFFNh0(A%,N%):=RIGHT$("00000000"+STR$~A%,N%)
 2270 DEFFNd(A%,N%):=RIGHT$("         "+STR$A%,N%)
 2280 DEFFNd0(A%,N%):=RIGHT$("00000000"+STR$A%,N%)
 2290 DEFFNuc(A$):IFA$="":=""
 2300 FORA%=1TOLENA$:IFMID$(A$,A%,1)>"_":A$=LEFT$(A$,A%-1)+CHR$(ASCMID$(A$,A%,1)AND&5F)+MID$(A$,A%+1)
 2310 NEXT:=A$
 2320 DEFFNfx(A%,X%):LOCAL Y%:Y%=X%DIV256:=(USR&FFF4 AND &FFFF00)DIV256
 2330 :
 2340 REM File routines
 2350 REM =============
 2360 DEFFNfs:LOCAL A%,Y%,E%:=(USR&FFDA)AND&FF
 2370 DEFPROCgbpb(A%,chn%,addr%,num%,ptr%):?X%=chn%
 2380 X%!1=addr%:X%!5=num%:X%!9=ptr%:CALL &FFD1:ENDPROC
 2390 DEFPROCcdir(A$):IFFNfile(A$,5)=0:OSCLI"CDIR "+A$
 2400 ENDPROC
 2410 DEFFNfile(A$,A%):$name%=A$:?X%=name%:X%?1=name%DIV256:=(USR&FFDD)AND&FF
 2420 :
 2430 :
 2520 REM Command Line Parsing
 2530 REM ~~~~~~~~~~~~~~~~~~~~
 2540 DEFFNcl(l$,n%):IFl$="":A$=FNs(A$):IFASCA$=34:A%=INSTR(A$+" "" ",""" ",2):l$=MID$(A$,2,A%-2):A$=FNs(MID$(A$,A%+1)):=l$
 2550 IFl$="":A%=INSTR(A$+" "," "):l$=LEFT$(A$,A%-1):A$=FNs(MID$(A$,A%+1)):=l$
 2560 IFn%=0:IFl$<>"":A%=INSTR(A$,l$):IFA%:A$=FNs(LEFT$(A$,A%-1)+MID$(A$,INSTR(A$," ",A%)+1)):=TRUE
 2570 IFn%=0:IFl$<>"":=FALSE
 2580 A%=INSTR(LEFT$(" ",ASCl$=32)+A$,l$):IFA%=0:=""
 2590 A$=LEFT$(A$,A%-1)+FNs(MID$(A$,INSTR(A$," ",A%)+1))
 2600 IFASCl$=32:l$=MID$(A$,A%):A$=LEFT$(A$,A%-1):=MID$(l$,1-(ASCl$=34),LENl$+2*(ASCl$=34))
 2610 IFASCMID$(A$,A%,1)<>34:l$=MID$(A$,A%,INSTR(A$+" "," ",A%)-A%):A$=LEFT$(A$,A%-1)+MID$(A$,A%+LENl$+1):=l$
 2620 l$=MID$(A$,A%+1,INSTR(A$+""" ",""" ",A%+1)-A%-1):A$=LEFT$(A$,A%-1)+MID$(A$,A%+LENl$+3):=l$
 2630 DEFFNs(A$):IFLEFT$(A$,1)=" ":REPEATA$=MID$(A$,2):UNTILLEFT$(A$,1)<>" "
 2640 IFRIGHT$(A$,1)=" ":REPEATA$=LEFT$(A$,LENA$-1):UNTILRIGHT$(A$,1)<>" "
 2650 =A$