$title ('SDIR - Search') /* Copyright (C) 1981 Digital Research P.O. Box 579 Pacific Grove, CA 93950 Revised: 14 Sept 81 by Danny Horovitz */ dsearch: do; /* search module for extended dir */ /* commonly used macros */ declare dcl literally 'declare', lit literally 'literally', word lit 'address', true literally '1', false literally '0', boolean literally 'byte', cr literally '13', lf literally '10'; /* definitions for assembly interface module */ declare maxb address external, /* addr field of jmp BDOS */ fcb (33) byte external, /* default file control block */ fcb16(16)byte external, tbuff(128)byte external, buff(128)byte external; /* default buffer */ mon1: procedure(f,a) external; declare f byte, a address; end mon1; mon2: procedure(f,a) byte external; declare f byte, a address; end mon2; mon3: procedure(f,a) address external; declare f byte, a address; end mon3; dcl debug boolean external; /* version information */ dcl (os,bdos) byte external, bdos20 lit '20H', bdos30 lit '30H', mpm lit '10H'; dcl first$pass boolean external; dcl get$all$dir$entries boolean external; dcl usr$vector address external; dcl active$usr$vector address external; dcl used$de address public; /* used directory entries */ dcl filesfound address public; /* num files collected in memory */ /* fcb and dma buffer constants */ declare sectorlen lit '128', /* sector length */ f$drvusr lit '0', /* drive and user byte */ f$name lit '1', /* file name */ fnamelen lit '8', /* file name length */ f$type lit '9', /* file type field */ f$typelen lit '3', /* type length */ f$rw lit '9', /* high bit is R/W attribute */ f$dirsys lit '10', /* high bit is dir/sys attribute */ f$arc lit '11', /* high bit is archive attribute */ f$ex lit '12', /* extent */ f$s1 lit '13', /* module byte */ f$rc lit '15', /* record count */ f$diskmap lit '16', /* file disk map */ diskmaplen lit '16', /* disk map length */ f$drvusr2 lit '16', /* fcb2 */ f$name2 lit '17', f$type2 lit '25', f$rrec lit '33', /* random record */ f$rreco lit '35'; /* " " overflow */ declare deleted$type lit '0E5H'; declare /* XFCB */ xfcb$type lit '10h', /* identifier on disk */ xf$passmode lit '12', /* pass word protection mode */ xf$pass lit '16', /* XFCB password */ passlen lit '8', /* password length */ xf$create lit '24', /* creation/access time stamp */ xf$update lit '28'; /* update time stamp */ declare /* directory label: special case of XFCB */ dirlabeltype lit '20h', /* identifier on disk */ dl$password lit '128', /* masks on data byte */ dl$access lit '64', dl$update lit '32', dl$makexfcb lit '16', dl$exists lit '1'; /* search variables */ dcl search$ops address external, /* search options or'd in here */ s$dir lit '1', s$sys lit '2', s$ro lit '4', s$rw lit '8', s$pass lit '16', s$xfcb lit '32', s$nonxfcb lit '64', s$exclude lit '128'; dcl format byte external, form$short lit '0'; dcl max$search$files lit '10', /* files to search for on each pass through */ num$s$files byte external, /* the directory */ search (max$search$files) structure( name(8) byte, type(3) byte, drv byte, anyfile boolean) external; /* logical drive information */ /* function call 32 in 2.0 or later BDOS, returns the address of the disk parameter block for the currently selected disk, which consists of: spt (2 bytes) number of sectors per track blkshf (1 byte) log2 of blocksize (2**blkshf=blksize) blkmsk (1 byte) 2**blkshf-1 extmsk (1 byte) logical/physical extents blkmax (2 bytes) max alloc number dirmax (2 bytes) size of directory-1 dirblk (2 bytes) reservation bits for directory chksiz (2 bytes) size of checksum vector offset (2 bytes) offset for operating system */ dcl dpb$adr address public, /* disk parameter block address */ dpb based dpb$adr structure (spt address, blkshf byte, blkmsk byte, extmsk byte, blkmax address, dirmax address, dirblk address, chksiz address, offset address), bytes$per$block address; /* bytes per block */ /* other globals */ dcl cur$usr byte external, cur$drv byte external, /* current drive " " */ dir$label byte public; /* directory label for BDOS 3.0 */ /* error flags */ /* BDOS calls */ read$char: procedure byte; return mon2 (1,0); end read$char; print: procedure(string$adr) external; dcl string$adr address; end print; print$char: procedure(char) external; dcl char byte; end print$char; pdecimal:procedure(val,prec,zsup) external; dcl (val, prec) address; dcl zsup boolean; end pdecimal; printfn: procedure(fnameadr) external; dcl fnameadr address; end printfn; check$console$status: procedure byte; return mon2 (11,0); end check$console$status; search$first: procedure (fcb$address) byte public; declare fcb$address address; return mon2 (17,fcb$address); end search$first; search$next: procedure byte public; return mon2 (18,0); end search$next; get$dpb: procedure address; /* return base of dpb */ return mon3(31,0); end get$dpb; terminate: procedure external; end terminate; set$vec: procedure(vector,value) external; dcl vector address, value byte; end set$vec; mult23: procedure (f$i$num) address external; dcl f$i$num address; end mult23; /* Utility routines */ crlf: procedure external; /* print carriage return, linefeed */ end crlf; set$drive: procedure public; /* base of disk parm block for the */ dpb$adr = get$dpb; /* currently selected drive */ bytes$per$block = shl(double(1),dpb.blkshf) * sectorlen; end set$drive; break: procedure public; dcl x byte; if check$console$status then do; x = read$char; call terminate; end; end break; number: procedure (char) boolean; dcl char byte; return(char >= '0' and char <= '9'); end number; add3byte: procedure(byte3adr,num) external; dcl (byte3adr,num) address; end add3byte; /* add three byte number to 3 byte accumulater */ add3byte3: procedure(totalb,numb) external; dcl (totalb,numb) address; end add3byte3; /* divide 3 byte value by 8 */ shr3byte: procedure(byte3adr) external; dcl byte3adr address; end shr3byte; add$block: procedure(ak,ab); declare (ak, ab) address; /* add one block to the kilobyte accumulator */ declare kaccum based ak address; /* kilobyte accum */ declare baccum based ab address; /* byte accum */ baccum = baccum + bytes$per$block; do while baccum >= 1024; baccum = baccum - 1024; kaccum = kaccum + 1; end; end add$block; declare buf$fcb$adr address public, /* index into directory buffer */ buf$fcb based buf$fcb$adr (32) byte, /* fcb template for dir */ (f$i$adr, end$adr, last$f$i$adr) address public, /* indices into file$info array */ file$info based f$i$adr structure( usr byte, /* user number */ name (8) byte, type (3) byte, bytes address, /* byte count (mod kilobyte) */ kbytes address, /* kilobyte count */ recs$lword address, /* record count is 3 byte value */ recs$hbyte byte, /* low word, high byte */ hash$link address, /* link for collison */ x$i$adr address), /* index into time stamp array for */ /* this file */ x$i$adr address public, xfcb$info based x$i$adr structure ( create (4) byte, update (4) byte, passmode byte); compare: procedure(length, str1$adr, str2$adr) boolean; dcl (length,i) byte, (str1$adr, str2$adr) address, str1 based str1$adr (1) byte, str2 based str2$adr (1) byte; do i = 0 to length - 1; if ((str1(i) and 7fh) <> (str2(i) and 7fh)) and str2(i) <> '?' then return(false); end; return(true); end compare; match: procedure boolean public; dcl i byte, temp word; if (i := (buf$fcb(f$drvusr) and 0fh)) <> cur$usr then if not get$all$dir$entries then /* Not looking for this user */ return(false); /* and not buffering all other*/ else /* specified user files on */ do; temp = 0; /* this drive. */ call set$vec(.temp,i); if (temp and usr$vector) = 0 then /* Getting all dir entries, */ return(false); /* with user number corresp'g */ end; /* to a bit on in usr$vector */ if usr$vector <> 0 then if i <> 0 and first$pass and usr$vector <> 0 then call set$vec(.active$usr$vector,i); /* build active usr vector for this drive */ do i = 0 to num$s$files - 1; if search(i).drv = 0ffh or search(i).drv = cur$drv then /* match on any drive if 0ffh */ if search(i).anyfile = true then return((search$ops and s$exclude) = 0); else if compare(11,.buf$fcb(f$name),.search(i).name(0)) then return((search$ops and s$exclude) = 0); end; return((search$ops and s$exclude) <> 0); end match; dcl hash$table$size lit '128', /* must be power of 2 */ hash$tablå (hash$table$size© address public at (.memory), /* must be initialized on each*/ hash$entry$adr address, /* disk scan */ hash$entry based hash$entry$adr address; /* where to put a new entry's */ /* address */ hash$look$up: procedure boolean; dcl (i,found,hash$index) byte; hash$index = 0; do i = f$name to f$namelen + f$typelen by 2; hash$index = hash$index + (buf$fcb(i) and 7fh); /* attributes may */ end; /* only be set w/ 1st extent */ hash$index = hash$index + cur$usr; hash$index = hash$index and (hash$table$size - 1); hash$entry$adr = .hash$table(hash$index); f$i$adr = hash$table(hash$index); found = false; do while f$i$adr <> 0 and not found; if file$info.usr = (buf$fcb(f$drvusr) and 0fh) and compare(f$namelen + f$typelen,.file$info.name(0),.buf$fcb(f$name)) then found = true; else do; hash$entry$adr = .file$info.hash$link; /* assuming no '?' */ f$i$adr = file$info.hash$link; /* in file name */ end; end; if f$i$adr = 0 then return(false); else return(true); end hash$look$up; store$file$info: procedure boolean; /* Look for file name of last found fcb or xfcb in fileinfo */ /* array, if not found put name in fileinfo array. Copy other */ /* info to fileinfo or xfcbinfo. The lookup is hash coded with */ /* collisions handled by linking up file$info records through */ /* the hash$link field of the previous file$info record. */ /* The file$info array grows upward in memory and the xfcbinfo */ /* grows downward. */ /* -------------------------<---.memory __ | HASH TABLE | hash = \ of filename -->| root of file$info list|------------>---| func /__ letters | . | | | . | | lower memory ------------------------- | | file$info entry | | -----<--| . | <--------------| (collision) | | . | ------->| . | | . |-------------------->| | last file$info entry | <- last$f$i$adr | |-----------------------| | | | | | | | | unused by dsearch, | | | used by dsort | | | for indices | | | | | | | | |-----------------------| | | last$xfcb entry | <- x$i$adr | | . | | | . | | | . | <-------------------| | first xfcb entry | |-----------------------| | un-usuable memory | <- maxb higher memory ------------------------- */ dcl (i, j) byte, block$num address; if not hash$look$up then /* not in table already */ /* hash$entry is where to put adr of new entry */ do; /* copy to new position in file info array */ if f$i$adr + 2 * size(file$info) > x$i$adr then return(false); /* out of memory */ f$i$adr = (last$f$i$adr := last$f$i$adr + size(file$info)); filesfound = filesfound + 1; call move(f$namelen + f$typelen, .buf$fcb(f$name),.file$info.name); file$info.usr = buf$fcb(f$drvusr) and 0fh; file$info.bytes,file$info.kbytes,file$info.recs$lword, file$info.recs$hbyte, file$info.x$i$adr,file$info.hash$link = 0; hash$entry = f$i$adr; /* save the address of file$info */ end; /* zero totals for the new file */ /* else hash$lookup has set f$i$adr to the file entry already in the */ /* hash table */ /* save xfcb or fcb type info */ if (buf$fcb(f$drvusr) and xfcb$type) <> 0 then do; /* XFCB */ if last$f$i$adr + size(file$info) > x$i$adr - size(xfcb$info) then return(false); /* out of memory */ x$i$adr = x$i$adr - size(xfcb$info); call move(8,.buf$fcb(xf$create),.xfcb$info.create); xfcb$info.passmode = buf$fcb(xf$passmode); file$info.x$i$adr = x$i$adr; end; else /* regular fcb, file$info is already positioned */ do; /* add to number of records */ call add3byte(.file$info.recs$lword, buf$fcb(f$rc) + shl(double(buf$fcb(f$ex) and dpb.extmsk) , 7)); file$info.name(f$arc-1) = file$info.name(f$arc-1) and buf$fcb(f$arc); /* 0 archive bit if it is 0 in any dir entry */ /* count kilobytes */ i = 1; /* 1 or 2 byte block numbers ? */ if dpb.blk$max > 255 then i = 2; do j = f$diskmap to f$diskmap + diskmaplen - 1 by i; block$num = buf$fcb(j); if i = 2 then /* word block numbers */ block$num = block$num or buf$fcb(j+1); if block$num <> 0 then /* allocated */ call add$block(.file$info.kbytes,.file$info.bytes); end; end; return(true); /* success */ end store$file$info; get$files: procedure public; /* with one scan through directory get */ dcl dcnt byte; /* files from currently selected drive */ last$f$i$adr = end$adr; /* last$f$i$adr is the address of the highest file info record */ /* in memory */ do dcnt = 0 to hash$table$size - 1; /* init hash table */ hash$table(dcnt) = 0; end; x$i$adr = maxb; /* top of mem, put xfcb info here */ call set$drive; dir$label,filesfound, used$de = 0; fcb(f$drvusr) = '?'; /* match all dir entries */ dcnt = search$first(.fcb); do while dcnt <> 255; buf$fcb$adr = shl(dcnt and 11b,5)+.buff; /* dcnt mod 4 * 32 */ if buf$fcb(f$drvusr) <> deleted$type then do; used$de = used$de + 1; if buf$fcb(f$drvusr) = dirlabel$type then /* dir label ? */ dir$label = buf$fcb(f$ex); /* save label info */ else if match then do; if not store$file$info then /* store fcb or xfcb info */ do; /* out of space */ call print (.(cr,lf,lf,'Out of Memory',cr,lf,lf,'$')); return; end; end; end; call break; dcnt = search$next; /* to next entry in directory */ end; /* of do while dcnt <> 255 */ end get$files; end dsearch;