NextDosFile

Ever felt the need to scan a DOS directory tree from SBASIC to process a selection of files?

I had good cause to do that recently, as I promised to help a friend bring some kind of order into her 30k+ collection of photos that are spread across numerous media and folders. She would like her photos to be arranged all in one place on an external hard disk, in folders according to year and month. Obviously QPC2 is not going to be used to do the actual copying. My intention is to output an "MSDOS" script with the necessary instructions and leave Windows to do the grunt work. Now, I wont get into the weeds on all that here and now - maybe another time. This part will just be about scanning the various directories for the photos, or whatever. Im sure there are adequate ways of doing it in Windows, but hey, where's the fun in that!

So the demo presented here does not include any filtering or real output, just the list of files. It demonstrates a generalised version of a "DOS" directory tree scanner for the "QL".

How to use

The demo uses a couple of my toolkit commands. If you cant be bothered to load them, then edit them away in the code below. You do need the LONG function, though, or replace it with something else such as Turbo TK's LONGINTEGER or my (slow) S*BASIC version here . Once youve done that, edit the top few lines to suit your own circumstances: Set the DOS anchor drive, dos%, you wish to use to a number between 1 and 8 (the orginal setting is restored after a (successful) run.) Then set the DOS directory you wish to start from, eg root$ = "%homepath%\Pictures" (Yes, %homepath% is a valid specification! - in QPC2, at least.) Next you need to specify the max directory depth, max%, you expect to encounter. (I guess the limit for QPC2 is an unlikely 97 or so.) If you need a "printed" list of the output, set co to the channel you wish it to go to. Then you should be ready to roll! Should the program fall over and fail at any point, remember to close all channels! There could be quite a few open ones. Type CloseAll to reset everything - or just type CLOSE and be done.

Program notes

The whole thing got a bit messier once I added in the extra clauses for SMSQmulator. There are slight differences in the kind of files and folders the different emulators will accept. I havent really had cause to test the code extensively in SMSQmulator yet, so please let me know if you discover any bugs and anomalies!

I left a couple of debug lines in the code (search for Debug) as I thought they were illustrative. These could safely be removed.

Note: Remember that any files and folders in the Windows file system with names longer than 36 characters will simply not show in the SMSQ/E-side directories! The 36 character limit applies to each segment, not the whole directory string, which, in QPC2, can be up to 199 characters long IIRC.


2 EXT_FN 'PROMPT', 'ERRM$':                       rem Optional, for harness.
4 EXT_FN 'LONG':                                  rem Necessary, but alternatives exist
6 EXT_PROC "NFA_USE":   EXT_FN "NFA_USE$":        rem To compile on QPC2
8 EXT_PROC "DOS_DRIVE": EXT_FN "DOS_DRIVE$":      rem To compile on SMSQmulator
10 :
100 rem + ************************************************************************ +
102 rem *<                            Next (DOS) File                             >*
104 rem + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +
106 rem *     Routines to traverse a "DOS" directory as a stream of filenames      *
108 rem *                                                                          *
110 rem * All the directory business is handled inside the routines so it          *
112 rem * appears the the user program as a continuous stream of file names.       *
114 rem * The user program need only check for the filetype, eg directory or       *
116 rem * other, and for EOF, to control the flow, and otherwise get on with       *
118 rem * the business of processing each file as it appears.                      *
120 rem * The first bit of this program is just a demo of capabilities. It         *
122 rem * expects the program to be RUN in the standard 3-window SBASIC console.   *
124 rem *                                                                          *
126 rem * This version is for QPC2 (V5+) and SMSQmulator (V2.3+) on Windows only!  *
128 rem * It will probably not work with Linux as host OS without modification.    *
130 rem + ------------------------------------------------------------------------ +
132 rem * V0.01, pjw, 2022 May 26, Initial, QPC2 only, version                     *
134 rem * V0.02, pjw, 2022 May 27, Added NFA, DosDirInit, channel stack only       *
136 rem + ************************************************************************ +
138 :
140 rem     User-editable variables
142 dos%  = 6:                              rem Dos drive anchor
144 root$ = 'D:\Temp':                      rem Starting position
146 max%  = 30:                             rem Max directory depth
148 slow% =  1:                             rem Slow, <> 1 => Fast
150 :
152 rem     Log file, REM out for no output
154 co = -1:                                rem co < 0 => no log output
156 co = FOP_OVER("ram1_dir_txt"): PRINT#co; root$
158 :
160 rem     Init program variables
162 ERT DosDirInit(max%, dos%, root$):      rem "Hard error" if init fails
164 :
166 rem     Start harness
168 FOR i = 0 TO 2: CLS#i
170 dc = 0: fc = 0: md% = 0:                rem Counters
172 :
174 REPeat main
176  er = NextFile(fnm$, fdt, ftp%)
178  IF er < 0 THEN
180   IF er = -10: er = 0
182   EXIT main
184  END IF
186  :
188  rem    Display
190  IF ftp% = 255 THEN
192   PRINT "> "; fnm$\, DATE$(fdt)
194   IF co >= 0: PRINT#co; "> "; d_dir$
196   dc = dc + 1
198  ELSE
200   PRINT fnm$\, DATE$(fdt)
202   IF co >= 0: PRINT#co; FILL$(" ", d_lev%); fnm$
204   fc = fc + 1
206  END IF
208  :
210  rem    User interaction
212  IF slow% = 1 THEN
214   AT#0; 0, 0: CLS#0; 3
216   slow% = PROMPT(#0; "Next, Fast, Quit ", 'Nn ', 'Ff' & CHR$(10), 'Qq' & CHR$(27))
218   SELect ON slow%
220    = 2: CLS#0: PRINT#0; 'Press any key to pause..'
222    = 3: er = -1: EXIT main
224   END SELect
226  ELSE
228   IF INKEY$(#0; 0) <> '': slow% = 1
230  END IF
232 END REPeat main
234 :
236 rem     Endgame
238 CLS#0
240 IF er THEN
242  PRINT#0; 'Error:'! ERRM$(er); '. Channels closed.'
244  IF co >= 0: PRINT#co; 'Error'! ERRM$(er)
246 ELSE
248  PRINT#0; 'Dirs:'! dc; ', Files:'! fc; ', max depth:'! md%
250  IF co >= 0 THEN
252   PRINT#co; 'Dirs: '; dc; ', Files: '; fc; ', max depth: '; md%
254  END IF
256 END IF
258 CloseAll
260 :
262 :
1000 rem + ------------------------------------------------------------------------ +
1002 rem |<                               Next File                                >|
1004 rem + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +
1006 rem |                  Get the next file in a DOS directory                    |
1008 rem |                                                                          |
1010 rem | The GLOBal variables must have been set up first followed by a call to   |
1012 rem | DosDirInit.                                                              |
1014 rem |                                                                          |
1016 rem | The parameters are return parameters only. They are all altered unless   |
1018 rem | error return.                                                            |
1020 rem |                                                                          |
1022 rem | GLObal d_dir$, d_chn%(), d_lev%; d_dos%, d_dos$, d_qpc                   |
1024 rem | Dependency: LONG (or use Turbo_TK's LONGINTEGER, etc)                    |
1026 rem + ------------------------------------------------------------------------ +
1028 rem | V0.01, pjw, 1gg5, Original Qdos FS scanner                               |
1030 rem | V0.02, pjw, 2022 May 25, -> DOS                                          |
1032 rem | V0.03, pjw, 2022 May 27, Only channel stacked, NFA added                 |
1034 rem + ------------------------------------------------------------------------ +
1036 :
1038 DEFine FuNction NextFile(r.nm$, r.dt, r.tp%)
1040 LOCal lp, ch%, l%, rec$(64)
1042 :
1044 REPeat lp
1046  IF EOF(#d_chn%(d_lev%)) THEN
1048   CLOSE#d_chn%(d_lev%)
1050   IF d_lev% = 0: RETurn -10:                            rem End of tree
1052   :
1054   d_lev% = d_lev% - 1: DosUp:                           rem Up one level
1056   PRINT#2; ''! d_lev%! d_dir$:                         rem ###Debug line
1058   NEXT lp
1060  END IF
1062  :
1064  BGET#d_chn%(d_lev%); rec$(1 TO 64):                    rem Get file record
1066  r.tp% = CODE(rec$(6)):                                 rem Get file type
1068  l% = CODE(rec$(16)):                                   rem Get file name length
1070  IF l% = 0: NEXT lp:                                    rem File not present
1072  r.nm$ = rec$(17 TO 16 + l%):                           rem File name
1074  r.dt = LONG(rec$(53 TO 56)):                           rem Get file date
1076  :
1078  IF r.tp% = 255 THEN :                                  rem If directory:
1080   IF d_lev% >= d_max%: RETurn -18:                      rem Max depth reached
1082   :
1084   IF DosDn(r.nm$) = 0 THEN
1086    d_lev% = d_lev% + 1:                                 rem Go down one level
1088    IF d_lev% > md%: md% = d_lev%:                       rem ###Debug line
1090    PRINT#2; ''! d_lev%! d_dir$:                        rem ###Debug line
1092    ch% = FOP_DIR(d_dos$)
1094    IF ch% < 0: d_lev% = d_lev% - 1: RETurn ch%:         rem Some file problem
1096    d_chn%(d_lev%) = ch%
1098   ELSE
1100    PRINT#2; '-'! d_lev%! d_dir$:                        rem ###Debug line
1102   END IF
1104  END IF
1106  EXIT lp
1108 END REPeat lp
1110 RETurn 0
1112 END DEFine NextFile
1114 :
1116 :
1118 rem + ------------------------------------------------------------------------ +
1120 rem |<                               DosDirInit                               >|
1122 rem + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +
1124 rem |                    Main DosDir system initialisation                     |
1126 rem |                                                                          |
1128 rem | Initialise all GLOBal variables                                          |
1130 rem |                                                                          |
1132 rem | Parameters:                                                              |
1134 rem |   mx% = max folder levels to descend                                     |
1136 rem |   ds% = DOS anchor drive number                                          |
1138 rem |   rt$ = Starting point in DOS folders (root for this scan)               |
1140 rem + ------------------------------------------------------------------------ +
1142 rem | V0.01, pjw, 2022 May 27                                                  |
1144 rem + ------------------------------------------------------------------------ +
1146 :
1148 DEFine FuNction DosDirInit(mx%, ds%, rt$)
1150 LOCal ch%
1152 rem     QPC2 and SMSQmulator only
1154 d_qpc = MACHINE
1156 SELect ON d_qpc
1158  = 20: d_qpc = 0: d_dos$ = DevGet$("NFA"):      rem Get usage name for NFA
1160  = 30: d_qpc = 1: d_dos$ = DevGet$("DOS"):      rem Get usage name for DOS
1162  = REMAINDER : RETurn -19:      rem Unsupported platform
1164 END SELect
1166 d_dos% = ds%:                   rem Anchor device
1168 d_dos$ = d_dos$ & ds% & '_':    rem "dos" name
1170 d_max% = mx%:                   rem Max estd folder depth
1172 :
1174 d_sep$  = '\':                  rem DOS filename seperator
1176 d_bas$ = rt$:                   rem Root = start of tree to descend
1178 d_dir$ = d_bas$:                rem Dynamic dir name
1180 :
1182 d_lev% = 0:                     rem Current level
1184 DIM d_chn%(d_max%):             rem Channel stack
1186 :
1188 IF d_qpc THEN
1190  d_org$ = DOS_DRIVE$(d_dos%):   rem Original Dos designation; to be restored
1192  DOS_DRIVE d_dos%, d_bas$:      rem Set current root
1194 ELSE
1196  d_org$ = NFA_USE$(d_dos%):     rem Original Dos designation; to be restored
1198  NFA_USE d_dos%, d_bas$:        rem Set current root
1200 END IF
1202 ch% = FOP_DIR(d_dos$)
1204 IF ch% >= 0: d_chn%(d_lev%) = ch%:       rem First init
1206 RETurn ch%
1208 END DEFine DosDirInit
1210 :
1212 :
1214 rem + ------------------------------------------------------------------------ +
1216 rem |<                                CloseAll                                >|
1218 rem + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +
1220 rem |          In case of interruption, close all associated channels          |
1222 rem |                                                                          |
1224 rem + ------------------------------------------------------------------------ +
1226 rem | V0.02, pjw, 2022 May 25                                                  |
1228 rem + ------------------------------------------------------------------------ +
1230 :
1232 DEFine PROCedure CloseAll
1234 LOCal l%
1236 FOR l% = d_lev% TO 0 STEP -1: CLOSE#d_chn%(l%)
1238 IF co >= 0: CLOSE#co: co = -1
1240 d_lev% = 0
1242 d_dir$ = d_bas$
1244 IF d_qpc THEN
1246  DOS_DRIVE d_dos%, d_org$:              rem Restore original setting
1248 ELSE
1250  NFA_USE d_dos%, d_org$:                rem Restore original setting
1252 END IF
1254 END DEFine CloseAll
1256 :
1258 :
1260 rem + ------------------------------------------------------------------------ +
1262 rem |<                                 DosDn                                  >|
1264 rem + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +
1266 rem |           Dos directory Down: Descend into given sub directory           |
1268 rem |                                                                          |
1270 rem | GLOBal d_dos%, d_dos$, d_dir$; d_sep$                                    |
1272 rem + ------------------------------------------------------------------------ +
1274 rem | V0.02, pjw, 2022 May 27, QPC2 and NFA version, -> function               |
1276 rem + ------------------------------------------------------------------------ +
1278 :
1280 DEFine FuNction DosDn(dr$)
1282 LOCal er
1284 IF dr$ = '': RETurn -1
1286 IF d_qpc THEN
1288  rem    QPC2 variant
1290  d_dir$ = DOS_DRIVE$(d_dos%):              rem Afirm
1292  IF d_dir$(LEN(d_dir$)) = d_sep$ THEN
1294   DOS_DRIVE d_dos%, d_dir$ & dr$:          rem Test waters
1296  ELSE
1298   DOS_DRIVE d_dos%, d_dir$ & d_sep$ & dr$: rem Test waters
1300  END IF
1302  er = FTEST(d_dos$)
1304  IF er = 0 THEN
1306   d_dir$ = DOS_DRIVE$(d_dos%):             rem It seems ok!
1308  ELSE
1310   DOS_DRIVE d_dos%, d_dir$:                rem Ooops! Revert
1312   BEEP 2000, 200
1314  END IF
1316 ELSE
1318  rem    SMSQmulator variant
1320  d_dir$ = NFA_USE$(d_dos%):                rem Afirm
1322  NFA_USE d_dos%, d_dir$ & dr$:             rem Test waters
1324  er = FTEST(d_dos$)
1326  IF er = 0 THEN
1328   d_dir$ = NFA_USE$(d_dos%):               rem It seems ok!
1330  ELSE
1332   NFA_USE d_dos%, d_dir$:                  rem Ooops! Revert
1334   BEEP 2000, 200
1336  END IF
1338 END IF
1340 RETurn er
1342 END DEFine DosDn
1344 :
1346 rem + ------------------------------------------------------------------------ +
1348 rem |<                                 DosUp                                  >|
1350 rem + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +
1352 rem |                Dos directory Up: Ascend to previous level                |
1354 rem |                                                                          |
1356 rem | GLOBal d_dos%, d_dir$; d_sep$                                            |
1358 rem + ------------------------------------------------------------------------ +
1360 rem | V0.02, pjw, 2022 May 27, QPC2 and NFA version                            |
1362 rem + ------------------------------------------------------------------------ +
1364 :
1366 DEFine PROCedure DosUp
1368 LOCal i%, l%
1370 l% = LEN(d_dir$)
1372 IF l% <= 3: BEEP 2000, 200: RETurn :            rem Top level! (Shouldnt happen?)
1374 IF d_dir$(l%) = d_sep$ THEN
1376  l% = l% - 1: d_dir$ = d_dir$(1 TO l%):         rem For SMSQmulator
1378 END IF
1380 FOR i% = l% TO 3 STEP -1
1382  IF d_dir$(i%) = d_sep$ THEN
1384   d_dir$ = d_dir$(1 TO i% - 1 * (i% > 3))
1386   IF d_qpc THEN
1388    DOS_DRIVE d_dos%, d_dir$
1390   ELSE
1392    NFA_USE d_dos%, d_dir$
1394   END IF
1396   EXIT i%
1398  END IF
1400 END FOR i%
1402 END DEFine DosUp
1404 :
1406 :
1408 rem + ------------------------------------------------------------------------ +
1410 rem |<                                DevGet$                                 >|
1412 rem + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +
1414 rem |                             Get device name                              |
1416 rem |                                                                          |
1418 rem | Given real device name, return Usage. Given Usage, return real device    |
1420 rem | name. If neither found, return nothing.                                  |
1422 rem |                                                                          |
1424 rem | SMSQ/E 3+                                                                |
1426 rem + ------------------------------------------------------------------------ +
1428 rem | V0.06, December 18th 2018 (check length)                                 |
1430 rem | V0.07, pjw, 2020 Dec 18, combined Real and Use                           |
1432 rem + ------------------------------------------------------------------------ +
1434 :
1436 DEFine FuNction DevGet$(dev$)
1438 LOCal gdl, l%, p
1440 p = PEEK_L(!! $48)
1442 REPeat gdl
1444 :
1446  IF PeekStrg$(p + $2A) == dev$ THEN
1448   RETurn PeekStrg$(p + $24)
1450  ELSE
1452   IF PeekStrg$(p + $24) == dev$ THEN
1454    RETurn PeekStrg$(p + $2A)
1456   END IF
1458  END IF
1460 :
1462  p = PEEK_L(p): IF p = 0: EXIT gdl
1464 END REPeat gdl
1466 RETurn '': rem Or  RETurn dev$
1468 END DEFine DevGet$
1470 :
1472 DEFine FuNction PeekStrg$(ad)
1474 l% = PEEK_W(ad)
1476 SELect ON l%: = 1 TO 4: RETurn PEEK$(ad + 2, l%)
1478 RETurn ''
1480 END DEFine PeekStrg$
1482 :
1484 :

  
Generated with sb2htm on 2022 May 29
©pjwitte 2oo1 - 2o22