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.
SBASIC 'ls' command
- janbredenbeek
- Super Gold Card
- Posts: 673
- Joined: Wed Jan 21, 2015 4:54 pm
- Location: Hilversum, The Netherlands
- Contact:
Re: SBASIC 'ls' command
Nice one, Jan!
And now for the machine code version.. 


Per
I love long walks, especially when they are taken by people who annoy me.
- Fred Allen
I love long walks, especially when they are taken by people who annoy me.
- Fred Allen
- janbredenbeek
- Super Gold Card
- Posts: 673
- Joined: Wed Jan 21, 2015 4:54 pm
- Location: Hilversum, The Netherlands
- Contact:
Re: SBASIC 'ls' command
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.pjw wrote:Nice one, Jan!And now for the machine code version..
(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.
Re: SBASIC 'ls' command
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).
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
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

Re: SBASIC 'ls' command
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.(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).
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
Michael
- janbredenbeek
- Super Gold Card
- Posts: 673
- Joined: Wed Jan 21, 2015 4:54 pm
- Location: Hilversum, The Netherlands
- Contact:
Re: SBASIC 'ls' command
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).EmmBee wrote: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.(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).
Thanks, I'll consider it for inclusion.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. 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).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.
Jan.
- janbredenbeek
- Super Gold Card
- Posts: 673
- Joined: Wed Jan 21, 2015 4:54 pm
- Location: Hilversum, The Netherlands
- Contact:
Re: SBASIC 'ls' command
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.
- 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.
Re: SBASIC 'ls' command
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.
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
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
