SBASIC 'ls' command

Anything QL Software or Programming Related.
Post Reply
User avatar
janbredenbeek
Super Gold Card
Posts: 631
Joined: Wed Jan 21, 2015 4:54 pm
Location: Hilversum, The Netherlands

SBASIC 'ls' command

Post by janbredenbeek »

Okay, here it is. Merge this into your BOOT file and you'll have a Unix-like 'ls' command, and even a 'lsr' command to list directories recursively. It lists one file per line, with type, size and date info, and without the subdirectory names in front of each filename (unlike DIR and WSTAT).

The current version requires SMSQ because it uses LGET and DMEDIUM_DRIVE. It can be made to work on native QLs without these commands but I couldn't work out a way to separate the device+dir and file wildcard without it. On JS and earlier it will probably crash the machine because it has more than 10 LOCal variables...

Here is the link: https://github.com/janbredenbeek/QL/blo ... SIC/ls_bas

Have fun,

Jan.


User avatar
pjw
QL Wafer Drive
Posts: 1296
Joined: Fri Jul 11, 2014 8:44 am
Location: Norway
Contact:

Re: SBASIC 'ls' command

Post by pjw »

Nice one, Jan! :) And now for the machine code version.. ;)


Per
dont be happy. worry
- ?
User avatar
janbredenbeek
Super Gold Card
Posts: 631
Joined: Wed Jan 21, 2015 4:54 pm
Location: Hilversum, The Netherlands

Re: SBASIC 'ls' command

Post by janbredenbeek »

pjw wrote:Nice one, Jan! :) And now for the machine code version.. ;)
He he... In the old days I would have done that, but nowadays SBASIC is fast enough. There's also a lot of string manipulation which is awkward to do in MC.
(one thing I still can't do in SBASIC is finding out how many lines will fit into a window, there's afaik still no SB equivalent to SD.CHENQ).

Jan.


User avatar
ql_freak
Gold Card
Posts: 354
Joined: Sun Jan 18, 2015 1:29 am

Re: SBASIC 'ls' command

Post by ql_freak »

I have also written an UNIX style ls program in C68 (an executable program), which can be used with e.g.:

ex"ls",#1;"WIN1_"

ex"ls":REMark alone prints the current DATA_USE directory in a new window

ex"ls";"-?":REMark prints a short help page to a new window

It supports some options, especially "-l" which prints all information from the file header (name, file type, file length, file update date, file backup date, version number and some other, which I can't remember).

You can download it from my English QL download page (it is currently not available on the German QL download page) where you also find some description (what I can remember, have found yet - it is a very old program from 1992).


http://peter-sulzer.bplaced.net
GERMAN! QL-Download page also available in English: GETLINE$() function, UNIX-like "ls" command, improved DIY-Toolkit function EDLINE$ - All with source. AND a good Python 3 Tutorial (German) for Win/UNIX :-)
EmmBee
Trump Card
Posts: 240
Joined: Fri Jan 13, 2012 5:29 pm
Location: Kent

Re: SBASIC 'ls' command

Post by EmmBee »

(one thing I still can't do in SBASIC is finding out how many lines will fit into a window, there's afaik still no SB equivalent to SD.CHENQ).
The Turbo TK Demos file has a function called Basic_ATTR1 which can find the base of window definition blocks. From there, the window height and character height can be obtained. Dividing one by the other will give the number of lines in the window.

There’s a few points I’ve come across. One can be several levels deep in recursion, when the Escape key is pressed. Only one level is aborted, and the program carries on. I’ve found a way to get over this: give lc a negative value, then this can be checked for when returning.
It doesn’t work correctly with the DOS device: it doesn’t recurse into sub-directories. This is because the file type t% should only be one byte at position 5. The correct value can be sliced out with t% = t% && 255. I’ve added in these additions. Here is the code …

Code: Select all

1000 :
1010 REMark A 'ls' procedure to produce a directory listing similar to the Unix 'ls' command
1020 REMark Usage: ls <dirspec> where <dirspec> may be any device+wildcard specification
1030 REMark    or: lsr <dirspec> to list directories recursively
1040 REMark  e.g.: ls win1_, ls win1_DOCS_, lsr _doc etc.
1050 REMark Recognises TKII default data directory and hard subdirectories on V2 devices
1060 REMark Requirements: SMSQ, SBASIC
1070 REMark Bugs: Wildcard match is simple INSTR operation, may need improvement
1080 REMark A global counter lc is used to pause listing. This assumes a fixed window length.
1090 REMark   AFAIK there's no easy way to find this out dynamically from SBASIC :(
1100 :
1110 REMark v1.0 Jan Bredenbeek, 29 January 2017. Licensed under GPL v3.
1120 :
1130 REMark smart_date$ returns either MMM DD YYYY or MMM DD hh:mm depending on how long ago
1140 :
1150 DEFine FuNction smart_date$(d)
1160 LOCal d$,y$,t$
1170   d$=DATE$(d):y$=d$(1 TO 4):t$=d$(13 TO 17):d$=d$(6 TO 12)
1180   IF DATE-d < 365*86400: RETurn d$&t$:ELSE RETurn d$&" "&y$
1190 END DEFine smart_date$
1200 :
1210 REMark Main procedure
1220 :
1230 DEFine PROCedure ls(d$,r$)
1240 LOCal ch,fnr,fl,ud,dh%,dl%,eh%,el%,dev$,dir$,dn$,fn$,wc$,t%,t$,i$,r,ls_lp,dir_printed
1245 LOCal lines
1246       lines = CON_LINES(#1)
1250   wc$=PARSTR$(d$,1)
1260   ch=FOP_DIR(wc$):IF ch<0:ERT ch
1270   REMark make dir$ hold full device+subdir
1280   dev$=DMEDIUM_DRIVE$(#ch)&"_"
1290   dn$=FNAME$(#ch):IF dn$<>"":dn$=dn$&"_":REMark subdir name if hard subdir
1300   dir$=dev$&dn$
1310   REMark make wc$ canonical
1320   IF dn$ INSTR wc$ <> 1 AND dev$ INSTR wc$ <> 1:wc$=DATAD$&wc$
1330   IF dev$ INSTR wc$ <> 1:wc$=DATAD$&wc$
1340   REMark remainder of wc$ after dev+subdir is wildcard
1350   IF LEN(wc$)>LEN(dir$):wc$=wc$(LEN(dir$) TO):ELSE wc$=""
1360   IF wc$<>""
1370     IF wc$(1)="_" THEN IF LEN(wc$)>1:wc$=wc$(2 TO):ELSE wc$=""
1380   END IF
1390   IF PARSTR$(r$,2)<>"":r=PARSTR$(r$,2):ELSE r=0:REMark recursive depth
1400   REMark for debugging:  PRINT "dn=";dn$;" wc=";wc$;" r=";PARSTR$(r$,2);
1410   dir_printed=0
1420   fnr=-1:IF r<2:lc=1
1430   REPeat ls_lp
1440     fnr=fnr+1
1450     GET#ch\fnr*64:IF EOF(#ch):EXIT ls_lp
1460     LGET#ch;fl:IF fl=0:NEXT ls_lp:REMark empty dir entry
1470     fl=fl-64:REMark subtract header length
1480     GET#ch;t%,dh%,dl%,eh%,el%,fn$:REMark type(w),datasize(l),extra(l),filename
1485            t% = t% && 255
1490     LGET#ch\fnr*64+52;ud:REMark update date(l)
1500     IF LEN(dn$)>0: fn$=fn$(LEN(dn$)+1 TO):REMark chop off directory name
1510     REMark filter on wildcard unless traversing directories recursively
1520     IF (NOT r OR t%<>255) AND wc$<>"" AND NOT wc$ INSTR fn$: NEXT ls_lp
1530     lc=lc+1
1540     REMark IF lc MOD 40=0:REMark need to change this to real # lines in window
1544     IF lc MOD lines = 0
1550       i$=INKEY$(-1):IF i$=="q" OR i$=CHR$(27):lc=-lc:EXIT ls_lp
1560     END IF
1570     SELect ON t%
1580       =0:t$=" ":REMark normal file
1590       =1:t$="E":REMark executable file
1600       =2:t$="R":REMark relocatable file
1610       =255:
1620         IF NOT r:t$="D":fn$=fn$&" ->":ELSE ls dir$&fn$&"_"&wc$,IDEC$(r+1,1,0):IF lc<0:EXIT ls_lp:ELSE NEXT ls_lp
1630       =REMAINDER :t$="?"
1640     END SELect
1650     IF NOT dir_printed:PRINT dir$;":":dir_printed=1
1660     PRINT t$;IDEC$(fl,11,0);" ";smart_date$(ud);" ";fn$
1670   END REPeat ls_lp
1680   CLOSE#ch
1690 END DEFine ls
1700 :
1710 REMark list directories recursively
1720 :
1730 DEFine PROCedure lsr(d$)
1740   ls PARSTR$(d$,1),"1"
1750 END DEFine lsr
1760 :
1770 REMark -- end of ls --
1780 :
1790 DEFine FuNction CON_LINES(chan%)
1800 LOCal ch_ide,badr,lin,I,base
1810   ch_ide=chan%*2^16+chan%
1820   badr=ALCHP(36)
1830   IF badr<0:RETurn badr
1840   RESTORE 1900
1850   FOR I=0 TO 34 STEP 2:READ lin:POKE_W badr+I,lin
1860   CALL badr+4,ch_ide
1870   base=PEEK_L(badr)
1880   RECHP badr
1890   RETurn PEEK_W(base+30) DIV PEEK_W(base+40)
1900  DATA 0,0,30463,8257,17914,22,28681,20035,17914
1910  DATA -18,19072,27138,8768,28672,9353,20085,8776,20085
1920 END DEFine CON_LINES
The INSTR way of searching is a good alternative, and is probably a better way than with DIR or WSTAT. The smart_date$ is a nice idea. Sometimes an error occurs at line 1500. The use of ERT at line 1260 will stop the program before it fully completes. If there’s more filenames to list than the number of lines on the screen, then the header is not seen. There’s quite a lot of work to do to get this perfect, if that is your intention. I wish you the best in your efforts to improve the code. This could become a very nice program.

Michael


User avatar
janbredenbeek
Super Gold Card
Posts: 631
Joined: Wed Jan 21, 2015 4:54 pm
Location: Hilversum, The Netherlands

Re: SBASIC 'ls' command

Post by janbredenbeek »

EmmBee wrote:
(one thing I still can't do in SBASIC is finding out how many lines will fit into a window, there's afaik still no SB equivalent to SD.CHENQ).
The Turbo TK Demos file has a function called Basic_ATTR1 which can find the base of window definition blocks. From there, the window height and character height can be obtained. Dividing one by the other will give the number of lines in the window.
Well I could do some PEEKing into the system tables and find the channel definition block (both Minerva and SMSQ/E have extended PEEK functions to avoid dependency on absolute addresses).
There’s a few points I’ve come across. One can be several levels deep in recursion, when the Escape key is pressed. Only one level is aborted, and the program carries on. I’ve found a way to get over this: give lc a negative value, then this can be checked for when returning.
It doesn’t work correctly with the DOS device: it doesn’t recurse into sub-directories. This is because the file type t% should only be one byte at position 5. The correct value can be sliced out with t% = t% && 255. I’ve added in these additions. Here is the code …
Thanks, I'll consider it for inclusion.
The INSTR way of searching is a good alternative, and is probably a better way than with DIR or WSTAT. The smart_date$ is a nice idea. Sometimes an error occurs at line 1500. The use of ERT at line 1260 will stop the program before it fully completes. If there’s more filenames to list than the number of lines on the screen, then the header is not seen. There’s quite a lot of work to do to get this perfect, if that is your intention. I wish you the best in your efforts to improve the code. This could become a very nice program.
Thanks. There are a few other things to improve. When you use the DEV device ls might go wrong in determining which part of the given parameter belongs to the device, because DMEDIUM_DRIVE$ returns the name of the real device where the directory is, not 'dev'. One solution would be to replace 'dev' with the real device name in dev$. My desire is to remove all dependencies on SMSQ-specific commands anyway so you can run it on other emulators and even plain QL's (provided at least TK2 and Minerva are present).

Jan.


User avatar
janbredenbeek
Super Gold Card
Posts: 631
Joined: Wed Jan 21, 2015 4:54 pm
Location: Hilversum, The Netherlands

Re: SBASIC 'ls' command

Post by janbredenbeek »

I've updated the code with various improvements and fixes:

- Listing now adjusts to window size and can be aborted by pressing 'Q' or ESC, even when recursing directories;
- Redirection by DEV device is now handled correctly (so long as you don't rename the DEV device itself ;))
- SMSQ is no longer required; it will now also work on native QL with TK2 and Minerva fitted.
- NOTE: On non-V2 drivers which don't support subdirectories, ls will fail because the FNAME$ function stops with 'bad parameter' on directory channels. This can be avoided by adjusting line 1710 in the code (as indicated in the REMarks). I'll probably have to design another machine code call to find out whether a device is V2 or not :(

https://github.com/janbredenbeek/QL/blo ... SIC/ls_bas

Jan.


User avatar
ql_freak
Gold Card
Posts: 354
Joined: Sun Jan 18, 2015 1:29 am

Re: SBASIC 'ls' command

Post by ql_freak »

I have downloaded The Shell (currently "sh112.zip) from Dilwyns download page and have found, that my "ls" program (an executable QDOS/SMSQ program) is working perfectly with the shell :-)

You can start it (inside of "The Shell") with e.g.:

ls

or

ls WIN1_

or

ls WIN1_ -l

or, to get help, with:

ls -?

Of course the redirection is working, if you want to output the help output to a file, use:

ls -? >RAM1_lsHelp_txt

Then load RAM1_lsHelp_txt into your favourite editor.

I'm very astonished that my program, written without knowledge of "The Shell" program, seems to be fully functional in this excellent command line shell. As there is already a ls program suggested (proposed) for the shell, you perhaps should rename my ls program to another name.

IMPORTANT: Of course my "ls" command must be in a directory, which is in one of the search path of the PTH device from Phil Borman or the PROG_USE directory points to the directory, where my ls is stored.


http://peter-sulzer.bplaced.net
GERMAN! QL-Download page also available in English: GETLINE$() function, UNIX-like "ls" command, improved DIY-Toolkit function EDLINE$ - All with source. AND a good Python 3 Tutorial (German) for Win/UNIX :-)
Post Reply