************************************************************************** * * The GNO Shell Project * * Developed by: * Jawaid Bazyar * Tim Meekins * * $Id: hash.asm,v 1.8 1998/12/31 18:29:13 tribby Exp $ * ************************************************************************** * * HASH.ASM * By Tim Meekins & Greg Thompson * Modified by Dave Tribby for GNO 2.0.6 * * Command hashing routines * * Note: text set up for tabs at col 16, 22, 41, 49, 57, 65 * | | | | | | * ^ ^ ^ ^ ^ ^ ************************************************************************** * * Interfaces defined in this file: * * hash jsr with params: (2:num, 4:name) * * dohash subroutine (4:files) * return 4:table * * search subroutine (4:file,4:table,4:paths) * return 4:full_path * * dispose_table subroutine (4:table) * return * * free_files subroutine (4:files) * return * * dir_search subroutine (4:dir,2:dirNum,4:files) * return * * hashpath jsl with no parameters * no returned value * * dispose_hash jsr with no parameters * no returned value * ************************************************************************** mcopy /obj/gno/bin/gsh/hash.mac dummyhash start ; ends up in .root end setcom 60 C1 gequ 11 C2 gequ 13 TAB_MULT gequ 4 ; ; Structure for filenames ; ; struct filenode { ; short dirnum; ; char name[32]; ; filenode *next; ; }; fn_dirNum gequ 0 fn_name gequ fn_dirNum+2 fn_next gequ fn_name+32 fn_size gequ fn_next+4 ; ; Structure for hash table ; ; struct tablenode { ; short dirnum; ; char *name[32]; ; }; tn_dirNum gequ 0 tn_name gequ tn_dirNum+2 tn_size gequ tn_name+32 ************************************************************************** * * Calculate hash value for a filename * ************************************************************************** hash START using hashdata space equ 1 num equ space+2 name equ num+2 end equ name+4 * NOTE: hash should only be called after hashmutex is locked ; ; No local variables; just need to save old Dir Page pointer and set ; up new one to point to parameters. ; tsc phd tcd lda num If this isn't the first time bne hasher through, reuse the value of "h". ; ; First time through for the name: calculate value for "h" ; stz h ldy #0 loop lda [name],y and #$FF beq hasher sta addit+1 Modify "adc #$FFFF" lda h ;left shift 7 xba and #$FF00 lsr a addit adc #$FFFF NOTE: immediate data was modified. phy UDivide (@a,t_size),(@a,@a) sta h ply iny bra loop ; ; "h" has been calculated; now do the rest of the hash function ; hasher lda num ;num*num sta tmp lda #0 ldx #16 mulloop asl a asl tmp bcc nomul clc adc num nomul dex bne mulloop pha ;Acc * C2 asl a asl a sec sbc 1,s (Use top word on stack as temp var) asl a asl a adc 1,s sta 1,s lda num ;num*C1 + (Acc*C2) + h asl a adc num asl a asl a adc 1,s adc h sec sbc num plx (Remove temp var from stack) UDivide (@a,t_size),(@a,@y) ; ; Return the hashed value to the user ; lda space sta end-2 pld tsc clc adc #end-3 tcs tya Final hash value is in accumulator. rts h ds 2 NOTE: h must be a "static" variable. tmp ds 2 END ************************************************************************** * * dohash * ************************************************************************** dohash START using hashdata h equ 1 temp equ h+2 qh equ temp+4 table equ qh+2 space equ table+4 files equ space+3 end equ files+4 ; subroutine (4:files),space tsc sec sbc #space-1 tcs phd tcd lda hash_numexe bne mktsize stz table stz table+2 jmp done ; ; t_size = (TAB_MULT * numexe) - 1 ; [Shift since TAB_MULT is 4, change later if needed] ; mktsize asl a asl a dec a sta t_size ; ; table = (tablenode **)malloc(sizeof(tablenode *) * t_size); ; inc a ;safety precaution asl a asl a pea 0 pha ~NEW sta table stx table+2 ; ; for (i=0; i < t_size; ++i) table[i] = NULL; ; ldy #0 ldx t_size tya clrtbl sta [table],y iny iny sta [table],y iny iny dex bne clrtbl ; ; files = files->next ; mainloop ldy #fn_next lda [files],y tax ldy #fn_next+2 lda [files],y sta files+2 stx files ; ; while (files != NULL) { ; ora files jeq done stz qh ; ; while (table[h = hash(files->name, qh))]) { ++qh; ++colls; } ; hashloop pei (files+2) lda files inc a inc a pha pei (qh) jsr hash asl a asl a sta h tay lda [table],y tax iny2 ora [table],y beq gotit ; If it's the same name, skip the duplicate entry ldy h Calculate address clc of hash entry's lda [table],y name field. adc #tn_name tax iny2 lda [table],y adc #0 pha High-order word of address. phx Low-order word of address. pei (files+2) lda files inc a inc a pha jsr cmpcstr beq mainloop inc qh bra hashloop ; ; table[h] = (tablenode *)malloc(sizeof(tablenode)) ; gotit ph4 #tn_size ~NEW sta temp stx temp+2 ldy h sta [table],y iny iny txa sta [table],y ; ; table[h]->dirnum = files->dirNum ; lda [files] sta [temp] ; ; strcpy(table[h]->name, files->name); ; pei (files+2) lda files inc a inc a pha pei (temp+2) lda temp inc a inc a pha jsr copycstr jmp mainloop done anop ; return 4:table ldx table+2 ldy table lda space sta end-3 lda space+1 sta end-2 pld tsc clc adc #end-4 tcs tya rtl END ************************************************************************** * * Search the hash table * ************************************************************************** search START using hashdata ptr equ 1 name_len equ ptr+4 full_path equ name_len+2 qh equ full_path+4 space equ qh+2 paths equ space+3 table equ paths+4 file equ table+4 end equ file+4 ; subroutine (4:file,4:table,4:paths),space tsc sec sbc #space-1 tcs phd tcd lock hashmutex stz qh stz full_path Set result to NULL. stz full_path+2 lda table If hash table hasn't ora table+2 been allocated, jeq done return null string. pei (file+2) pei (file) jsr lowercstr mainloop pei (file+2) Get hash(qh,file) pei (file) pei (qh) jsr hash asl a Multiply by 4 asl a tay Use as index into table. lda [table],y ptr = table[hash(qh,file)] sta ptr tax iny iny ora [table],y If == 0, jeq done all done. lda [table],y sta ptr+2 pei (file+2) pei (file) pha ;ptr+2 inx ;ptr + #2 inx phx jsr cmpcstr Compare filename against entry. beq found inc qh bra mainloop ; ; Found an entry that matches the filename. Calculate full path. ; found lda [ptr] asl a asl a adc paths ;(cf=0) sta ptr ldx paths+2 stx ptr+2 pei (file+2) pei (file) jsr cstrlen Get length of prog name. sta name_len ldy #2 lda [ptr],y pha lda [ptr] pha jsr cstrlen Get length of path. pha sec adc name_len Add name length + 1 (carry bit). pea 0 pha ~NEW Allocate memory, sta full_path storing address at stx full_path+2 functional return value. ldy #2 lda [ptr],y pha lda [ptr] pha pei (full_path+2) pei (full_path) jsr copycstr Copy pathname into buffer. pla ;length of path pei (file+2) pei (file) pei (full_path+2) clc adc full_path pha jsr copycstr Put filename at end of pathname. done unlock hashmutex ldx full_path+2 Load return value into Y- & X- regs ldy full_path ; Adjust stack in preparation for return lda space sta end-3 lda space+1 sta end-2 pld tsc clc adc #end-4 tcs tya A- & X- regs contain ptr (or NULL) rtl END ************************************************************************** * * Dispose the hash table * ************************************************************************** dispose_table START using hashdata ptr equ 0 count equ ptr+4 space equ count+2 subroutine (4:table),space mv4 table,ptr mv2 t_size,count loop ldy #2 lda [ptr],y pha lda [ptr] pha jsl nullfree add2 ptr,#4,ptr dec count bne loop pei (table+2) pei (table) jsl nullfree return END ************************************************************************** * * Dispose the file table * ************************************************************************** free_files START space equ 0 subroutine (4:files),space loop ora2 files,files+2,@a beq done ldy #fn_next lda [files],y tax ldy #fn_next+2 lda [files],y pei (files+2) pei (files) stx files sta files+2 jsl nullfree bra loop done return END ************************************************************************** * * Directory search * ************************************************************************** dir_search START using hashdata temp2 equ 0 temp equ temp2+4 entry equ temp+4 numEntries equ entry+2 ptr equ numEntries+2 space equ ptr+4 subroutine (4:dir,2:dirNum,4:files),space * NOTE: dir_search is only called from hashpath (after hashmutex locked) ; ; Open directory name passed as 1st parameter ; ld2 3,ORec pei (dir+2) Turn "dir" c-string into pei (dir) a GS/OS string, allocated jsr c2gsstr via ~NEW. sta ORecPath stx ORecPath+2 phx Put GS/OS string addr on stack pha so it can be deallocated. Open ORec Open that file. bcc goodopen If there was an error, jsl nullfree Free the GS/OS string jmp exit and exit. goodopen jsl nullfree Free the GS/OS string. ; ; Set up parameter block for GetDirEntry ; mv2 ORecRef,DRecRef Copy the file ref num from open. stz DRecBase Zero the base and stz DRecDisp displacement. GetDirEntry DRec Make DirEntry call. mv2 DRecEntry,numEntries Save number of entries. ld2 1,(DRecBase,DRecDisp) stz entry # processed entries = 0. ; ; Process each entry in the directory ; loop lda entry If number of processed entries cmp numEntries equals the total number, jge done we are all done. ; ; Get directory entry's information ; GetDirEntry DRec ; Check for filetype $B3: GS/OS Application (S16) if2 DRecFileType,eq,#$B3,goodfile ; Check for filetype $B5: GS/OS Shell Application (EXE) if2 @a,eq,#$B5,goodfile ; Check for filetype $B0, subtype $0006: Shell command file (EXEC) cmp #$B0 bne nextfile lda DRecAuxType cmp #$06 bne nextfile lda DRecAuxType+2 bne nextfile ; ; This directory entry points to an executable file. ; Included it in the file list. ; goodfile inc hash_numexe Bump the (global) # files. ldx TempRBlen Get length word from GS/OS string short a stz TempRBname,x Store terminating null byte. long a ph4 #TempRBname jsr lowercstr Convert name to lower case. ph4 #TempRBname Push src addr for copycstr. ldy #fn_next temp = files->next. lda [files],y sta temp ldy #fn_next+2 lda [files],y sta temp+2 ph4 #fn_size temp2 = new entry. ~NEW sta temp2 stx temp2+2 ldy #fn_next files->next = temp2 sta [files],y ldy #fn_next+2 txa pha sta [files],y lda temp2 clc adc #fn_name pha jsr copycstr Copy string into entry. lda dirNum temp2->dirnum = dirNum sta [temp2] ldy #fn_next temp2->next = temp lda temp sta [temp2],y ldy #fn_next+2 lda temp+2 sta [temp2],y nextfile inc entry Bump entry number jmp loop and stay in the loop. ; ; Done adding entries to the hash table from this directory ; done anop ld2 1,ORec ORec.pCount = 1 Close ORec exit return ; Parameter block for GS/OS Open and Close calls ORec dc i'3' pCount (3 for Open, 1 for Close) ORecRef ds 2 refNum ORecPath ds 4 pathname (result buf) ORecAccess dc i'1' requested access = read ; Parameter block for GS/OS GetDirEntry call DRec dc i'13' pCount DRecRef ds 2 refNum DRecFlag ds 2 flags: extended/not DRecBase dc i'0' base: displacement is absolute entry # DRecDisp dc i'0' displacement: get tot # active entries DRecName dc i4'TempResultBuf' name: result buf DRecEntry ds 2 entryNum: entry # whose info is rtrned DRecFileType ds 2 fileType DRecEOF ds 4 eof: # bytes in data fork DRecBlockCnt ds 4 blockCount: # blocks in data fork DRecCreate ds 8 createDateTime DRecMod ds 8 modDateTime DRecAccess ds 2 access attribute DRecAuxType ds 4 auxType ; GS/OS result buffer for getting a directory entry's name TempResultBuf dc i2'68' Total length = 64 bytes + 4 for length TempRBlen ds 2 Value's length returned here TempRBname ds 64 Allow 64 bytes for returned name ds 1 Extra byte for null string termination END ************************************************************************** * * Hash the path variable * ************************************************************************** hashpath START using hashdata using vardata using global len equ 1 pathnum equ len+2 ptr equ pathnum+2 files equ ptr+4 pathptr equ files+4 qflag equ pathptr+4 qptr equ qflag+2 gsosbuf equ qptr+4 space equ gsosbuf+4 end equ space+3 ; ; Allocate space on stack for direct page variables ; tsc sec sbc #space-1 tcs phd tcd lock hashmutex ; ; Allocate special file node ; ph4 #fn_size ~NEW sta hash_files sta files stx hash_files+2 stx files+2 ; ; Initialize counters and pointers ; lda #0 sta hash_numexe sta pathnum ldy #fn_next sta [files],y ldy #fn_next+2 sta [files],y ldy #fn_name sta [files],y sta [files] ; ; Get value of $PATH environment variable string ; ph4 #pathname jsl getenv sta gsosbuf Save address of allocated buffer. stx gsosbuf+2 ora gsosbuf+2 If null, bne setptr ldx #^nopatherr print error message lda #nopatherr jsr errputs jmp noprint and exit. setptr clc Add 4 bytes to lda gsosbuf direct page pointer adc #4 to get the addr of sta pathptr beginning of text. lda gsosbuf+2 adc #0 sta pathptr+2 ; ; Begin parsing $PATH ; loop lda [pathptr] and #$FF jeq pathdone ; ; parse next pathname ; stz qflag Clear quote flag for this path mv4 pathptr,ptr ldy #0 despace lda [pathptr],y and #$FF beq gotspace0 if2 @a,eq,#' ',gotspace1 if2 @a,eq,#009,gotspace1 if2 @a,eq,#013,gotspace1 if2 @a,eq,#'\',gotquote iny bra despace ; Found "\" gotquote anop iny2 ldx qflag If quote flag hasn't already been set, bne despace sty qflag set it to index of first "\" + 2. bra despace ; Found null byte gotspace0 tyx Why put Y-reg in X??? bra gotspace3 ; Found " ", tab, or creturn gotspace1 tyx Why put Y-reg in X??? short a lda #0 sta [pathptr],y long a gotspace2 iny lda [pathptr],y and #$FF if2 @a,eq,#' ',gotspace2 if2 @a,eq,#009,gotspace2 if2 @a,eq,#013,gotspace2 gotspace3 anop clc Bump pathptr by tya the number of bytes adc pathptr indicated in Y-reg. sta pathptr lda pathptr+2 adc #0 sta pathptr+2 lda pathnum cmp #32*4 bcc numok ldx #^toomanyerr lda #toomanyerr jsr errputs jmp pathdone ; ; Convert c string to GS/OS string (allocating space for it) ; numok pei (ptr+2) pei (ptr) jsr c2gsstr phx Push allocated address onto pha stack for later deallocation. sta EPinputPath Save address in ExpandPath stx EPinputPath+2 parameter block. ; ; If any quoted characters were included, the "\" chars must be removed ; ldy qflag Get quote flag (index to "\" char). beq xpandit If no "\", go ahead with expansion. sta qptr Save EPinputPath pointer in stx qptr+2 direct page variable. lda [qptr] Store length + 2 (since we're indexing inc2 a from before length word) in qflag. sta qflag tyx X = index of 1st overwritten "\". short a Use 1-byte accumulator ; ; Copy characters toward front of string, removing "\" chars ; chkloop2 lda [qptr],y Get next character. cmp #'\' If it's a quote, bne storeit lda [qptr] Decrement length. dec a sta [qptr] iny Skip over "\". lda [qptr],y Get character following. storeit phy Push source index onto stack txy so destination index can be sta [qptr],y used to store the character. ply Restore the source index. inx Bump destination and iny source index registers. cpy qflag If source index < length, bcc chkloop2 stay in copy loop. long a Restore long accumulator. ; ; Convert the input pathname into the corresponding full pathname with ; colons as separators. Use temp result buf this time, just to get length. ; xpandit anop ld4 TempResultBuf,EPoutputPath ExpandPath EPParm ; ; Allocate memory for ExpandPath GS/OS result string ; lda TempRBlen Get length of value. inc2 a Add 4 bytes for result buf len words. inc2 a sta len Save result buf len. inc a Add 1 more for terminator. pea 0 pha ~NEW Request the memory. sta EPoutputPath Store address in ReadVariable stx EPoutputPath+2 parameter block and sta ptr direct page pointer. stx ptr+2 ora ptr+2 If address == NULL, jeq donext get next entry. lda len Store result buffer length sta [ptr] at beginning of buf. ExpandPath EPParm Call again, and get the name. bcc epok ldx #^eperrstr Print error message: lda #eperrstr "Invalid pathname syntax." jsr errputs donext jsl nullfree Free GS/OS string (pushed earlier). jmp next Get the next one. epok anop jsl nullfree Free source string (addr on stack) ldy #2 lda [ptr],y Get length of text sta len tay iny4 and add four. short a lda #0 Store 0 at end of text so it sta [ptr],y can act like a C string. long a ; ; Move text in GS/OS result buffer to beginning of buffer ; (overwritting the two length words). ; clc Source is result lda ptr buffer plus adc #4 four bytes. tay lda ptr+2 adc #0 pha phy pei (ptr+2) Destination is first pei (ptr) byte of buffer. jsr copycstr lda ptr ldy pathnum sta hash_paths,y Store address of this lda ptr+2 path's address in the sta hash_paths+2,y hash path table. ldy len beq bumppnum dey lda [ptr],y If last character and #$FF of path name cmp #':' isn't ":", beq bumppnum iny lda #':' store ":\0" sta [ptr],y at end of string. bumppnum anop add2 pathnum,#4,pathnum Bump path pointer. next jmp loop Stay in loop. ; ; The $PATH entries have been created. Now we need to search each of the ; directories for executable files and add them to the "files" list. ; The earliest versions of gsh added files to the list in the order that ; directories appeared in $PATH, which put the earliest directories' files ; at the end of the list. Check for existence of $OLDPATHMODE environment ; variable to see if the user wants this, or would rather have them hashed ; in the expected order. ; pathdone anop lda varoldpmode beq neworder ; ; Search directories and add executables to file list starting at the ; beginning of $PATH and working to the end. ; stz pathnum Start at beginning of path table. nextpath1 ldy pathnum Get offset into hash table. cpy #32*4 bcs filesdone lda hash_paths,y If address of this path ora hash_paths+2,y has not been set, beq filesdone all done. lda hash_paths,y Get address of this ldx hash_paths+2,y path's address in the phx hash path table. pha tya Directory number = lsr2 a offset / 4 pha pei (files+2) Pointer to file list. pei (files) jsl dir_search Add executables from this directory. add2 pathnum,#4,pathnum Bump path offset. bra nextpath1 ; ; Search directories and add executables to file list starting at end ; of $PATH and working back to the beginning. (Note: Loop begins at ; "neworder", but structuring the code this ways saves an instruction.) ; nextpath2 dey4 Decrement path offset. sty pathnum lda hash_paths,y Get address of this ldx hash_paths+2,y path's address in the phx hash path table. pha tya Directory number = lsr2 a offset / 4 pha pei (files+2) Pointer to file list. pei (files) jsl dir_search Add executables from this directory. neworder ldy pathnum Get offset into hash table. bne nextpath2 When == 0, no more to do. ; ; Executable files in $PATH have been added to the list. Print ; number of files, then build the hash table. ; filesdone anop ph4 gsosbuf Free memory allocated for jsl nullfree $PATH string. lda done_init If initialization isn't complete, beq noprint don't print the # of files. Int2Dec (hash_numexe,#hashnum,#3,#0) ldx #^hashmsg lda #hashmsg jsr puts noprint anop ; ; Create the hash table from the file list. ; ph4 hash_files jsl dohash sta hash_table stx hash_table+2 unlock hashmutex pld tsc clc adc #end-4 tcs rtl pathname gsstr 'path' hashmsg dc c'hashed ' hashnum dc c'000 files',h'0d00' ; Parameter block for GS/OS call ExpandPath EPParm dc i'2' pCount = 2 EPinputPath ds 4 ptr to inputPath (GS/OS string) EPoutputPath ds 4 ptr to outputPath (Result buffer) ; GS/OS result buffer for getting the full length of expanded name TempResultBuf dc i2'5' Only five bytes total. TempRBlen ds 2 Value's length returned here. ds 1 Only 1 byte for value. eperrstr dc c'rehash: Invalid pathname syntax.',h'0d00' toomanyerr dc c'rehash: Too many paths specified.',h'0d00' nopatherr dc c'rehash: PATH string is not set.',h'0d00' END ************************************************************************** * * Dispose of hashing tables * ************************************************************************** dispose_hash START using hashdata ora2 hash_table,hash_table+2,@a beq done ldx #32 32 different paths, maximum ldy #0 Start looking at the first entry. loop1 phx Save path counter phy and index. lda hash_paths+2,y Put address for this pha path table entry on lda hash_paths,y the stack. pha lda #0 Zero out the table entry. sta hash_paths+2,y sta hash_paths,y jsl nullfree Free the entry's memory. next1 ply Restore path index plx and counter. iny4 Bump pointer to next address. dex If more paths to process, bne loop1 stay in the loop. ph4 hash_files jsl free_files stz hash_files stz hash_files+2 ph4 hash_table jsl dispose_table stz hash_table stz hash_table+2 done rts END ************************************************************************** * * Hash data * ************************************************************************** hashdata DATA hashmutex key Mutual exclusion key t_size ds 2 t_size = (TAB_MULT * numexe) - 1 hash_paths dc 32i4'0' 32 paths max for now. hash_files dc i4'0' hash_table dc i4'0' Pointer to table (t_size entries) hash_numexe dc i2'0' Number of hashed executables END