NAME ccscmd ; File CCSCMD.ASM ;CHINESE ifdef MSDOS include msscmd.dat else include ccscmd.dat endif code segment public 'code' extrn ctlu:near, cmblnk:near, clearl:near, locate:near, takrd:near extrn takclos:near, docom:near assume cs:code, ds:datas, es:nothing ; This routine parses the specified function in AH. Any additional ; information is in DX and BX. ; Returns rskp on success and ret on failure COMND PROC NEAR mov cmdstk,sp ; save stack ptr for longjmp exit mov noparse,0 ; recognize semicolons in Take files cmp ah,cmcfm ; Parse a confirm? jne cm2 ; nz = no jmp cmcfrm ; get a confirm cm2: cmp ah,cmkey ; Parse a keyword? jne cm3 jmp cmkeyw ; Try and get one cm3: cmp ah,cmtxt ; Parse arbitrary text jne cm4 jmp cmtext cm4: cmp ah,cmfile ; parse text surrounded by whitespace jne cm5 jmp cmfil0 cm5: mov ah,prstr ; Else give error ; mov dx,offset cmer00 ; "?Program internal error" mcmsg cmer00,ccmer00 int dos jmp prserr COMND ENDP ; This routine parses a keyword from the table pointed at by DX, help text ; point to by BX. Format of the table is as follows (use macro mkeyw): ; addr: db N ; Where N is the # of entries in the table ; db M ; M is the size of the keyword (excl '$') ; db 'string$' ; String is the keyword ; dw value ; Value is data to be returned ; Keywords may be in any order and in mixed case. ; Return is rskp for success and ret for failure. ; comand.cmptab: pointer to keyword table (supplied by caller) ; comand.cmhlp: pointer to help message (supplied by caller) ; comand.cmsptr: pointer to current user word text ; comand.cmsiz: length of user text, excluding terminator ; comand.cmcr: 0 = empty lines not allowed, 1 = empty lines allowed ; comand.cmwhite: non-zero allows leading whitespace for cmtxt and cmfile, ; reset automatically at end of call ; comand.cmwptr: buffer write pointer to next free byte ; comand.cmrptr: buffer read pointer for next free byte ; comand.cmper: 0 to do \%x substitution. Set to 0 at end of call ; comand.impdo: non-zero permits keyword failure to retry as DO command, reset ; automatically at time of failure. cmkeyw proc near mov comand.cmsiz,0 ; user word length mov ax,comand.cmrptr ; get command reading pointer mov comand.cmsptr,ax ; set pointer for start of user word mov comand.cmhlp,bx ; save the help pointer mov comand.cmptab,dx ; save the beginning of keyword table mov bx,dx cmp byte ptr[bx],0 ; get number of entries in table jne cmky1 jmp cmky7 ; e = no keywords to check, error cmky1: mov comand.cmsflg,0ffh ; skip leading spaces/tabs call cmgtch ; get char from the user into ah jc cmky3 ; c = terminator mov dx,comand.cmrptr ; next byte to read dec dx ; where we just read a char mov comand.cmsptr,dx ; remember start of keyword inc comand.cmsiz ; start counting user chars cmky2: call cmgtch ; read until terminator jc cmky3 ; c = terminator inc comand.cmsiz ; count user chars jmp short cmky2 ; no terminator yet cmky3: cmp ah,'?' ; need help? jne cmky4 ; ne = no jmp cmkyhlp ; do help and exit cmky4: cmp ah,escape ; escape? jne cmky6 ; ne = no call cmkyesc ; process escape jc cmky5 ; c = failure (no unique keyword yet) mov comand.cmper,0 ; reset to variable recognition mov comand.cmkeep,0 mov comand.impdo,0 ; clear flag to prevent loops mov comand.cmquiet,0 ; permit echoing again jmp rskp ; return successfully to user cmky5: cmp comand.cmsiz,0 ; started a word yet? je cmky1 ; e = no, ignore escape, keep looking jmp cmkyhlp ; ne = yes, show type of error cmky6: cmp comand.cmsiz,0 ; length of user's text, empty? je cmky7 ; e = yes, parse error push bx mov bx,comand.cmsptr ; point at first user character cmp byte ptr[bx],':' ; start of a label? pop bx jne cmky6a ; ne = no, return success mov comand.cmsiz,1 ; say just one byte cmky6a: call getkw ; get unique kw, point to it with bx jc cmky8 ; c = not found add bl,[bx] ; add length of keyword text (CNT) adc bh,0 add bx,2 ; point at value field mov bx,[bx] ; bx = return value following keyword mov comand.cmper,0 ; reset to variable recognition mov comand.cmkeep,0 mov comand.impdo,0 ; clear flag to prevent loops mov comand.cmquiet,0 ; permit echoing again mov errflag,0 jmp rskp ; return successfully ; all other terminators come here cmky7: cmp comand.cmsiz,0 ; empty table or empty user's text? jne cmky8 ; ne = no cmp comand.cmcr,0 ; empty lines allowed? jne cmky10 ; ne = yes, do not complain push dx mov ah,prstr ; mov dx,offset cmer01 ; command word expected mcmsg cmer01,ccmer01 int dos pop dx mov comand.cmquiet,0 ; permit echoing again mov comand.impdo,0 ; clear flag to prevent loops ret ; do ret exit cmky8: cmp comand.impdo,0 ; failed here, ok to try Macro table? je cmky8a ; e = no, use regular exit path mov comand.impdo,0 ; yes, but clear flag to prevent loops mov comand.cmrptr,offset comand.cmdbuf ; reinit read pointer mov comand.cmquiet,1 ; suppress echoing of same keyword mov bx,offset docom ; return DO as "found" keyword jmp rskp ; return success to invoke DO cmky8a: mov comand.cmquiet,0 ; permit echoing again mov errflag,1 ; say already doing error recovery or kstatus,1 ; global command status, failure call isdev ; reading pretyped lines? jnc cmky9 ; nc = yes, consume rest of line cmp taklev,0 ; in a Take file? jne cmky9 ; ne = yes call cmskw ; display offending keyword dec comand.cmrptr ; interactive, backup to terminator mov bx,comand.cmrptr ; look at it cmp byte ptr [bx],' ' ; got here on space terminator? jne cmky10 ; ne = no, (cr,lf,ff) exit failure mov ah,prstr ; start a fresh line mov dx,offset crlf int dos call bufreset ; cut back buffer to just before term jmp repars ; reparse interactive lines cmky9: call cmcfrm ; get formal end of command line nop ; to maintain illusion of typeahead nop ; and let user backspace to correct nop ; mistakes (we reparse everything) call cmskw ; display offending keyword cmky10: ret ; do ret exit cmkeyw endp ;;;;;; start support routines for keyword parsing. cmkyesc proc near ; deal with escape terminator call bufreset ; reset buffer to end just before ESC cmp comand.cmsiz,0 ; user word length, empty? jne cmkye2 ; ne = have user text, else complain cmkye1: call esceoc ; do normal escape end-of-command stc ; say failure to fill out word ret ; add unique keyword to buffer cmkye2: call getkw ; is there a matching keyword? jc cmkye1 ; c = ambiguous or not found push bx ; unique, bx points to structure push si mov cl,[bx] ; length of keyword mov ch,0 inc bx ; point to first letter mov si,comand.cmwptr ; where next char goes mov dx,comand.cmsiz ; length of user word add bx,dx ; add chars known so far sub cx,dx ; calculate number yet to add add comand.cmsiz,cx jcxz cmkye4 ; z = none mov ah,conout ; display new char cmkye3: mov al,[bx] ; get a keyword letter inc bx call tolowr ; lowercase mov [si],al ; store it inc si mov dl,al int dos ; display it loop cmkye3 ; do all new chars cmkye4: mov byte ptr[si],' ' ; insert space terminator in buffer mov dl,' ' ; display it mov ah,conout int dos inc si mov comand.cmrptr,si ; move token pointer after the space mov comand.cmwptr,si ; next free slot is after the space mov comand.cmsflg,0ffh ; set space-seen flag pop si pop bx ; bx = keyword structure add bl,[bx] ; add length of keyword text adc bh,0 add bx,2 ; point at value field mov bx,[bx] ; bx = return value following keyword clc ; carry clear for success ret cmkyesc endp esceoc proc near ; do normal escape end-of-command push ax push dx mov ah,conout ; ring the bell mov dl,bell int dos pop dx pop ax call bufreset ; reset buffer stc ; say error condition ret esceoc endp ; Help. Question mark entered by user. Display all the keywords that match ; user text. If text is null then use external help if available; otherwise, ; display all keywords in the table. Removes question mark from buffer and ; invokes reparse of command line to-date. User word starts at .cmsptr and ; is .cmsiz bytes long. cmkyhlp proc near mov cx,0 ; clear number of keyword (none yet) cmp comand.cmsiz,0 ; user text given? jne cmkyh1 ; ne = yes, use matching keywords cmp comand.cmhlp,0 ; external help given? jne cmkyh6 ; yes, use it instead of full table cmkyh1: mov temp,0 ; count # chars printed on this line mov bx,comand.cmptab ; beginning of kw table mov ch,[bx] ; length of table mov cl,0 ; no keywords or help displayed yet inc bx ; point at CNT field cmkyh2: cmp comand.cmsiz,0 ; length of user word je cmkyh3 ; e = null, use full table call cmpwrd ; compare keyword with user word jc cmkyh5 ; c = no match, get another keyword cmkyh3: mov al,[bx] ; length of table keyword add byte ptr temp,al ; count chars printed so far cmp temp,76 ; will this take us beyond column 78? jbe cmkyh4 ; be = no, line has more room mov ah,prstr mov dx,offset crlf ; break the line int dos mov temp,0 ; and reset the count cmkyh4: cmp cl,0 ; any keywords found yet? jne cmkyh4a ; ne = yes ; mov dx,offset cmin01 ; start with One of the following: msg mcmsg cmin01,ccmin01 mov ah,prstr int dos inc cl ; say one keyword has been found cmkyh4a:mov dl,spc ; put two spaces before each keyword mov ah,conout int dos int dos add temp,2 ; count output chars mov dx,bx ; get current keyword structure inc dx ; text part mov ah,prstr int dos ; display it cmkyh5: dec ch ; are we at end of table? jle cmkyh7 ; le = yes, quit now add bl,[bx] ; next keyword, add CNT chars to bx adc bh,0 add bx,4 ; skip CNT, '$' and 16 bit value jmp cmkyh2 ; go examine this keyword cmkyh6: mov dx,comand.cmhlp ; external help text mov ah,prstr int dos inc cl ; say gave help already cmkyh7: cmp cl,0 ; found any keywords? jne cmkyh9 ; ne = yes mov cx,comand.cmsiz ; length of word cmp cx,0 jg cmkyh8 ; g = something to show push dx mov ah,prstr ; mov dx,offset cmer01 ; command word expected mcmsg cmer01,ccmer01 int dos pop dx jmp prserr cmkyh8: mov kwstat,0 ; set keyword not-found status call cmskw ; display offending keyword cmkyh9: mov ah,prstr ; start a fresh line mov dx,offset crlf int dos call bufreset ; cut back buffer to just before '?' jmp repars cmkyhlp endp ; See if keyword is ambiguous or not from what the user has typed in. ; Return carry set if word is ambiguous or not found, carry clear otherwise. ; Uses table pointed at by comand.cmptab, user text pointed at by ; comand.cmsptr and length in comand.cmsiz. cmambg proc near push bx push cx push dx mov dl,0 ; count keyword matches so far mov bx,comand.cmptab ; look at start of keyword table mov cl,[bx] ; get number of entries in table mov ch,0 ; use cx as a counter jcxz cmamb8 ; z = no table so always ambiguous inc bx ; look at CNT byte of keyword cmamb4: call cmpwrd ; user vs table words, same? jc cmamb6 ; c = no match inc dl ; count this as a match cmp dl,1 ; more than one match? ja cmamb8 ; a = yes, quit now cmamb6: add bl,[bx] ; add CNT chars to bx adc bh,0 add bx,4 ; skip CNT, '$' and 16 bit value loop cmamb4 ; do rest of keyword table cmamb7: cmp dl,1 ; how many matches were found? jne cmamb8 ; ne = none or more than 1: ambiguous pop dx ; restore main registers pop cx pop bx clc ret ; ret = not ambiguous cmamb8: pop dx ; restore main registers pop cx pop bx stc ret ; return ambiguous or not found cmambg endp ; Compare user text with keyword, abbreviations are considered a match. ; Enter with bx pointing at keyword table CNT field for a keyword. ; Return carry clear if they match, set if they do not match. User text ; pointed at by comand.cmsptr and length is in comand.cmsiz. ; Registers preserved. cmpwrd proc near push cx mov cx,comand.cmsiz ; length of user's text jcxz cmpwrd2 ; z: null user word matches no keyword cmp cl,[bx] ; user's text longer than keyword? ja cmpwrd2 ; a = yes, no match push ax push bx push si inc bx ; point at table's keyword text mov si,comand.cmsptr ; buffer ptr to user input cld cmpwrd1:lodsb ; user text mov ah,[bx] ; keyword text inc bx ; next keyword letter call tolowr ; force lower case on both chars cmp ah,al ; same? loope cmpwrd1 ; e = same so far pop si pop bx pop ax jne cmpwrd2 ; ne = mismatch pop cx ; recover keyword counter clc ; they match ret cmpwrd2:pop cx ; recover keyword counter stc ; they do not match ret cmpwrd endp ; Get pointer to keyword structure using user text. Uses keyword table ; pointed at by comand.cmptab and comand.cmsiz holding length ; of user's keyword (cmpwrd needs comand.cmsptr pointing at user's ; keyword and length of comand.cmsiz). Structure pointer returned in BX. ; Return carry clear for success and carry set for failure. Modifies BX. getkw proc near push cx mov kwstat,0 ; keyword status, set to not-found cmp comand.cmsiz,0 ; length of user word, empty? je getkw3 ; e = yes, fail mov bx,comand.cmptab ; table of keywords mov cl,[bx] ; number of keywords in table mov ch,0 jcxz getkw3 ; z = none, fail inc bx ; point to first getkw1: call cmpwrd ; compare user vs table words jc getkw2 ; c = failed to match word, try next mov kwstat,1 ; say found one keyword, maybe more push dx mov dx,comand.cmsiz ; users word length cmp [bx],dl ; same length (end of keyword)? pop dx je getkw4 ; e = yes, exact match. Done call cmambg ; ambiguous? jnc getkw4 ; nc = unique, done, return with bx mov kwstat,2 ; say more than one such keyword getkw2: add bl,[bx] ; next keyword, add CNT chars to bx adc bh,0 add bx,4 ; skip CNT, '$' and 16 bit value loop getkw1 ; do all, exhaustion = failure getkw3: pop cx stc ; return failure ret getkw4: pop cx clc ; return success ret getkw endp ; show offending keyword message. Comand.cmsptr points to user word, ; comand.cmsiz has length. Modifies AX, CX, and DX. cmskw proc near mov ah,prstr ; not one of the above terminators ; mov dx,offset cmer02 ; '?Word "' mcmsg cmer02,ccmer02 int dos mov cx,comand.cmsiz ; length of word jcxz cmskw3 ; z = null mov ah,conout push si mov si,comand.cmsptr ; point to word cld cmskw1: lodsb cmp al,' ' ; control code? jae cmskw2 ; ae = no push ax mov dl,5eh ; caret int dos pop ax add al,'A'-1 ; plus ascii bias cmskw2: mov dl,al ; display chars in word int dos loop cmskw1 pop si cmskw3: ; mov dx,offset cmer03 ; '" not usable here.' mcmsg cmer03,ccmer03 cmp kwstat,1 ; kywd status from getkw, not found? jb cmskw4 ; b = not found, a = ambiguous ; mov dx,offset cmer04 ; '" ambiguous' mcmsg cmer04,ccmer04 cmskw4: mov ah,prstr int dos ret cmskw endp ;;;;;;;;;; end of support routines for keyword parsing. ; Parse arbitrary text up to a CR. Enter with BX = pointer to output buffer, ; DX pointing to help text. Produces asciiz string. Return updated pointer in ; BX and output size in AH. Leading spaces are omitted unless comand.cmwhite ; is non-zero (cleared upon exit). It does not need to be followed by the ; usual call to confirm the line. Byte comand.cmblen can be used to specify ; the length of the caller's buffer; cleared to zero by this command to ; imply a length of 127 bytes (default) and if zero at startup use 127 bytes. cmtext proc near mov comand.cmptab,bx ; save pointer to data buffer mov comand.cmhlp,dx ; save the help message mov cx,0 ; init the char count cmp comand.cmblen,0 ; length of user's buffer given? jne cmtxt0 ; ne = yes mov comand.cmblen,127 ; else set 127 byte limit plus null cmtxt0: mov comand.cmsflg,0ffh ; skip initial spaces cmp comand.cmwhite,0 ; allow leading whitespace? je cmtxt1a ; e = no cmtxt1: mov comand.cmsflg,0 ; get all spaces cmtxt1a:call cmgtch ; get a char jnc cmtxt5 ; nc = non-terminator, put in buffer cmp ah,' ' ; space? je cmtxt5 ; e = yes, record it cmp ah,escape ; escape? jne cmtxt2 ; ne = no call esceoc ; do normal escape end-of-command jmp cmtxt0 ; try again cmtxt2: cmp ah,'?' ; asking a question? je cmtxt3 ; e = yes cmp ah,cr ; formal carriage return? je cmtxt2a ; e = yes inc comand.cmrptr ; accept char into buffer jmp cmtxt5 ; and record the char cmtxt2a:mov ah,cl ; return count in AH mov bx,comand.cmptab ; return updated pointer mov byte ptr[bx],0 ; put terminator into the buffer mov comand.cmwhite,0 ; clear leading whitespace flag mov comand.cmper,0 ; reset to variable recognition mov comand.cmblen,0 ; set user buffer length to unknown mov comand.cmkeep,0 jmp rskp ; Help processor cmtxt3: inc comand.cmrptr ; count the ? cmp cx,0 ; Is "?" first char? jne cmtxt5 ; ne = no, just add to buffer dec comand.cmrptr mov comand.cmsiz,0 ; no keyword for help mov comand.cmwhite,0 ; clear leading whitespace flag cmp comand.cmhlp,0 ; external help given? jne cmtxt3a ; ne = yes ;---------------------- Sept.20,1990 [zqf] ; mov comand.cmhlp,offset cmin00 ; confirm with c/r msg push dx mcmsg cmin00,ccmin00 mov comand.cmhlp,dx pop dx ;---------------------- mov comand.cmblen,0 ; set user buf length to unknown cmtxt3a:jmp cmkyhlp ; do help process cmtxt5: inc cx ; increment the count mov bx,comand.cmptab ; pointer into destination array mov [bx],ah ; put char into the buffer inc bx mov al,0 mov [bx],al ; insert null terminator mov comand.cmptab,bx cmp ch,0 ; overflowed into high byte? jne cmtxt6 ; ne = yes cmp cl,comand.cmblen ; buffer filled? ja cmtxt6 ; a = yes, declare error jb cmtxt5a ; a = not filled yet mov ah,conout ; notify user that the buffer is full mov dl,bell int dos cmtxt5a:jmp cmtxt1 cmtxt6: mov ah,prstr ; mov dx,offset cmer09 mcmsg cmer09,ccmer09 int dos jmp prserr ; declare parse error cmtext endp ; Parse arbitrary text up to whitespace. Enter with DX pointing to output ; buffer and BX pointing to help text. Produces asciiz string. Return updated ; pointer in DX and input size in AH. Skips leading whitespace unless ; comand.cmwhite is non-zero (cleared upon exit). Does a return skip exit. cmfil0 proc near mov comand.cmptab,dx ; save pointer to data buffer mov comand.cmhlp,bx ; save the help message mov comand.cmsiz,0 ; init the char count cmfil0a:cmp comand.cmwhite,0 ; allow leading whitespace? jne cmfil1 ; ne = yes mov comand.cmsflg,0ffh ; omit leading space cmfil1: call cmgtch ; get a char jc cmfi1a ; c = terminator jmp cmfil5 ; put char into the buffer cmfi1a: cmp ah,escape ; escape? je cmfi1b ; e = yes jmp cmfil2 ; process other terminators cmfi1b: call esceoc ; do normal escape end-of-command jmp cmfil0a ; try again cmfil2: cmp ah,'?' ; asking a question? je cmfil3 ; e = yes xchg dx,bx ; re-interchange bx and dx mov comand.cmwhite,0 ; clear whitespace flag mov comand.cmper,0 ; reset to variable recognition mov comand.cmkeep,0 mov bx,comand.cmptab ; pointer into destination array mov byte ptr[bx],0 ; put null terminator into the buffer inc bx mov ah,byte ptr comand.cmsiz ; return count in AH mov dx,comand.cmptab ; return updated pointer xchg dx,bx ; re-interchange bx and dx mov comand.cmwhite,0 ; clear whitespace flag mov comand.cmper,0 ; reset to variable recognition mov comand.cmkeep,0 jmp rskp ; return success cmfil3: inc comand.cmrptr ; count the ? cmp comand.cmsiz,0 ; Is "?" first char? jne cmfil5 ; ne = no, just add to buffer dec comand.cmrptr mov comand.cmsiz,0 cmp comand.cmhlp,0 ; external help given? jne cmfil3a ; ne = yes ; ------------------------Sept.20,1990 [zqf] ; mov comand.cmhlp,offset cmin00 ; confirm with c/r msg push dx mcmsg cmin00,ccmin00 mov comand.cmhlp,dx pop dx ; ------------------------ cmfil3a:jmp cmkyhlp ; do help process cmfil5: inc comand.cmsiz ; inrement the count mov bx,comand.cmptab ; pointer into destination array mov [bx],ah ; put char into the buffer inc bx mov comand.cmptab,bx jmp cmfil1 ; the end of cmfil0 cmfil0 endp ; This routine gets a confirm (CR) and displays any extra non-blank text. ; errflag non-zero means suppress "extra text" display in this routine ; because another routine is handling errors. cmcfrm proc near mov comand.cmper,1 ; do not react to \%x substitutions cmcfr1: mov comand.cmsflg,0ffh ; set space-seen flag (skip spaces) call cmgtch ; get a char push comand.cmrptr pop temp ; remember first non-space position jc cmcfr4 ; c = terminator dec temp ; backup to text char cmcfr3: mov comand.cmsflg,0ffh ; set space-seen flag (skip spaces) call cmgtch jnc cmcfr3 ; read until terminator cmcfr4: cmp ah,' ' je cmcfr3 ; ignore ending on space cmp ah,escape ; escape? jne cmcfr5 ; ne = no call esceoc ; do standard end of cmd on escape mov ax,comand.cmrptr cmp ax,temp ; started text yet? je cmcfr1 ; e = no jmp short cmcfr3 ; try again cmcfr5: cmp ah,'?' ; curious? jne cmcfr6 ; ne = no ;---------------------- Sept.20,1990 [zqf] ; mov comand.cmhlp,offset cmin00 ; msg Confirm with c/r push dx mcmsg cmin00,ccmin00 mov comand.cmhlp,dx pop dx ;---------------------- mov comand.cmsiz,0 ; no keyword mov errflag,0 jmp cmkyhlp ; do help cmcfr6: cmp ah,cr ; the confirmation char? jne cmcfr3 ; ne = no cmp errflag,0 ; already doing one error? jne cmcfr7 ; ne = yes, skip this one mov cx,comand.cmrptr ; pointer to terminator mov dx,temp ; starting place sub cx,dx ; end minus starting point = length cmp cx,0 ; string present? jle cmcfr7 ; le = nothing to display push dx ; save source pointer mov ah,prstr ; mov dx,offset cmer07 ; ?Ignoring extras mcmsg cmer07,ccmer07 int dos pop dx mov bx,1 ; stdout handle, cx=count, dx=src ptr mov ah,write2 ; allow embedded dollar signs int dos mov ah,prstr mov dx,offset cmer08 ; trailer msg int dos cmcfr7: mov errflag,0 mov comand.cmper,0 ; reset to variable recognition mov comand.cmkeep,0 jmp rskp ; return confirmed cmcfrm endp ;;; Routines to get and edit incoming text. ; Detect '\%x' (x = '0' or above) and substitute the matching Macro string ; in place of the '\%x' phrase in the user's buffer. If comand.cmper != 0 ; then treat '\%' as literal characters. If no matching parameter exists ; just remove '\%x'. Returns carry clear if nothing done, else carry set and ; new text already placed in user's buffer. comand.cmwptr and comand.cmcnt ; are updated. Uses depth-first recursion algorithm. All registers preserved. subst proc near cmp comand.cmper,0 ; should we recognize '\%'? jne subst2 ; ne = no, treat as literals cmp ah,'\' ; is it the first char of the pattern? jne subst1 ; ne = no, try next mov subcnt,1 ; say first is matched jmp short subst2 ; exit successfully subst1: cmp subcnt,1 ; first char matched already? ja subst3 ; a = first two have been matched jb subst2 ; b = none yet inc subcnt ; assume a match follows cmp ah,'%' ; second match char, same? je subst2 ; e = yes mov subcnt,0 ; mismatch, clear match counter subst2: clc ; carry clear = no substitution done ret subst3: mov subcnt,0 ; clear match counter cmp ah,'0' ; third char is '0' or above? jb subst2 ; b = out of range, no match push bx ; save working regs push cx sub comand.cmrptr,3 ; reread commands where backslash was call bufreset ; reset buffer to this point push comand.cmptab ; save current keyword parsing parms push comand.cmsptr push comand.cmsiz mov bx,comand.cmrptr ; points at backslash mov comand.cmsptr,bx ; direct keyword routine to it mov comand.cmsiz,3 ; three bytes (\%x) of user text mov comand.cmptab,offset mcctab ; use Macro table for new text call getkw ; get ptr, bx, to matching keyword pop comand.cmsiz ; restore borrowed keyword parameters pop comand.cmsptr pop comand.cmptab jc substx ; c = not found, keep after pops mov cl,byte ptr[bx] ; length of found word add cl,2 ; plus count field and '$' mov ch,0 add bx,cx ; point to 16 bit value (string ptr) mov bx,[bx] ; point to string structure mov cl,[bx] ; length of string (ch=0 from above) inc bx ; skip length byte, bx=string address jcxz substx ; z = nothing left to transfer cld subst4: mov ah,[bx] ; get a char inc bx push di ; assume di is used by other routines mov di,comand.cmrptr mov [di],ah ; store char (without es:di) inc di mov comand.cmwptr,di ; where to store next char mov comand.cmrptr,di ; move read pointer too cmp di,offset comand.cmdbuf+size cmdbuf ; reached max buffer size? pop di jae subst6 ; ae = yes, no more room cmp sp,20*2 ; still some stack space? jle subst6 ; le = insufficient room call SUBST ; rescan what we stored (recursion) jnc subst5 ; nc = no substitution, so no kbd test push ax ; break out of loops with Control-C push dx mov ah,constat ; check console status for Control-C int dos ; our control-break handler sees it pop dx ; and cmgetc reads it pop ax subst5: loop subst4 substx: pop cx pop bx stc ; set carry to say have stored chars ret subst6: mov ah,prstr ; mov dx,offset stkmsg ; out of work space msg mcmsg stkmsg,cstkmsg int dos jmp prserr ; and declare parse error subst endp ; Read chars from Take file, keyboard, or redirected stdin. Edit and remove ; BS & DEL, Tab becomes space, act on Control-C, pass Control-U and Control-W. ; Do echoing unless comand.cmquiet is non-zero. Do semicolon comments in Take ; and indirect stdin files (\; means literal semicolon). Return char in AL. CMGETC proc near ; Basic raw character reader mov ah,taklev ; get current Take level mov intake,ah ; remember here for later callers cmget01:cmp prevch,0 ; left over char yet to be exported? je cmget02 ; e = no mov al,prevch ; get old char mov prevch,0 ; clear storage jmp cmget6 ; analyze it cmget02:cmp taklev,0 ; in a Take file? jne cmget1 ; ne = yes, do Take reader section call isdev ; is stdin a device or a file? jnc cmget20 ; nc = file (redirection of stdin) jmp cmget10 ; c = device, do separately cmget20:call iseof ; see if file is empty jc cmget21 ; c = EOF on disk file mov ah,coninq ; read the char from file, not device int dos cmp al,cr ; is it a cr? je cmget01 ; yes, ignore and read next char cmp al,ctlz ; Control-Z? je cmget21 ; e = yes, same as EOF here cmp al,lf ; LF's end lines from disk files jne cmget23 ; ne = not LF, pass along as is mov al,cr ; make LF a CR for this parser call iseof ; see if this is the last char in file jnc cmget23 ; nc = not EOF, process new CR cmget21:mov flags.extflg,1 ; EOF on disk file, set exit flag cmget23:jmp short cmget6 ; do echoing and return cmget1: push bx ; read from Take file mov bx,takadr cmp [bx].takcnt,0 ; bytes remaining in Take buffer jne cmget4 ; ne = not empty cmp [bx].taktyp,0feh ; type of Take (file?) jne cmget3 ; ne = no (macro) call takrd ; read another buffer cmp [bx].takcnt,0 ; anything in the file? jne cmget4 ; ne = yes cmget3: pop bx ; clear stack jmp cmget5 ; close take file cmget4: push si mov si,[bx].takptr ; read a char from Take buffer cld lodsb mov [bx].takptr,si ; move buffer pointer pop si dec [bx].takcnt ; decrease number of bytes remaining pop bx cmp al,ctlz ; Control-Z? jne cmget6 ; ne = no cmget5: cmp comand.cmkeep,0 ; keep Take/macro open after eof? jne cmget5a ; ne = yes call takclos ; close take file cmget5a:mov al,cr ; report cr as last char mov noparse,0 ; and say end of comment ; start common code cmget6: cmp al,lf ; line feed? jne cmget8 ; ne = no jmp cmget01 ; yes, ignore and read another char ; handle comments (echo but not parse) cmget8: cmp noparse,0 ; parsing? jne cmget9 ; ne = yes, do echo and no parse cmp prevch,0 ; have previous char to analyze? jne cmget8c ; ne = yes cmp al,'\' ; start of '\;'? jne cmget8a ; ne = no mov prevch,al ; yes, maybe. save '\' til later jmp cmget02 ; read next char cmget8a:cmp al,';' ; possible start of comment? jne cmget8e ; no, export al mov al,' ' ; replace ';' with space for comment jmp cmget9 ; go start a comment cmget8c:cmp al,';' ; end of '\;'? je cmget8d ; e = yes, omit leading backslash xchg prevch,al ; no, save new, recover old '\' jmp short cmget8e ; export '\' cmget8d:mov prevch,0 ; clear old '\' cmget8e:jmp cmget12 ; do parsing mov prevch,0 ; clear previous char jmp cmget02 ; get next char ; echo comment cmget9: cmp flags.takflg,0 ; echoing take files? je cmget9a ; e = no push ax push dx mov ah,conout ; echo current char mov dl,al int dos pop dx pop ax cmget9a:mov noparse,0 ; cr ends comment cmp al,cr ; end of comment? je cmget13 ; e = yes, export cr mov noparse,1 ; still in comment jmp cmget01 ; read more chars ; read from tty device cmget10:mov ah,coninq ; Get a char from device, not file int dos ; with no echoing or al,al jnz cmget12 ; ignore null bytes of special keys int dos ; read and discard scan code byte jmp cmget10 ; try again cmget12:cmp al,'C'and 1Fh ; Control-C? je cmget14 ; e = yes cmp al,TAB ; tab is replaced by space jne cmget13 ; ne = not tab mov al,' ' cmget13:ret ; normal exit, char is in AL cmget14:mov ah,prstr ; Control-C handler push dx mov dx,offset ctcmsg ; show Control-C int dos pop dx mov prevch,0 mov flags.cxzflg,'C' ; tell others the news mov sp,cmdstk ; restore command entry stack pointer ret ; and fail immediately (a longjmp) cmgetc endp ; Read chars from user (cmgetc). Detect terminators. Reads from buffer ; comand.cmbuf. Set read pointer comand.cmrptr to next free buffer byte if ; char is not a terminator: chars CR, LF, FF, '?' (returns carry set for ; terminators). Do ^U, ^W editing, convert FF to CR plus clear screen. ; Edit "-" as line continuation, "\-" as "-". ; Return char in AH. CMINBF proc near ; Buffer reader, final editor push di mov di,comand.cmwptr cmp di,offset comand.cmdbuf+size cmdbuf-3 ; max buffer size - 3 pop di jb cminb1 ; b = not full mov ah,conout ; almost full, notify user push dx mov dl,bell int dos mov dx,offset comand.cmdbuf+size cmdbuf cmp comand.cmrptr,dx ; max buffer size? pop dx jb cminb1 ; b = more room mov ah,prstr push dx ; mov dx,offset cmer09 ; command too long mcmsg cmer09,ccmer09 int dos pop dx jmp prserr ; overflow = parse error cminb1: push bx mov bx,comand.cmrptr mov ah,[bx] ; get current command char while here cmp bx,comand.cmwptr ; do we need to read more? pop bx ; no if cmrptr < cmwptr jb cminb2 ; b: cmrptr < cmwptr (have extra here) call cmgetc ; no readahead, read another into al mov ah,al ; keep char in 'ah' push bx mov bx,comand.cmwptr ; Get the pointer into the buffer mov [bx],ah ; Put it in the buffer inc bx mov comand.cmwptr,bx ; inc write pointer pop bx ; Char to be delivered is in ah cminb2: cmp ah,'W' and 1fh ; Is it a ^W? jne cminb3 call cntrlw ; Kill the previous word jnc cminbf ; nc = no change, get another char jmp repars ; need a new command scan (cleans stk) cminb3: cmp ah,'U' and 1fh ; Is it a ^U? jne cminb3a ; ne = no mov comand.cmwptr,offset comand.cmdbuf ;reset buffer write pointer jmp repars ; Go start over (cleans stack) ; BS and DEL cminb3a:cmp ah,DEL ; Delete code? je cminb3b ; e = yes cmp ah,BS ; Backspace (a delete operator)? jne cminb4 ; ne = no cminb3b:call bufdel ; delete char from buffer jc cminb3c ; c = did erasure jmp cminbf ; no erasure, ignore BS, get more cminb3c:jmp repars ; could have deleted previous token cminb4: push bx ; look for hyphen or \hyphen cmp ah,cr ; check for hyphen line continuation jne cminb4b ; ne = not end of line mov bx,comand.cmwptr ; Get the pointer into the buffer cmp bx,offset comand.cmdbuf-2 ; do we have a previous char? jb cminb4b ; b = no cmp byte ptr[bx-2],'-' ; previous char was a hyphen? jne cminb4b ; ne = no pop bx call bufdel ; delete the hyphen jmp repars cminb4b:pop bx ; Echoing done here cmp comand.cmquiet,0 ; quiet mode? jne cminb5 ; yes, skip echoing cmp taklev,0 ; in a take file? je cminb4a ; e = no cmp flags.takflg,0 ; echo take file? je cminb5 ; e = no cminb4a:push ax ; save the char cmp ah,' ' ; printable? jae cminb4c ; yes, no translation needed cmp ah,cr ; this is printable je cminb4c cmp ah,lf je cminb4c cmp ah,escape ; escape? je cminb4d ; do not echo this character push ax ; show controls as caret char push dx mov dl,5eh ; caret mov ah,conout int dos pop dx pop ax add ah,'A'-1 ; make control code printable cminb4c:push dx mov dl,ah mov ah,conout int dos ; echo it ourselves pop dx cminb4d:pop ax ; and return char in ah cminb5: cmp ah,cr ; Is it a carriage return? je cminb6 cmp ah,lf ; Is it a line feed? je cminb6 cmp ah,ff ; Is it a formfeed? jne cminb7 ; none of the above, report bare char call cmblnk ; FF: clear the screen and push bx push cx push dx call locate ; Home the cursor mov bx,comand.cmwptr ; make the FF parse like a cr mov byte ptr [bx-1],cr ; pretend a carriage return were typed pop dx pop cx pop bx cminb6: cmp comand.cmwptr,offset comand.cmdbuf ; parsed any chars yet? jne cminb7 ; ne = yes cmp comand.cmcr,0 ; bare cr's allowed? jne cminb7 ; ne = yes jmp prserr ; If not, just start over cminb7: clc ret cminbf endp ; Read chars from cminbf. Comand.cmrptr points to next char to be read. ; Compresses repeated spaces if comand.cmsflg is non-zero. Exit with ; comand.cmrptr pointing at a terminator or otherwise at next free slot. ; Non-space then space acts as a terminator but comand.cmrptr is incremented. ; Substitution variables, '\%x', are detected and expanded. Return char in AH. CMGTCH proc near ; return char in AH, from rescan buf call cminbf ; get char from buffer or user push bx mov bx,comand.cmrptr ; get read pointer into the buffer mov ah,[bx] ; read the next char inc bx mov comand.cmrptr,bx ; where to read next time pop bx call subst ; examine for text substitution jnc cmgtc1 ; nc = no substitutions done jmp repars ; reparse line with new material cmgtc1: cmp ah,' ' ; Is it a space? jne cmgtc3 ; ne = no cmgtc2: cmp comand.cmsflg,0 ; space flag, was last char a space? jne cmgtch ; ne = yes, get another char mov comand.cmsflg,0FFH ; Set the space(s)-seen flag mov ah,' ' ; character for caller stc ; set carry for terminator ret ; return space as a terminator cmgtc3: mov comand.cmsflg,0 ; clear the space-seen flag cmp ah,escape ; terminators remain in buffer but je cmgtc4 ; are ready to be overwritten cmp ah,'?' ; Is the user curious? jne cmgtc3a ; ne = no cmp taklev,0 ; in a Take file? jne cmgtc3b ; ne = yes, make query ordinary char je cmgtc4 cmgtc3a:cmp ah,cr je cmgtc4 cmp ah,lf je cmgtc4 cmp ah,ff je cmgtc4 cmgtc3b:clc ; carry clear for non-terminator ret cmgtc4: dec comand.cmrptr ; point at terminating char stc ; set carry to say it is a terminator ret cmgtch endp ; Reset comand.cmdbuf write pointer (.cmwptr) to where the read pointer ; (.cmrptr) is now. Discards material not yet read. bufreset proc near push comand.cmrptr ; where next visible char is read pop comand.cmwptr ; where new char goes in buffer ret bufreset endp ; Delete character from screen and adjust buffer. Returns carry clear if ; no erasure, carry set otherwise. bufdel proc near dec comand.cmrptr ; remove previous char from buffer cmp comand.cmrptr,offset comand.cmdbuf ; back too far? jae bufde2 ; ae = no, material can be erased mov comand.cmrptr,offset comand.cmdbuf ; set to start of buffer call bufreset ; reset buffer clc ; say no erasure ret bufde2: call bufreset ; reset buffer stc ; say did erasure ret bufdel endp ; Come here is user types ^W when during input. Remove word from buffer. cntrlw proc near push ax push cx push dx mov cx,comand.cmrptr ; char beyond what user sees mov comand.cmwptr,cx ; truncate buffer there sub cx,offset comand.cmdbuf ; compute chars in buffer clc ; say have not yet modified line jcxz ctlw2 ; z = nothing to do, exit no-carry push es std ; scan backward mov ax,ds mov es,ax ; point to the data are mov di,comand.cmwptr ; looking from here dec di mov al,' ' repe scasb ; look for non-space je ctlw1 ; all spaces, nothing to do inc di ; move back to non-space inc cx repne scasb ; look for a space jne ctlw1 ; no space, leave ptrs alone inc di inc cx ; skip back over space ctlw1: inc di pop es cld ; reset direction flag mov comand.cmwptr,di ; update pointer stc ; set carry to say modified line ctlw2: pop dx pop cx pop ax ret cntrlw endp ; Jump to REPARS to do a rescan of the existing buffer. ; Jump to PRSERR on a parsing error (quits command, clears old read material) PRSERR PROC NEAR mov comand.cmwptr,offset comand.cmdbuf ; initialize write pointer mov ah,prstr mov dx,offset crlf ; leave old line, start a new one int dos ; reparse current line REPARS: mov comand.cmrptr,offset comand.cmdbuf ; reinit read pointer mov comand.cmper,0 ; reset to variable recognition mov comand.cmsflg,0FFH ; strip leading spaces cmp taklev,0 ; in Take cmd? je prser2 ; e = no cmp flags.takflg,0 ; echo contents of Take file? je prser3 ; e = no prser2: call ctlu ; clear display's line, reuse it mov ah,prstr mov dx,comand.cmprmp ; display the prompt int dos prser3: mov bx,0ffffh ; returned keyword value mov sp,comand.cmostp ; set new sp to old one jmp comand.cmrprs ; jump to just before the prompt call PRSERR ENDP ; This routine prints the prompt and specifies the reparse address. ; Enter with pointer to prompt string in dx. PROMPT PROC NEAR mov comand.cmprmp,dx ; save the prompt pop ax ; Get the return address mov comand.cmrprs,ax ; Save as address to go to on reparse mov comand.cmostp,sp ; Save for later restoration push ax ; Put it on the stack again mov ax,offset comand.cmdbuf mov comand.cmwptr,ax ; reset buffer read/write pointers mov comand.cmrptr,ax mov ax,0 mov comand.cmper,0 ; allow substitutions mov comand.cmsflg,0FFH ; remove leading spaces cmp flags.takflg,0 ; look at Take flag jne promp1 ; ne=supposed to echo, skip this check cmp taklev,0 ; inside a take file? je promp1 ; no, keep going ret ; yes, return promp1: mov ah,prstr mov dx,offset crlf int dos mov ah,prstr ; display the prompt mov dx,comand.cmprmp int dos ret PROMPT ENDP ISDEV PROC NEAR ; Set carry if STDIN is non-disk push ax push bx push dx mov al,0 ; get device info mov ah,ioctl mov bx,0 ; handle 0 is stdin int dos and dl,80h ; bit set if handle is for a device rcl dl,1 ; put it into the carry bit pop dx pop bx pop ax ret ; carry set if device ISDEV ENDP ISEOF PROC NEAR ; Set carry if STDIN is at EOF push ax ; but only if stdin is a non-device push bx push dx mov al,0 ; get device info mov ah,ioctl mov bx,0 ; handle 0 is stdin int dos test dl,80h ; bit set if handle is for a device mov ah,ioctl mov al,6 ; get handle input status, set al jnz iseof1 ; nz = device, always ready (al != 0) int dos iseof1: or al,al ; EOF? pop dx pop bx pop ax jnz iseof2 ; nz = no stc ; set carry for eof ret iseof2: clc ; clear carry for not-eof ret ISEOF ENDP ; Convert ascii characters in al and ah to lowercase. [jrd] ; All registers are preserved except AX, of course. TOLOWR PROC NEAR cmp ah,'A' ; less that cap A? jl tolow1 ; l = yes. leave untouched cmp ah,'Z'+1 ; more than cap Z? jns tolow1 ; ns = yes or ah,20H ; convert to lowercase tolow1: cmp al,'A' ; less that cap A? jl tolow2 ; l = yes. leave untouched cmp al,'Z'+1 ; more than cap Z? jns tolow2 ; ns = yes or al,20H ; convert to lowercase tolow2: ret TOLOWR endp ; Jumping to this location is like retskp. It assumes the instruction ; after the call is a jmp addr. RSKP PROC NEAR pop bp add bp,3 push bp ret RSKP ENDP ; Jumping here is the same as a ret. R PROC NEAR ret R ENDP code ends end