NAME msscmd ; File MSSCMD.ASM include mssdef.h ; Copyright (C) 1982, 1997, Trustees of Columbia University in the ; City of New York. The MS-DOS Kermit software may not be, in whole ; or in part, licensed or sold for profit as a software product itself, ; nor may it be included in or distributed with commercial products ; or otherwise distributed by commercial concerns to their clients ; or customers without written permission of the Office of Kermit ; Development and Distribution, Columbia University. This copyright ; notice must not be removed, altered, or obscured. ; ; Edit history ; 12 Jan 1995 version 3.14 ; Last edit ; 12 Jan 1995 public comnd, comand, isdev, iseof, prompt, tolowr, toupr, valtab public parstate, pardone, parfail, nparam, param, lparam, ninter public inter, atparse, atpclr, atdispat, cspb, dspb, mprompt, nvaltoa public savepoff, saveplen, keyboard, fwrtdir, cspb1, filetdate public fqryenv, ifelse, oldifelse, retbuf, vfile env equ 2CH ; environment address in psp braceop equ 7bh ; opening curly brace bracecl equ 7dh ; closing curly brace ; codes for various \fxxx(arg list) operations F_char equ 1 F_code equ 2 F_contents equ 3 F_definition equ 4 ;F_eval equ 5 F_exec equ 6 F_files equ 7 F_index equ 8 F_length equ 9 F_literal equ 10 F_lower equ 11 F_lpad equ 12 F_maximum equ 13 F_minimum equ 14 F_nextfile equ 15 F_repeat equ 16 F_reverse equ 17 F_right equ 18 F_rpad equ 19 F_substr equ 20 F_upper equ 21 F_date equ 22 F_size equ 23 F_replace equ 24 F_eval equ 25 F_rindex equ 26 F_verify equ 27 F_ipaddr equ 28 F_tod2secs equ 29 F_chksum equ 30 F_basename equ 31 ; Constants for valtoa V_environ equ 0 V_argc equ 1 V_carrier equ 22 V_charset equ 33 V_console equ 27 V_count equ 2 V_cmdlevel equ 29 V_cps equ 24 V_date equ 3 V_ndate equ 14 V_dir equ 5 V_dosver equ 19 V_errlev equ 4 V_kbd equ 10 V_line equ 15 V_monitor equ 28 V_parity equ 21 V_platform equ 8 V_port equ 15 V_program equ 12 V_prompt equ 23 V_query equ 31 V_session equ 17 V_tcpip equ 20 V_space equ 25 V_speed equ 11 V_startup equ 26 V_status equ 13 V_sysid equ 32 V_system equ 9 V_terminal equ 16 V_time equ 6 V_ntime equ 18 V_version equ 7 V_input equ 30 V_inpath equ 34 V_disk equ 35 V_cmdfile equ 36 V_inidir equ 37 V_instatus equ 38 V_minput equ 39 V_return equ 40 V_connection equ 41 V_filespec equ 42 ; Like main filest, but shorter for local usage. fileiost struc dta db 26 dup(0) ; DOS, 21 resev'd bytes, file attr, 2 each date & time sizelo dw 0 ; DOS, file size double word sizehi dw 0 fname db 13 dup(0) ; DOS, filename, asciiz, with dot. End of DOS section fileiost ends data segment extrn flags:byte, taklev:byte, takadr:word, mcctab:byte extrn kstatus:word, errlev:byte, psp:word, fsta:word extrn portval:word, bdtab:byte, tdfmt:byte, machnam:byte extrn verident:word, kstatus:word, errlev:byte, comptab:byte extrn termtb:byte, dosnum:word, prmptr:word, startup:byte extrn crt_mode:byte, tmpbuf:byte, buff:byte, minpcnt:word extrn decbuf:byte, encbuf:byte, queryseg:word, inpath_seg:word extrn cmdfile:byte, inidir:byte, dos_bottom:byte, marray:word extrn input_status:word, domath_ptr:word, domath_cnt:word extrn domath_msg:word, atoibyte:byte, atoi_err:byte, atoi_cnt:word ifndef no_tcp extrn sescur:word, tcp_status:word endif ; no_tcp ; Start Patch structure. Must be first. even dspb dw code ; segment values for patcher dw code1 dw code2 dw data dw data1 dw _TEXT db 64 dup (0) ; data segment patch buffer ; with space for other material, if req'd ; end of Patch structure progm db 'MS-DOS_KERMIT$' ; for \v(program) system db 'MS-DOS$' ; for \v(system) keyboard dw 88 ; \v(keyboard) kind of keybd 88/101 ten db 10 ; for times ten comand cmdinfo <> cmer00 db cr,lf,'?Program internal error, recovering$' cmer01 db cr,lf,'?More parameters are needed$' cmer02 db cr,lf,'?Word "$' cmer03 db '" is not usable here$' cmer04 db '" is ambiguous$' cmer07 db cr,lf,'?Ignoring extra characters "$' cmer08 db '"$' cmer09 db cr,lf,'?Text exceeded available buffer capacity$' cmin01 db ' Use one of the following words in this position:',cr,lf,'$' stkmsg db cr,lf,bell,'?Exhausted work space! Circular definition?$' moremsg db '... more, press a key to continue ...$' crlf db cr,lf,'$' ctcmsg db 5eh,'C$' cmunk db 'unknown' errflag db 0 ; non-zero to suppress cmcfrm errors kwstat db 0 ; get-keyword status subcnt db 0 ; count of chars matched in '\%' subtype db 0 ; kind of sub (% or v) evaltype dw 0 ; evaltoa kind bracecnt db 0 ; curly brace counter parencnt db 0 ; parenthesis counter report_binary db 0 ; subst reporting \number directly argptr dw 3 dup (0) ; \fxxx() argument pointers arglen dw 3 dup (0) ; \fxxx() argument lengths replay_cnt dw 0 ; report to app, no echo replay_skip db 0 ; non-zero to skip subst for replays retbuf db 130 dup (0) ; \v(return) buffer vfile db 65 dup (0) ; \v(filespec) buffer fileio fileiost <> ; find first/next support cmsflg db 0 ; Non-zero when last char was a space cmkind db 0 ; command kind request (cmkey etc) in_showprompt db 0 ; Non-zero when in showprompt cmdbuf db cmdblen dup (0) ; Buffer for command parsing rawbuf db cmdblen dup (0) ; input, what user provided read_source db 0 ; channel whence we read endedbackslash db 0 in_reparse db 0 even cmdstk dw 0 ; stack pointer at comand call time cmptab dw 0 ; Address of present keyword table cmhlp dw 0 ; Address of present help cmwptr dw 0 ; Pointer for next char write cmrptr dw 0 ; Pointer for next char read cmsiz dw 0 ; Size info of user input cmsptr dw 0 ; Place to save a pointer mcmprmp dw 0 ; master prompt, string address mcmrprs dd 0 ; master prompt, reparse address mcmostp dw 0 ; master prompt, stack pointer temp dw 0 ; temp (counts char/line so far) cmrawptr dw 0 ; non-substitution level, write ptr ifndef no_tcp valtab db 1+42 ; table of values for \v(value) else valtab db 1+42 - 2 endif ; no_tcp mkeyw 'argc)',V_argc mkeyw 'carrier)',V_carrier mkeyw 'charset)',V_charset mkeyw 'cmdfile)',V_cmdfile mkeyw 'connection)',V_connection mkeyw 'console)',V_console mkeyw 'count)',V_count mkeyw 'cmdlevel)',V_cmdlevel mkeyw 'cps)',V_cps mkeyw 'date)',V_date mkeyw 'ndate)',V_ndate mkeyw 'directory)',V_dir mkeyw 'dosversion)',V_dosver mkeyw 'disk)',V_disk mkeyw 'errorlevel)',V_errlev mkeyw 'filespec)',V_filespec mkeyw 'inidir)',V_inidir mkeyw 'inpath)',V_inpath mkeyw 'input)',V_input mkeyw 'instatus)',V_instatus mkeyw 'keyboard)',V_kbd mkeyw 'line)',V_port mkeyw 'minput)',V_minput mkeyw 'monitor)',V_monitor mkeyw 'parity)',V_parity mkeyw 'platform)',V_platform mkeyw 'port)',V_port mkeyw 'program)',V_program mkeyw 'prompt)',V_prompt mkeyw 'query)',V_query mkeyw 'return)',V_return ifndef no_tcp mkeyw 'session)',V_session mkeyw 'tcpip_status)',V_tcpip endif ; no_tcp mkeyw 'space)',V_space mkeyw 'speed)',V_speed mkeyw 'startup)',V_startup mkeyw 'status)',V_status mkeyw 'sysid)',V_sysid mkeyw 'system)',V_system mkeyw 'terminal)',V_terminal mkeyw 'time)',V_time mkeyw 'ntime)',V_ntime mkeyw 'version)',V_version evaltab db 29 ; \fverb(args) mkeyw 'basename(',F_basename mkeyw 'character(',F_char mkeyw 'checksum(',F_chksum mkeyw 'code(',F_code mkeyw 'contents(',F_contents mkeyw 'date(',F_date mkeyw 'definition(',F_definition mkeyw 'eval(',F_eval mkeyw 'files(',F_files mkeyw 'index(',F_index mkeyw 'ipaddr(',F_ipaddr mkeyw 'length(',F_length mkeyw 'literal(',F_literal mkeyw 'lower(',F_lower mkeyw 'lpad(',F_lpad mkeyw 'maximum(',F_maximum mkeyw 'minimum(',F_minimum mkeyw 'nextfile(',F_nextfile mkeyw 'rindex(',F_rindex mkeyw 'repeat(',F_repeat mkeyw 'replace(',F_replace mkeyw 'reverse(',F_reverse mkeyw 'right(',F_right mkeyw 'rpad(',F_rpad mkeyw 'size(',F_size mkeyw 'substr(',F_substr mkeyw 'tod2secs(',F_tod2secs mkeyw 'verify(',F_verify mkeyw 'upper(',F_upper ; not implemented in MS-DOS Kermit ; mkeyw 'execute(',F_exec envtab db 1 ; \$(..) Environment table mkeyw 'An Environment variable)',0 ; reserve 0 in valtab numtab db 1 ; \number help table mkeyw 'a number such as \123',0 ; table of Parity strings parmsgtab dw parevnmsg,parmrkmsg,parnonmsg,paroddmsg,parspcmsg parevnmsg db 'EVEN',0 parmrkmsg db 'MARK',0 parnonmsg db 'NONE',0 paroddmsg db 'ODD',0 parspcmsg db 'SPACE',0 onoffmsgtab dw offmsg,onmsg ; on/off table, asciiz offmsg db 'OFF',0 onmsg db 'ON',0 ansiword db 'ANSI',0 ; \v(console) ASCIIZ strings noneword db 'NONE',0 colormsg db 'COLOR',0 ; \v(monitor) ASCIIZ strings monomsg db 'MONO',0 connmsg db 'local',0 ; \v(connection) state valtmp dw 0 numtmp dw 0 even envadr dd 0 ; seg:offset of a string in Environemt envlen dw 0 ; length of envadr's string even ; Control sequence storage area maxparam equ 16 ; number of ESC and DCS Parameters maxinter equ 16 ; number of ESC and DCS Intermediates savepoff label word ; Start per session save area parstate dw 0 ; parser state, init to startup pardone dw 0 ; where to jmp after Final char seen parfail dw 0 ; where to jmp if parser fails nparam dw 0 ; number of received Parameters param dw maxparam dup (0) ; Parameters for ESC lparam db 0 ; a single letter Parameter for ESC ninter dw 0 ; number of received Intermediates inter db maxinter dup (0),0 ; Intermediates for ESC, + guard saveplen dw ($-savepoff) ifelse db 0 ; non-zero if last IF statement failed oldifelse db 0 ; copy of ifelse from previous command month db 'Jan ','Feb ','Mar ','Apr ','May ','Jun ' db 'Jul ','Aug ','Sep ','Oct ','Nov ','Dec ' data ends data1 segment db 0 ; so error msg has non-zero offset cmin00 db ' Press ENTER to execute command$' f1hlp db ' number$' f2hlp db ' char$' f3hlp db ' variable or macro name$' f8hlp db ' pattern-text, string, start-position$' f9hlp db ' text$' f12hlp db ' text, pad-length, pad-char$' f15hlp db ' no argument$' f16hlp db ' repeat-text, repeat-count$' f18hlp db ' text, right-most count$' f20hlp db ' text, start-position, substring len$' f22hlp db ' filename$' f24hlp db ' source, pattern, replacement$' f25hlp db ' arithmetic expression$' f30hlp db ' string$' n1hlp db ' digits of a number whose value fits into one byte$' data1 ends ifndef no_terminal ifndef no_tcp _TEXT segment extrn cpatch:far _TEXT ends endif ; no_tcp endif ; no_terminal code1 segment assume cs:code1 extrn shovarcps:near, dskspace:far, fparse:far extrn strlen:far, prtscr:far, strcpy:far, prtasz:far extrn dec2di:far, decout:far, malloc:far, domath:far extrn atoi:far, takrd:far, buflog:far, tod2secs:far cspb1 equ this byte db (256-($-cspb1)) dup (0) ; code1 segment patch buffer ; end of Patch area code1 ends code segment extrn ctlu:near, cmblnk:near, locate:near extrn takclos:far, docom:near, getenv:near extrn getbaud:near, lnout:near, takopen_sub:far extrn takopen_macro:far assume cs:code, ds:data, es:nothing ; Patch area. Must be first in MSK's Code Seg cspb equ this byte dw seg code1 dw seg code2 dw seg data dw seg data1 dw seg _TEXT dw seg _DATA ifndef no_terminal ifndef no_tcp dw offset cpatch endif ; no_tcp endif ; no_terminal db (256-($-cspb)) dup (0) ; code segment patch buffer ; end of Patch area fctlu proc far ; FAR callable versions of items in seg code call ctlu ; for calling from code segment code1 below ret fctlu endp fcmblnk proc far call cmblnk ret fcmblnk endp fgetbaud proc far call getbaud ret fgetbaud endp fgetenv proc far call getenv ret fgetenv endp flocate proc far call locate ret flocate endp fdec2di proc far push cx push ds mov cx,es mov ds,cx call dec2di pop ds pop cx ret fdec2di endp ftakclos proc far call takclos ret ftakclos endp nvaltoa proc near push es mov ax,ds mov es,ax call fvaltoa pop es ret nvaltoa endp flnout proc far call lnout ret flnout endp fdskspace proc far call dskspace ret fdskspace endp ; This routine parses the specified function in AH. Any additional ; information is in DX and BX. ; Returns carry clear on success and carry set on failure COMND PROC NEAR mov cmdstk,sp ; save stack ptr for longjmp exit mov bracecnt,0 ; curly brace counter cmp ah,cmeol ; Parse a confirm? jne cm2 ; nz = no call cmcfrm ; get a Carriage Return end of line ret cm2: mov cmkind,ah ; remember for {} line continuation cmp ah,cmkey ; Parse a keyword? jne cm3 ; ne = no xor al,al ; get a zero/clear xchg al,ifelse ; get current ifelse state mov oldifelse,al ; remember here (one cmd delay) call cmkeyw ; get keyword ret cm3: cmp ah,cmline ; parse line of text jne cm4 call cmtxt ret cm4: cmp ah,cmword ; parse arbitrary word jne cm5 call cmtxt ret cm5: mov ah,prstr ; else give error mov dx,offset cmer00 ; "?Program internal error" int dos jmp prserr ; reparse ; Control-C exit path (far to near) cmexit label far mov sp,cmdstk ; restore command entry stack pointer stc retn ; and fail immediately (a longjmp) COMND ENDP code ends code1 segment assume cs:code1 ; 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 ; dw M ; M is the size of the keyword ; 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 carry clear for success and carry set for failure. ; cmptab: pointer to keyword table (supplied by caller) ; cmhlp: pointer to help message (supplied by caller) ; cmsptr: pointer to current user word text ; 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 cmline and cmword, ; reset automatically at end of call ; cmwptr: buffer write pointer to next free byte ; 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 far mov cmsiz,0 ; user word length mov ax,cmrptr ; get command reading pointer mov cmsptr,ax ; set pointer for start of user word mov cmhlp,bx ; save the help pointer mov 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 cmsflg,0ffh ; skip leading spaces/tabs call cmgtch ; get char from the user into ah jc cmky3 ; c = terminator mov dx,cmrptr ; next byte to read dec dx ; where we just read a char mov cmsptr,dx ; remember start of keyword inc cmsiz ; start counting user chars cmky2: call cmgtch ; read until terminator jc cmky3 ; c = terminator inc cmsiz ; count user chars jmp short cmky2 ; no terminator yet cmky3: cmp ah,'?' ; need help? jne cmky4 ; ne = no call cmkyhlp ; display help jmp repars 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 mov comand.cmcnvkind,cmcnv_none ; default is no conversion clc ret ; return successfully to user cmky5: cmp cmsiz,0 ; started a word yet? je cmky1 ; e = no, ignore escape, keep looking call cmkyhlp ; display help jmp repars cmky6: cmp cmsiz,0 ; length of user's text, empty? je cmky7 ; e = yes, parse error push bx mov bx,cmsptr ; point at first user character cmp byte ptr[bx],':' ; start of a label? pop bx jne cmky6a ; ne = no, return success mov cmsiz,1 ; say just one byte cmky6a: call getkw ; get unique kw, point to it with bx jc cmky8 ; c = not found add bx,[bx] ; add length of keyword text (CNT) add bx,2 ; point at value field mov bx,[bx] ; bx = return value following keyword call optionclr ; clear parser options mov errflag,0 clc ret ; return successfully ; all other terminators come here cmky7: cmp 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 int dos pop dx xor al,al mov comand.cmquiet,al ; permit echoing again mov comand.impdo,al ; clear flag to prevent loops stc ; failure ret 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 cmrptr,offset cmdbuf ; reinit read pointer mov comand.cmquiet,1 ; suppress echoing of same keyword mov bx,offset docom ; return DO as "found" keyword clc ret ; return success to invoke DO cmky8a: mov errflag,1 ; say already doing error recovery or kstatus,ksgen ; global command status, failure mov comand.cmquiet,0 ; permit echoing again 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 mov comand.impdo,0 ; clear flag to prevent loops dec cmrptr ; interactive, backup to terminator mov bx,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 bufdel ; cut back buffer to last good char jmp repars ; reparse interactive lines cmky9: call cmcfrm ; get formal end of command line ; to maintain illusion of typeahead ; and let user backspace to correct ; mistakes (we reparse everything) call cmskw ; display offending keyword cmky10: mov comand.cmquiet,0 ; permit echoing again stc ; say failure ret cmkeyw endp ;;;;;; start support routines for keyword parsing. cmkyesc proc near ; deal with escape terminator push cmrptr ; points at ESC pop cmwptr ; pointed one byte beyond ESC cmp 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 cx,[bx] ; length of keyword add bx,2 ; point to first letter dec cmrawptr ; overwrite ESC mov si,cmrawptr ; where raw writes go mov dx,cmsiz ; length of user word add bx,dx ; add chars known so far sub cx,dx ; calculate number yet to add jcxz cmkye4 ; z = none cmkye3: mov al,[bx] ; get a keyword letter inc bx call tolowr ; lowercase mov [si],al ; store it inc si loop cmkye3 ; do all new chars cmkye4: mov word ptr [si],' ' ; add trailing space, clear old next inc si mov cmrawptr,si pop si pop bx ; bx = keyword structure inc cmrawptr ; bufdel backs up one char jmp bufdel 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 xor cx,cx ; clear number of keyword (none yet) cmp cmsiz,0 ; user text given? jne cmkyh1 ; ne = yes, use matching keywords cmp 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,cmptab ; beginning of kw table mov ch,[bx] ; length of table xor cl,cl ; no keywords or help displayed yet inc bx ; point at CNT field cmkyh2: cmp 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 ax,[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 byte ptr temp,al ; reset the count mov ah,prstr mov dx,offset crlf ; break the line int dos cmkyh4: or cl,cl ; any keywords found yet? jnz cmkyh4a ; nz = yes mov dx,offset cmin01 ; start with One of the following: msg 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 di,bx ; get current keyword structure add di,2 ; text part push cx mov cx,[bx] ; string length to cx, offset to di call prtscr ; display counted string pop cx cmkyh5: dec ch ; are we at end of table? jle cmkyh7 ; le = yes, quit now add bx,[bx] ; next keyword, add CNT chars to bx add bx,4 ; skip CNT and 16 bit value jmp cmkyh2 ; go examine this keyword cmkyh6: mov si,cmhlp ; external help text in seg data1 xor bx,bx ; line counter push es mov ax,seg data1 ; all help text is in data1 mov es,ax cld cmkyh10:mov al,es:[si] ; read a help msg byte inc si cmp al,'$' ; end of message? je cmkyh14 ; e = yes, stop cmkyh11:mov ah,conout mov dl,al int dos ; display byte cmp dl,LF ; line break? jne cmkyh10 ; ne = no inc bl ; count line cmp bl,dos_bottom ; (24) time for a more msg? jbe cmkyh10 ; be = not yet xor bl,bl ; reset line count call iseof ; are we at EOF, such as from disk? jc cmkyh10 ; c = yes, ignore more msg mov ah,prstr mov dx,offset moremsg ; "... more..." msg int dos cmkyh13:mov ah,coninq ; read the char from file, not device int dos cmp al,3 ; a ^C? je short cmkyh14 ; e = yes, stop the display push bx ; save line counter push es ; and read pointer push si call fctlu ; clear display's line, reuse it pop si pop es pop bx jmp short cmkyh10 ; continue cmkyh14:pop es inc cl ; say gave help already cmkyh7: or cl,cl ; found any keywords? jnz cmkyh9 ; nz = yes mov cx,cmsiz ; length of word or cx,cx jg cmkyh8 ; g = something to show push dx mov ah,prstr mov dx,offset cmer01 ; command word expected 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 bufdel ; unwrite the "?" (cmrptr is there) ret 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 cmptab, user text pointed at by cmsptr and length ; in cmsiz. cmambg proc near push bx push cx push dx xor dl,dl ; count keyword matches so far mov bx,cmptab ; look at start of keyword table mov cl,[bx] ; get number of entries in table xor ch,ch ; 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 bx,[bx] ; add CNT chars to bx add bx,4 ; skip CNT and 16 bit value loop cmamb4 ; do rest of keyword table 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 cmsptr and length is in cmsiz. ; Registers preserved. cmpwrd proc near push cx mov cx,cmsiz ; length of user's text jcxz cmpwrd2 ; z: null user word matches no keyword cmp cx,[bx] ; user's text longer than keyword? ja cmpwrd2 ; a = yes, no match push ax push bx push si add bx,2 ; point at table's keyword text mov si,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 cmptab and cmsiz holding length of user's keyword (cmpwrd ; needs cmsptr pointing at user's keyword and length of 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 cmsiz,0 ; length of user word, empty? je getkw3 ; e = yes, fail mov bx,cmptab ; table of keywords mov cl,[bx] ; number of keywords in table xor ch,ch 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,cmsiz ; users word length cmp [bx],dx ; 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 bx,[bx] ; next keyword, add CNT chars to bx 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. Cmsptr points to user word, ; cmsiz has length. Modifies AX, CX, and DX. cmskw proc near cmp comand.cmquiet,0 ; Quiet mode? je cmskw0 ; e = no, regular mode ret ; else say nothing cmskw0: mov ah,prstr ; not one of the above terminators mov dx,offset cmer02 ; '?Word "' int dos mov ah,conout mov cx,cmsiz ; length of word jcxz cmskw3 ; z = null mov ah,conout push si mov si,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.' cmp kwstat,1 ; kywd status from getkw, not found? jb cmskw4 ; b = not found, a = ambiguous mov dx,offset cmer04 ; '" ambiguous' cmskw4: mov ah,prstr int dos ret cmskw endp ;;;;;;;;;; end of support routines for keyword parsing. ; CMLINE: Parse arbitrary text up to a CR. ; CMWORD: Parse text up to first trailing space, or if starts with () ; then consume the line. ; Enter with BX = pointer to output buffer ; DX pointing to help text. Produces asciiz string. Return updated pointer in ; BX and output size in AX. 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. ; If the line starts with an opening curly brace, then physical lines are ; automatically continued until the closing curly brace is obtained. ; Continuation breaks are a comma in the data stream and a CR/LF to the ; visual screen. Material after the closing brace is discarded. ; ; Lines and words starting on a curly brace and ending on a matching ; curly brace plus optional whitespace have the trailing whitespace ; omitted and both outer braces removed. cmtxt proc far mov cmptab,bx ; save pointer to data buffer xor ax,ax mov word ptr [bx],ax ; clear result buffer mov cmhlp,dx ; save the help message mov cmsiz,ax ; init the char count mov parencnt,al ; clear count of parentheses cmp comand.cmblen,ax ; length of user's buffer given? jne cmtxt1 ; ne = yes mov comand.cmblen,127 ; else set 127 byte limit plus null cmtxt1: cmp comand.cmwhite,al ; allow leading whitespace? jne cmtxt2 ; ne = yes mov cmsflg,0ffh ; omit leading space cmtxt2: call cmgtch ; get a char jc cmtxt3 ; c = terminator jmp cmtxt10 ; put char into the buffer cmtxt3: cmp ah,' ' ; space terminator? jne cmtxt4 ; ne = no cmp cmkind,cmline ; parsing lines? je cmtxt10 ; e = yes, put space in the buffer mov bx,cmptab ; words, check on () delimiters sub bx,cmsiz cmp byte ptr [bx],'(' ; started word with paren? jne cmtxt6 ; ne = no, it's a terminator cmp parencnt,0 ; outside parens? jne cmtxt10 ; ne = no, use as data inside (..) jmp cmtxt6 ; space is terminator cmtxt4: cmp ah,escape ; escape? jne cmtxt5 ; ne = no call esceoc ; do normal escape end-of-command jmp short cmtxt1 ; try again cmtxt5: cmp ah,'?' ; asking a question? je cmtxt8 ; e = yes cmp ah,CR ; bare CR? je cmtxt6 ; e = yes, always a terminator cmp cmkind,cmline ; reading a line? je cmtxt10 ; e = yes, other terms go into buffer ; else terminators terminate words cmtxt6: mov bx,cmptab ; pointer into destination array mov byte ptr[bx],0 ; put null terminator into the buffer xchg ax,cmsiz ; return count in AX or ax,ax ; empty? jz cmtxt7a ; z = yes cmtxt7: push si ; remove terminating curly braces mov si,cmptab ; where next output byte goes sub si,ax ; minus read, equals start of buffer mov cx,ax ; count to cx for unbrace call unbrace ; outer curly brace remover mov ax,cx ; returned length back to AX mov bx,si add bx,ax ; bx points to null terminator pop si cmtxt7a:call optionclr ; clear parser options cmp cmkind,cmline ; lines? jne cmtxt7f ; ne = no, words call rprompt ; restore master prompt level cmtxt7f:clc ret cmtxt8: inc cmrptr ; count the ? cmp cmsiz,0 ; Is "?" first char? jne cmtxt10 ; ne = no, just add to buffer dec cmrptr cmp cmhlp,0 ; external help given? jne cmtxt9 ; ne = yes mov cmhlp,offset cmin00 ; confirm with c/r msg cmtxt9: push cmsiz mov cmsiz,0 ; so we do not use keyword table call cmkyhlp ; use our help message pop cmsiz jmp cmtxt2 cmtxt10:inc cmsiz ; increment the output count mov bx,cmptab ; pointer into destination array mov [bx],ah ; put char into the buffer cmp cmkind,cmword ; word? jne cmtxt12 ; ne = no, line cmp ah,'(' ; opening paren? jne cmtxt11 ; ne = no inc parencnt ; count up parens jmp short cmtxt12 cmtxt11:cmp ah,')' ; closing paren? jne cmtxt12 ; ne = no sub parencnt,1 jns cmtxt12 ; ns = no underflow mov parencnt,1 ; don't underflow cmtxt12:inc bx mov cmptab,bx mov cx,cmsiz ; length of command so far cmp cx,4 ; got four chars? jne cmtxt12a ; ne = no cmp comand.cmarray,0 ; worry about \& as destination? je cmtxt12a ; e = no cmp word ptr [bx-4],'&\' ; starts with array indicator? jne cmtxt12a ; ne = no mov comand.cmper,0 ; allow substitution in [...] mov comand.cmarray,0 ; say have done array destination test cmtxt12a: cmp cx,comand.cmblen ; buffer filled? ja cmtxt14 ; a = yes, declare error jb cmtxt13 ; a = not filled yet mov ah,conout ; notify user that the buffer is full mov dl,bell int dos jmp cmtxt6 ; quit cmtxt13:jmp cmtxt2 cmtxt14:mov ah,prstr mov dx,offset cmer09 int dos jmp prserr ; declare parse error cmtxt 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 far mov bracecnt,0 mov comand.cmper,1 ; do not react to \%x substitutions cmp cmrptr,offset cmdbuf ; empty buffer? je cmcfr7 ; e = yes mov bx,cmrptr ; where to read next dec bx ; last read byte cmp byte ptr [bx],CR ; terminated already? je cmcfr7 ; e = yes cmcfr1: mov cmsflg,0ffh ; set space-seen flag (skip spaces) call cmgtch ; get a char push cmrptr pop temp ; remember first non-space position jc cmcfr4 ; c = terminator dec temp ; backup to text char cmcfr3: mov 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,cmrptr cmp ax,temp ; started text yet? je cmcfr1 ; e = no jmp short cmcfr3 ; try again cmcfr5: cmp ah,'?' ; curious? jne cmcfr6 ; ne = no mov cmhlp,offset cmin00 ; msg Confirm with c/r mov 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,cmrptr ; pointer to terminator mov dx,temp ; starting place sub cx,dx ; end minus starting point = length jle cmcfr7 ; le = nothing to display push dx ; save source pointer mov ah,prstr mov dx,offset cmer07 ; ?Ignoring extras 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: xor ax,ax mov errflag,al call optionclr ; clear parser options clc ; return confirmed ret 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'. Ditto for \v(variable). Returns carry clear if nothing ; done, else carry set and new text already placed in user's buffer. ; Includes \v(variable) and \$(Environment variable) and \m(macro name). ; Uses depth-first recursion algorithm. All registers preserved. subst proc near cmp comand.cmper,0 ; recognize '\%','\v(','\$(','\m(' ? jne subst0 ; ne = no, treat as literals cmp taklev,0 ; in a Take file? je subst0a ; e = no push bx mov bx,takadr cmp [bx].takper,0 ; expand macros? pop bx je subst0a ; e = yes subst0: clc ; report out to application ret subst0a:cmp subtype,'0' ; doing \numbers already? jne subst0b ; ne = no jmp subst30 ; continue to parse digits subst0b:cmp ah,'\' ; is it the first char of the pattern? jne subst1 ; ne = no, try next cmp subcnt,1 ; \\? jne subst0c ; ne = no mov subcnt,0 ; clear state and pass back one \ clc ret subst0c:cmp endedbackslash,0 ; ended sub macro on backslash? jne subst1a ; ne = yes inc subcnt ; say first char (\) is matched and subcnt,1 ; modulo 2 mov subtype,ah stc ; do not pass to application ret subst1: cmp subcnt,1 ; first char matched already? ja subst3 ; a = first two have been matched jb subst0 ; b = none yet mov al,ah ; test kind of substitution or al,20h ; convert to lower case cmp ah,'%' ; second match char, same? je subst2 ; e = yes cmp al,'v' ; \v(...)? je subst2 ; e = yes cmp al,'$' ; \$(..)? je subst2 ; e = yes cmp al,'m' ; \m(..)? je subst2 ; e = yes cmp comand.cmarray,0 ; allow array recognition? jne subst1d ; ne = no cmp al,'&' ; \&[..]? je subst2 ; e = yes subst1d: ; start \number cmp comand.cmdonum,0 ; convert \numbers allowed? je subst1c ; e = no cmp ah,'{' ; \{number}? je subst1b ; e = yes cmp al,'d' ; \Dnumber? je subst1b ; e = yes cmp al,'o' ; \Onumber? je subst1b ; e = yes cmp al,'x' ; \Xnumber? je subst1b ; e = yes cmp ah,'0' ; in range for numbers? jb subst1c ; b = no cmp ah,'9' ja subst1c ; a = no subst1b:mov subtype,'0' ; mark as numeric mov subcnt,2 ; matched second introducer push bx mov bx,cmrptr sub bx,2 ; point at \ mov numtmp,bx ; where \number starts pop bx stc ; do not pass to application ret ; end \number subst1c:cmp al,'f' ; \fname(..)? jne subst1a ; ne = no mov subtype,al ; remember type inc subcnt ; count match jmp subst10 ; read more bytes internally subst1a:push ax ; replay bytes as literals mov al,subcnt ; previously matched bytes xor ah,ah inc ax ; plus current byte sub cmrptr,ax mov replay_cnt,ax ; trailer bytes, amount to replay pop ax mov subcnt,0 ; mismatch, clear match counter mov subtype,0 mov endedbackslash,0 stc ; reread from input ret subst2: cmp al,'%' ; starting \%? jne subst2b ; ne = no cmp subtype,'v' ; doing \v(..)? (dirs of c:\%foobar) ;;; je subst1a ; e = yes, don't expand \% inside subst2b:mov subtype,al ; remember kind of substitution inc subcnt ; count match cmp al,'%' je subst10 cmp al,'&' ; doing \&[..]? je subst10 ; e = yes stc ; do not pass to application ret subst3: cmp subtype,'v' ; doing \v(..)? je subst3a ; e = yes cmp subtype,'$' ; doing \$(..)? je subst3a ; e = yes cmp subtype,'m' ; doing \m(..)? je subst3a ; e = yes mov subcnt,0 ; clear match counter jmp subst1a ; no match subst3a:cmp ah,'(' ; have leading parenthesis? jne subst1a ; ne = no, mismatch, exit jmp subst10 ; process \v(..), \$(..), \m(..) ; \fname(..), \m(..), \v(..), \$(..) subst10:push bx ; save working regs push cx push es push cmptab ; save current keyword parsing parms push cmsptr push cmsiz push valtmp push dx mov subcnt,0 ; clear match counter mov cmhlp,0 mov ax,cmrptr mov valtmp,ax ; remember current read pointer mov cmsptr,ax ; start of word mov cmptab,offset valtab ; table of keywords for \v(..) cmp subtype,'v' ; \v(..)? je subst10a ; e = yes mov cmptab,offset envtab ; Environment variable table \$(..) cmp subtype,'$' ; \$(..)? je subst10a ; e = yes mov cmptab,offset evaltab ; evaluate (name) table cmp subtype,'f' ; \fname()? je subst10a ; e = yes mov cmptab,offset mcctab ; main Macro table for \m(..) subst10a:mov cmsflg,0 ; see leading spaces/tabs mov cmsiz,0 ; word size subst11:mov ch,subtype ; save \type push cx call cmgtch ; read a character into ah rcl al,1 ; put carry bit into low bit of al pop cx xchg ch,subtype ; recover \type rcr al,1 ; recover carry bit jnc subst13 ; nc = non-terminator cmp subtype,'%' ; \%? jne subst11d ; ne = no cmp ah,' ' ; question or similar? jbe subst13 ; be = no, a funny inc cmrptr ; accept the terminator as data jmp subst13 subst11d: cmp subtype,'m' ; discard trailing spaces for these je subst11b cmp subtype,'v' je subst11b cmp subtype,'&' je subst11b cmp subtype,'$' jne subst11c subst11b:cmp ah,' ' ; space terminator? je subst11 ; e = yes, ignore it subst11c:cmp ah,'?' ; need help? jne subst11a ; ne = no call cmkyhlp ; display help information jmp short subst11 subst11a:cmp ah,escape ; escape? jne subst12 ; ne = no cmp in_showprompt,0 ; making new prompt? jne subst12 ; ne = yes, include literal escape call cmkyesc ; process escape jmp short subst11 ; failed, ignore esc, read more ; subst12:cmp subtype,'f' ; \fname(..)? je subst12a ; e = yes, else no need to replay jmp subst17 subst12a:mov bx,cmsptr ; where \fname() word started sub bx,2 ; back over "\f" part mov cx,cmrptr ; last read + 1 sub cx,bx ; bytes in \foobar mov cmrptr,bx ; reread point is the \ add replay_cnt,cx ; bytes to reread (+external \ fails) jmp subst17 subst13:inc cmsiz ; count user chars cmp subtype,'%' ; \%? jne subst13j ; ne = no cmp ah,'0' ; large enough? jb subst17 ; b = no, fail mov cmsiz,3+1 ; size is three bytes, plus dec below sub cmsptr,2 ; backup to \ jmp short subst13b subst13j:cmp subtype,'f' ; \fname(...)? jne subst13a ; ne = no cmp ah,'(' ; look for "(" as terminator je subst13b ; e = found, lookup "name" as keyword cmp ah,'0' ; numeric? jb subst12a ; b = no, end of string cmp ah,'9' jbe subst11 ; be = yes, keep reading call tolowr ; to lower case cmp ah,'z' ja subst12a ; a = non-alpha, end of string cmp ah,'a' jae subst11 ; ae = alpha, keep reading jmp short subst12a ; yes, stop here subst13a:cmp subtype,'&' ; \&c[..]? jne subst13i ; ne = no cmp ah,']' ; end bracket? jne subst11 ; ne = no, keep going jmp short subst13b ; have it subst13i:cmp ah,')' ; end bracket? jne subst11 ; ne = no, keep looking subst13b:dec cmsiz ; omit user's ')' from tests cmp subtype,'&' ; \&char[..]? je subst20 ; e = yes, have all bytes cmp subtype,'$' ; \$(..)? je subst13c ; e = yes, no keyword in table push cmsptr ; save pointer subst13g:mov bx,cmsptr cmp byte ptr [bx],' ' ; leading spaces? jne subst13h ; ne = no inc cmsptr ; look at next char jmp short subst13g subst13h:call getkw ; \m(..) and \v(..) test for keyword pop cmsptr jc subst12 ; c = failure jmp short subst13d ; success subst13c:call envvar ; search Environment for the word jc subst12 ; c = failure mov bx,V_environ ; set bx to kind of value for valtoa jmp short subst13f subst13d:cmp subtype,'f' ; doing \fname(...)? jne subst13f ; ne = no mov subcnt,0 ; clear \f match indicator add bx,[bx] ; add length of keyword text (CNT) add bx,2 ; point at value field mov bx,[bx] ; get value subst13e:mov evaltype,bx ; remember kind of operation call evaltoa ; do \fname argument evaluation jc subst12a ; error, reparse jmp subst17 subst13f:mov ax,valtmp ; where word started sub ax,3 ; backup to "\v(" or "\$(" cmp subtype,'%' ; doing \%? jne subst13k ; ne = no inc ax ; one less field byte than others subst13k:mov cmrptr,ax ; write output where backslash was call bufreset ; resets cmwptr too xor dx,dx ; signal valtoa to not add trailing sp cmp subtype,'$' ; \$(..)? je subst14 ; e = yes, no keyword structure mov cx,[bx] ; bx = structure pointer, cx=keyw len add cx,2 ; skip count byte add bx,cx ; point at 16 bit value field mov bx,[bx] ; get value to bx for valtoa subst14:cmp taklev,maxtak ; room in take level? jb subst15 ; b = yes mov dx,offset stkmsg ; out of work space msg mov ah,prstr ; display error message int dos jmp subst17 ; \&char[..] subst20:mov ax,valtmp ; where started mov bx,ax cmp byte ptr [bx+1],'[' jne subst12a sub ax,2 ; backup to "\&[" mov cmrptr,ax ; write output where backslash was add cmsptr,2 ; point after "[" for index math call bufreset ; resets cmwptr too push si mov si,cmsptr ; start of string after "[" mov cx,1024 ; cmsiz ignores leading spaces subst20a:cmp byte ptr [si],' ' ; remove leading spaces jne subst20b inc si loop subst20a subst20b:push si ; save starting point mov cx,1024 ; assumed max string in [..] xor bx,bx cld subst20c:lodsb cmp al,']' ; terminator? je subst20e ; e = yes inc bx ; count string chars subst20d:loop subst20c subst20e:pop si ; recover starting point push si mov domath_ptr,si ; ptr to string mov domath_cnt,bx ; length of string call domath ; string to binary in dx:ax pop si mov si,cmsptr ; points just after "\&[" mov bl,[si-2] ; back up to cmp bl,'_' ; arg list \$_[list element]? jne subst20g ; ne = no pop si ; clean stack cmp ax,9 ; too large? ja subst17 ; a = yes, do nothing push ax mov ax,4+2 ; want four bytes plus count call malloc ; to seg in ax mov es,ax pop ax mov word ptr es:[2],'%\' ; compose string \% add al,'0' ; use index of 0..9 xor ah,ah mov word ptr es:[4],ax ; null terminate mov cx,3 mov word ptr es:[0],cx ; three bytes of text mov ax,es ; setup for below jmp short subst20h ; compose the macro subst20g:and bl,not 20h ; upper case it cmp bl,'Z' ; last ja subst20f ; a = out of range sub bl,'@' ; remove bias xor bh,bh shl bx,1 ; address words mov si,marray[bx] ; get segment of string storage or si,si ; any? jz subst20f ; z = none mov es,si ; look at segment of array cmp es:[0],ax ; number of elements vs index above jbe subst20f ; be = out of range, quit mov si,ax shl si,1 ; index words mov ax,es:[si+2] ; get definition string segment to ax pop si or ax,ax ; any? jz subst17 ; z = no, empty subst20h:mov es,ax ; string seg mov cx,es:[0] ; length of definition jcxz subst17 ; z = empty call takopen_sub ; open take as text substitution jc subst17 ; c = cannot open mov bx,takadr ; pointer to new Take structure mov [bx].takbuf,es ; segment of Take buffer mov [bx].takcnt,cx ; number of unread bytes jmp subst17 subst20f:pop si ; failure jmp subst17 subst15:mov subcnt,0 ; clear match indicator call takopen_sub ; open take as text substitution jc subst17 ; c = failed mov cx,bx ; value command kind, save mov ax,tbufsiz ; bytes of buffer space wanted call malloc jc subst17 ; c = failed mov bx,takadr ; point to structure or [bx].takattr,take_malloc ; remember to dispose via takclos mov [bx].takbuf,ax ; seg of buffer mov es,ax ; ES:DI will be buffer pointer mov di,[bx].takptr ; where to write next mov [bx].takper,0 ; expand macros cmp subtype,'m' ; \m(..)? jne subst16a ; ne = no mov [bx].takper,1 ; do not expand macros in \m(..) subst16a:mov bx,cx ; value command call valtoa ; make text be an internal macro mov bx,takadr mov [bx].takcnt,di ; length subst17: pop dx mov subtype,0 pop valtmp pop cmsiz ; restore borrowed keyword parameters pop cmsptr pop cmptab pop es pop cx pop bx stc ; carry = signal reread source ret ; convert \number subst30:cmp ah,'?' ; asking for help? jne subst30a ; ne = no mov cmsiz,0 ; no keyword to expand push cmhlp ; save existing help mov cmhlp,offset n1hlp ; our message call cmkyhlp ; display help message pop cmhlp ; restore old help stc ; buffer has been cleaned by cmkyhlp ret subst30a:push si mov si,numtmp ; where \ starts mov atoibyte,1 ; convert only one character mov cx,cmrptr sub cx,numtmp ; byte count to examine mov atoi_cnt,cx ; tell atoi the count call atoi ; value to dx:ax mov cmsflg,0 ; clear space-seen flag jnc subst31 ; nc = converted a value cmp atoi_err,4 ; insufficient bytes to resolve je subst30b ; e = yes, get more mov cx,cmrptr ; replay complete failure sub cx,numtmp ; where \ started add replay_cnt,cx ; count to replay mov si,numtmp mov cmrptr,si ; replay from here mov subcnt,0 ; mismatch, clear match counter mov subtype,0 ; and substitution type mov endedbackslash,0 ; general principles subst30b:pop si stc ; carry set to read more bytes ret subst31:pop si cmp atoi_err,1 ; success and terminated? jne subst31a ; ne = no mov ah,al ; ah now has value for app mov subtype,0 mov subcnt,0 inc report_binary ; signal have number to report to app clc ret subst31a:cmp atoi_err,0 ; success, can accept more data? jne subst32 ; ne = no stc ; c = return to read more data ret subst32:mov ah,al ; ah now has value for app mov subtype,0 mov subcnt,0 push ax dec cmrptr ; reread break byte mov replay_cnt,1 ; for non-\ push bx mov bx,cmrptr ; last read byte mov ah,[bx] ; get break byte pop bx inc report_binary ; signal have number to report to app cmp ah,'\' ; substitution introducer? jne subst34 ; ne = no, pass it to app inc cmrptr ; step over read byte mov replay_cnt,0 ; no replay mov subcnt,1 ; mark \ introducer as have been read subst34:pop ax clc ret subst endp ; Make an internal macro defined as the text for one of the value variables. ; Use incoming DX as trailing space suppression flag, if null. valtoa proc near push di ; save starting di push dx ; save trailing space flag mov word ptr es:[di],0 ; fill buffer with sweet nothings ; BX has index of variable cmp bx,V_environ ; \$() Environment? jne valtoa1 ; ne = no mov cx,envlen ; string length jcxz valtoa0 ; z = empty cmp cx,tbufsiz-2 ; greater than current buffer? jbe valtoa0a ; be = no push cx mov ax,[bx].takbuf ; old buffer mov es,ax ; new ES from above mov ah,freemem ; free it int dos mov ax,cx ; bytes wanted call malloc ; get more space mov bx,takadr mov [bx].takbuf,ax ; seg of macro def mov ES,ax ; new ES or [bx].takattr,take_malloc ; remember to dispose via takclos pop cx valtoa0a: push si push ds lds si,envadr ; ds:si is source from Environment cld rep movsb ; copy string pop ds pop si valtoa0:jmp valtoa90 valtoa1:cmp bx,V_argc ; \v(argc)? jne valtoa2 ; ne = no call wrtargc ; write argc jmp valtoa90 valtoa2:cmp bx,V_count ; \v(count)? jne valtoa3 ; ne = no call wrtcnt ; write it jmp valtoa90 valtoa3:cmp bx,V_date ; \v(date)? jne valtoa4 call wrtdate jmp valtoa90 valtoa4:cmp bx,V_errlev ; \v(errorlevel)? jne valtoa5 ; ne = no call wrterr jmp valtoa90 valtoa5:cmp bx,V_dir ; \v(dir)? jne valtoa6 call wrtdir jmp valtoa90 valtoa6:cmp bx,V_time ; \v(time)? jne valtoa7 call wrttime jmp valtoa90 valtoa7:cmp bx,V_version ; \v(version)? jne valtoa8 ; ne = no mov ax,version ; get version such as 300 call fdec2di ; convert binary to asciiz jmp valtoa90 valtoa8:cmp bx,V_platform ; \v(platform)? jne valtoa9 ; ne = no call wrtplat ; get machine name, e.g. "IBM-PC" jmp valtoa90 valtoa9:cmp bx,V_system ; \v(system)? jne valtoa10 ; ne = no call wrtsystem ; get "MS-DOS" string jmp valtoa90 valtoa10:cmp bx,V_kbd ; \v(keyboard)? jne valtoa11 ; ne = no call wrtkbd ; 88 or 101 value jmp valtoa90 valtoa11:cmp bx,V_speed ; \v(speed)? jne valtoa12 ; ne = no push di call fgetbaud ; read baud rate from hardware pop di mov bx,portval mov ax,[bx].baud cmp al,byte ptr bdtab ; index versus number of table entries jb valtoa11a ; b = index is in the table mov si,offset cmunk-2 ; unrecognized value, say "unknown" mov bx,7 ; length of string jmp short valtoa11c valtoa11a:mov si,offset bdtab ; ascii rate table mov cl,[si] ; number of entries inc si ; point to an entry valtoa11b: mov bx,[si] ; length of text string cmp ax,[si+bx+2] ; our index vs table entry index je valtoa11c ; e = match add si,bx ; skip text add si,4 ; skip count and index word loop valtoa11b ; look again mov si,offset cmunk-2 ; unrecognized value, say "unknown" mov bx,7 ; length of string valtoa11c:mov cx,bx ; length of string add si,2 ; point at string rep movsb ; copy string jmp valtoa90 valtoa12:cmp bx,V_program ; \v(program)? jne valtoa13 ; ne = no call wrtprog ; get "MS-DOS_KERMIT" string jmp valtoa90 valtoa13:cmp bx,V_status ; \v(status)? jne valtoa14 ; ne = no call wrtstat ; compose status string jmp valtoa90 valtoa14:cmp bx,V_ndate ; \v(ndate)? jne valtoa15 ; ne = no call wrtndate jmp valtoa90 valtoa15:cmp bx,V_port ; \v(port)? or \v(line)? jne valtoa16 ; ne = no call wrtport jmp valtoa90 valtoa16:cmp bx,V_terminal ; \v(terminal)? jne valtoa17 ; ne = no call wrtterm jmp valtoa90 valtoa17: ifndef no_tcp cmp bx,V_session ; \v(session) (internal Telnet)? jne valtoa18 ; ne = no mov ax,sescur ; get internal Telnet session ident inc ax ; count from 1 for users (0 == none) call fdec2di ; convert binary to asciiz jmp valtoa90 endif ; no_tcp valtoa18:cmp bx,V_ntime ; \v(ntime) (seconds in day)? jne valtoa19 ; ne = no mov ah,gettim ; get DOS time of day int dos ; ch=hh, cl=mm, dh=ss, dl=0.01 sec mov bx,60 mov al,ch ; hours mul bl ; to minutes xor ch,ch add ax,cx ; plus minutes mov cl,dh ; preserve seconds mul bx ; need carry out to DX add ax,cx ; add seconds adc dx,0 push di push es mov di,ds mov es,di mov di,offset cmdbuf-30-cmdblen call flnout ; 32 bit converter mov dx,offset cmdbuf-30-cmdblen call strlen mov si,dx pop es pop di cld rep movsb mov word ptr [di],0020h ; space, null jmp valtoa90 valtoa19:cmp bx,V_dosver ; \v(dosversion)? jne valtoa20 ; ne = no mov ax,dosnum ; DOS verson, major high, minor low push ax xchg ah,al xor ah,ah call fdec2di ; write major pop ax xor ah,ah cmp al,10 ; less than 10? ja valtoa19a ; a = no mov byte ptr es:[di],'0' ; use two digits for minor inc di valtoa19a: call fdec2di ; write minor jmp valtoa90 valtoa20: ifndef no_tcp cmp bx,V_tcpip ; \v(tcp_status)? jne valtoa21 ; ne = no ; SUCCESS 0 ; NO_DRIVER 1 ; NO_LOCAL_ADDRESS 2 ; BOOTP_FAILED 3 ; RARP_FAILED 4 ; BAD_SUBNET_MASK 5 ; SESSIONS_EXCEEDED 6 ; HOST_UNKNOWN 7 ; HOST_UNREACHABLE 8 ; CONNECTION_REJECTED 9 mov ax,tcp_status ; get tcp status, if any call fdec2di ; write value jmp valtoa90 endif ; no_tcp valtoa21:cmp bx,V_parity ; \v(parity)? jne valtoa22 ; ne = no mov bx,portval mov bl,[bx].parflg ; parity xor bh,bh shl bx,1 ; address words cld mov si,parmsgtab[bx] ; offset of parity name string valtoa21a:lodsb stosb or al,al ; end of string? jnz valtoa21a ; nz = no mov word ptr es:[di-1],0020h ; space, null jmp valtoa90 valtoa22:cmp bx,V_carrier ; \v(carrier)? jne valtoa23 ; ne = no mov bl,flags.carrier ; carrier and bl,1 ; just one bit xor bh,bh shl bx,1 ; address words cld mov si,onoffmsgtab[bx] ; offset of carrier string valtoa22a:lodsb stosb or al,al ; end of string? jnz valtoa22a ; nz = no mov word ptr es:[di-1],0020h ; space, null jmp valtoa90 valtoa23:cmp bx,V_prompt ; \v(prompt)? jne valtoa24 ; ne = no push si mov si,prmptr ; current prompt raw text cld valtoa23a:lodsb ; read a byte stosb ; store or al,al ; end of string? jnz valtoa23a ; z = no pop si dec di ; don't show trailing null mov word ptr es:[di],0020h ; space, null jmp valtoa90 valtoa24:cmp bx,V_cps ; \v(cps)? jne valtoa25 ; ne = no push di mov di,offset decbuf+200 ; must be in DS data seg call shovarcps ; use worker in msssho.asm mov si,offset decbuf+200 ; copy this buffer to es:di buffer pop di cld mov cx,7 ; limit loop valtoa24a:lodsb ; read result or al,al ; terminator? jz valtoa24b ; z = yes, stop stosb loop valtoa24a valtoa24b:jmp valtoa90 valtoa25:cmp bx,V_space ; \v(space)? jne valtoa26 ; ne = no xor cx,cx ; drive letter (null means current) call fdskspace ; compute space, get letter into CL jnc valtoa25a ; nc = success xor ax,ax xor dx,dx valtoa25a: push di push es mov di,ds mov es,di mov di,offset cmdbuf-30-cmdblen call flnout mov dx,offset cmdbuf-30-cmdblen call strlen mov si,dx pop es pop di cld rep movsb mov word ptr [di],0020h ; space, null jmp valtoa90 valtoa26:cmp bx,V_startup ; \v(startup)? jne valtoa27 ; ne = no push si mov si,offset startup ; startup directory string mov dx,si call strlen ; get length to cx rep movsb ; copy string pop si mov word ptr es:[di],0020h ; space, null jmp valtoa90 valtoa27:cmp bx,V_console ; \v(console)? jne valtoa28 ; ne = no mov ax,1a00h ; get ANSI.SYS installed state int 2fh mov si,offset ansiword ; assume installed or al,al ; installed? jnz valtoa27a ; nz = yes mov si,offset noneword ; say "NONE" valtoa27a:lodsb ; read a byte stosb ; store a byte or al,al ; at end of string? jnz valtoa27a ; nz = no mov word ptr es:[di-1],0020h ; space, null jmp valtoa90 valtoa28:cmp bx,V_monitor ; \v(monitor)? jne valtoa29 ; ne = no mov si,offset colormsg ; assume color monitor cmp crt_mode,7 ; mono text jne valtoa28a ; ne = no mov si,offset monomsg ; say mono valtoa28a:jmp short valtoa27a ; copy material valtoa29:cmp bx,V_cmdlevel ; \v(cmdlevel)? jne valtoa30 ; ne = no mov al,taklev ; take level xor ah,ah call fdec2di ; write value jmp valtoa90 valtoa30:cmp bx,V_input ; \v(input)? jne valtoa31 ; ne = no push temp push si push DS push es call buflog ; get INPUT buffer pointers mov ax,es ; seg of input buffer pop es jcxz valtoa30b ; z = empty cmp cx,tbufsiz-2-1 ; buffer plus null terminator jbe valtoa30a ; be = enough space to hold text mov cx,tbufsiz-2-1 ; limit text valtoa30a:mov DS,ax ; ds:si is input buffer cld rep movsb xor al,al mov es:[di],al ; null terminator valtoa30b:pop DS pop si pop temp jmp valtoa90 valtoa31:cmp bx,V_query ; \v(query)? jne valtoa32 ; ne = no mov word ptr es:[di],0020h ; space, null cmp queryseg,0 ; is there an malloc'd seg to use? je valtoa90 ; e = no push es mov si,queryseg mov es,si mov cx,es:[0] ; get length of string pop es push si cmp cx,tbufsiz-2 ; greater than current buffer? jbe valtoa31a ; be = no push cx mov ax,[bx].takbuf ; old buffer mov es,ax ; new ES from above mov ah,freemem ; free it int dos mov ax,cx ; bytes wanted call malloc ; get more space mov bx,takadr mov [bx].takbuf,ax ; seg of macro def mov ES,ax ; new ES or [bx].takattr,take_malloc ; remember to dispose via takclos pop cx valtoa31a:push ds mov si,queryseg ; get segment of query string mov ds,si mov si,2 ; skip count word cld rep movsb ; copy to es:di pop ds pop si jmp valtoa90 valtoa32:cmp bx,V_sysid ; \v(sysid)? jne valtoa33 ; ne = no mov ax,'8U' ; always report U8 stosw jmp valtoa90 valtoa33:cmp bx,V_charset ; \v(charset)? jne valtoa34 ; ne = no mov ax,'PC' stosw ; CP prefix mov ax,flags.chrset ; get current Code Page call fdec2di jmp valtoa90 valtoa34:cmp bx,V_inpath ; \v(inpath)? jne valtoa35 ; ne = no mov si,inpath_seg ; get segment of in_path string or si,si ; anything there? jz valtoa34b ; z = no push ds mov ds,si xor si,si cld lodsw ; get count word mov cx,ax cmp cx,64 ; keep it reasonable jbe valtoa34a ; be = ok mov cx,64 valtoa34a:rep movsb ; copy to es:di dec di ; don't include trailing null from env pop ds valtoa34b:jmp valtoa90 valtoa35:cmp bx,V_disk ; \v(disk)? jne valtoa36 ; ne = no mov ah,gcurdsk ; get current disk int dos add al,'A' ; make 1 == A (not zero) cld stosb xor al,al mov es:[di],al jmp valtoa90 valtoa36:cmp bx,V_cmdfile ; \v(cmdfile)? jne valtoa37 ; ne = no mov si,offset cmdfile ; path of last Take file mov dx,si call strlen cld rep movsb jmp valtoa90 valtoa37:cmp bx,V_inidir ; \v(inidir)? jne valtoa38 ; ne = no mov si,offset inidir ; path of mskermit.ini mov dx,si call strlen cld rep movsb jmp valtoa90 valtoa38:cmp bx,V_instatus ; \v(instatus)? jne valtoa39 ; ne = no mov ax,input_status ; special INPUT status word cmp ax,0 ; negative? jge valtoa38a ; ge = no mov byte ptr es:[di],'-' inc di neg ax valtoa38a:call fdec2di jmp valtoa90 valtoa39:cmp bx,V_minput ; \v(minput)? jne valtoa40 ; ne = no mov ax,minpcnt ; get minput match count call fdec2di ; convert to decimal jmp short valtoa90 ; done valtoa40:cmp bx,V_return ; \v(return)? jne valtoa41 ; ne = no mov si,offset retbuf ; mov cx,[si] ; get count word add si,2 ; point at string cld rep movsb ; copy to variable buffer jmp short valtoa90 ; done valtoa41:cmp bx,V_connection ; \v(connection)? jne valtoa42 ; ne = no mov si,offset connmsg ; word "local" mov dx,si call strlen ; length to cx cld rep movsb ; copy to variable buffer jmp short valtoa90 ; done valtoa42:cmp bx,V_filespec ; \v(filespec)? jne valtoa80 ; ne = no mov si,offset vfile ; last used file transfer name mov dx,si call strlen ; length to cx cld rep movsb ; copy to variable buffer jmp short valtoa90 ; done valtoa80:push bx ; \m(macro_name) push es mov di,bx ; save seg of macro def mov bx,takadr test [bx].takattr,take_malloc ; buffer already allocated? jz valtoa80a ; z = no push es mov ax,[bx].takbuf ; old buffer or ax,ax ; if any allocated jz valtoa80b ; z = none mov es,ax mov ah,freemem ; free it int dos valtoa80b:and [bx].takattr,not take_malloc ; say no more freeing needed pop es valtoa80a: mov [bx].takbuf,di ; seg of macro def mov [bx].takptr,2 ; offset of two mov es,di mov cx,es:[0] ; get length of string pop es pop bx pop dx pop di mov di,cx ; report length in di clc ret valtoa90:pop dx ; trailing space flag or dx,dx ; leave the spaces? jnz valtoa91 ; nz = yes cmp word ptr es:[di-1],0020h ; trailing space? jne valtoa91 ; ne = no dec di ; remove space valtoa91:pop ax ; saved starting di sub di,ax ; di = length of the buffer contents clc ret valtoa endp ; Far callable version fvaltoa proc far call valtoa ret fvaltoa endp ; Make an internal macro defined as the text for one of the value variables. ; Use incoming DX as trailing space suppression flag, if null. ; BX has keyword value (table envtab) ; If these fail they consume their text quietly. evaltoa proc near push dx ; save trailing space flag push si push temp ; save work variable mov al,comand.cmdonum push ax mov comand.cmdonum,0 ; kill \number conversion call evarg ; get argument array pop ax mov comand.cmdonum,al ; restore \number conversion state mov ax,valtmp ; where '\fname' started sub ax,2 ; back over \f mov cmwptr,ax ; where to write new output mov cmrptr,ax ; readjust read pointer too call takopen_sub ; open take as text substitution jnc evalt5c ; nc = success evalt5b:jmp evalt99 ; fail evalt5c:mov bx,takadr ; Take structure mov ax,tbufsiz ; bytes of buffer space wanted call malloc jc evalt5b ; c = failed mov [bx].takbuf,ax ; seg of allocated buffer or [bx].takattr,take_malloc ; remember to dispose via takclos mov es,ax mov word ptr es:[0],tbufsiz mov di,[bx].takptr ; where to start writing mov [bx].takcnt,0 ; number of unread bytes mov [bx].takper,0 cmp evaltype,F_contents ; \fcontents(macro)? je evalt5d ; e = yes cmp evaltype,F_definition ; \fdefinition(macro)? je evalt5d ; e = yes cmp evaltype,F_literal ; \fliteral(string)? jne evalt5e ; ne = no evalt5d:mov [bx].takper,1 ; treat macro names as literals evalt5e:mov bx,takadr mov di,[bx].takptr ; destination for replacment text cmp evaltype,F_length ; \flength(text)? jne evalt6 ; ne = no mov ax,arglen ; length of variable name call fdec2di ; convert to ASCII string mov bx,takadr sub di,[bx].takptr ; end minus start mov [bx].takcnt,di ; string length jmp evalt99 evalt6: cmp evaltype,F_upper ; \fupper(text)? je evalt6a ; e = yes cmp evaltype,F_lower ; \flower(text)? jne evalt7 ; ne = no evalt6a:mov si,argptr ; point to argument mov cx,arglen ; length of string jcxz evalt6e ; z = empty cld evalt6b:lodsb ; read from parse buffer cmp evaltype,F_lower ; to lower? ja evalt6c ; a = no, upper call tolowr ; move to lower case jmp short evalt6d evalt6c:call toupr ; move to upper case evalt6d:stosb ; write byte to Take buffer loop evalt6b evalt6e:mov bx,takadr mov cx,arglen ; argument length mov [bx].takcnt,cx ; count entered bytes jmp evalt99 evalt7: cmp evaltype,F_char ; \fcharacter(n)? jne evalt8 ; ne = no mov si,argptr ; point at text of number mov domath_ptr,si mov ax,arglen mov domath_cnt,ax call domath jc evalt7d ; c = failed cld stosb ; store single byte mov bx,takadr mov [bx].takcnt,1 ; one byte as result evalt7d:jmp evalt99 evalt8: cmp evaltype,F_substr ; \fsubstr(text, n1, n2)? jne evalt9 ; ne = no xor ax,ax mov si,argptr[2] ; point to n1 string mov domath_ptr,si mov ax,arglen[2] mov domath_cnt,ax call domath jnc evalt8g ; nc = got a number evalt8f:xor ax,ax xor dx,dx jmp short evalt8a evalt8g:or dx,dx ; number overflowed to high word? jnz evalt8f ; nz = yes, reset number to zero or ax,ax ; zero? jz evalt8a ; z = yes dec ax ; count from 0 internally evalt8a:mov si,argptr ; point at text add si,ax ; point at text[n1] mov cx,arglen ; length of 'text' sub cx,ax ; bytes remaining in 'text' jle evalt8d ; le = nothing left to extract push si mov si,argptr[4] ; point to n2 string mov domath_ptr,si mov ax,arglen[4] mov domath_cnt,ax call domath pop si jnc evalt8b ; nc = got a number mov ax,arglen ; default to full string xor dx,dx evalt8b:or dx,dx ; number overflowed to high word? jnz evalt8d ; nz = yes cmp cx,ax ; length available more than needed? jbe evalt8c ; be = no mov cx,ax ; use smaller n2 evalt8c:mov bx,tbufsiz-2 ; unused buffer capacity (tbufsiz-2) cmp cx,bx ; n larger than buffer? jbe evalt8e ; be = no mov ax,cx ; remember amount wanted call evmem ; get more memory, want CX bytes xchg ax,cx ; now ax=amount available, cx=wanted cmp cx,ax ; wanted > available? jbe evalt8e ; be = no mov cx,ax ; limit to available memory evalt8e:mov bx,takadr mov [bx].takcnt,cx ; count of bytes cld rep movsb ; copy bytes to Take buffer evalt8d:jmp evalt99 evalt9: cmp evaltype,F_right ; \fright(text, n)? jne evalt10 ; ne = no mov si,argptr[2] ; point to n string mov domath_ptr,si mov ax,arglen[2] or ax,ax ; empty field? jz evalt9d ; z = yes, use whole string mov domath_cnt,ax call domath cmp domath_cnt,0 je evalt9a ; e = consummed whole string mov arglen,0 ; force 0 evalt9d:mov ax,arglen ; default to full string xor dx,dx evalt9a:or dx,dx ; number overflowed to high word? jnz evalt9c ; nz = yes mov cx,arglen ; length of 'text' cmp cx,ax ; length available more than needed? jbe evalt9b ; be = no mov cx,ax ; use smaller n evalt9b:mov si,argptr ; start of 'text' add si,arglen ; point at last + 1 byte of 'text' sub si,cx ; minus bytes to be copied mov bx,takadr mov [bx].takcnt,cx ; count of bytes cld rep movsb ; copy bytes to Take buffer evalt9c:jmp evalt99 evalt10:cmp evaltype,F_literal ; \fliteral(text)? jne evalt11 ; ne = no mov si,argptr ; start of string mov cx,arglen ; length of text (inc leading spaces) mov bx,takadr mov [bx].takcnt,cx ; count of bytes cld rep movsb ; copy bytes to Take buffer jmp evalt99 evalt11:cmp evaltype,F_rpad ; \frpad(text, n, c)? jne evalt12 mov si,argptr[2] ; get n mov domath_ptr,si mov ax,arglen[2] mov domath_cnt,ax call domath jc evalt11d ; c = error or dx,dx ; numeric overflow to high word? jne evalt11d ; ne = yes mov bx,takadr mov cx,tbufsiz-2 ; unused buffer capacity (tbufsiz-2) cmp ax,cx ; n larger than buffer? jbe evalt11a ; be = no call evmem ; get more memory jc evalt11d ; c = fail cmp ax,cx ; enough? jbe evalt11a ; be = yes mov ax,cx evalt11a:mov cx,arglen ; length of text cmp ax,cx ; field (n) shorter than text? jae evalt11b ; ae = no, use full text mov cx,ax ; copy just n of text evalt11b:sub ax,cx ; available minus used bytes mov bx,takadr mov [bx].takcnt,cx ; count of bytes mov si,argptr ; text cld rep movsb ; copy bytes to Take buffer mov cx,ax ; padding bytes jcxz evalt11d ; z = none add [bx].takcnt,cx ; increase count of bytes mov bx,argptr+4 ; point to c mov al,[bx] ; padding character, if any cmp arglen+4,0 ; any char given? jne evalt11c ; ne = yes mov al,' ' ; else default to space evalt11c:cld rep stosb ; append byte to Take buffer evalt11d:jmp evalt99 evalt12:cmp evaltype,F_lpad ; \flpad(text, n, c)? jne evalt13 push si mov si,argptr[2] ; get n mov domath_ptr,si mov ax,arglen[2] mov domath_cnt,ax call domath pop si jc evalt12e ; c = error or dx,dx ; numeric overflow to high word? jne evalt12e ; ne = yes mov bx,takadr mov cx,tbufsiz-2 ; unused buffer capacity (tbufsiz-2) cmp ax,cx ; n larger than buffer? jbe evalt12a ; be = no call evmem ; get more memory cmp ax,cx ; wanted > available? jbe evalt12a ; be = no mov ax,cx ; limit text evalt12a:sub ax,arglen ; n - length of 'text' jns evalt12b ; ns = no underflow xor ax,ax ; else omit padding evalt12b:mov cx,ax ; padding count mov [bx].takcnt,cx ; count of bytes mov bx,argptr+4 ; point to c mov al,[bx] ; padding character, if any cmp arglen+4,0 ; any char given? jne evalt12c ; ne = yes mov al,' ' ; default to space evalt12c:cld rep stosb ; copy byte to Take buffer mov cx,arglen ; length of text mov cx,tbufsiz-2 ; buffer capacity mov bx,takadr sub cx,[bx].takcnt ; minus bytes written so far mov ax,arglen ; length of 'text' cmp ax,cx ; n larger than buffer? jbe evalt12d ; be = no mov ax,cx evalt12d:mov cx,ax ; append count mov bx,takadr add [bx].takcnt,cx ; increase count of bytes mov si,argptr ; start of text cld rep movsb ; append byte to Take buffer evalt12e:jmp evalt99 evalt13:cmp evaltype,F_code ; \fcode(char)? jne evalt14 ; ne = no mov bx,argptr ; point to char address mov al,[bx] ; original char xor ah,ah call fdec2di ; convert to ASCII string, no '\' mov bx,takadr ; prefix sub di,[bx].takptr ; end minus start mov [bx].takcnt,di ; string length jmp evalt99 evalt14:cmp evaltype,F_definition ; \fdefinition(macro)? je evalt14i ; e = yes cmp evaltype,F_contents ; \fcontents(macro) jne evalt15 evalt14i:cmp arglen,0 ; any name? jne evalt14a ; e = yes jmp evalt14x ; fail evalt14a:mov si,argptr ; trim trailing spaces from user name mov cx,arglen ; length of raw user string mov bx,si add bx,cx evalt14j:cmp byte ptr [bx-1],' ' ; trailing space? jne evalt14k ; ne = no dec arglen ; remove it dec bx ; preceeding char loop evalt14j evalt14k: mov si,offset mcctab ; table of macro names cld lodsb mov cl,al ; number of macro entries xor ch,ch jcxz evalt14x ; z = none evalt14b:push cx ; save loop counter lodsw ; length of macro name mov cx,arglen ; length of user's string cmp ax,cx ; mac name shorter that user spec? jb evalt14d ; b = yes, no match push ax push si ; save these around match test push di mov di,argptr ; user's string evalt14c:mov ah,[di] inc di lodsb ; al = mac name char, ah = user char and ax,not 2020h ; clear bits (uppercase chars) cmp ah,al ; same? loope evalt14c ; while equal, do more pop di pop si ; restore regs pop ax je evalt14e ; e = a match evalt14d:add si,ax ; point to next name, add name length add si,2 ; and string pointer pop cx ; recover loop counter loop evalt14b ; one less macro to examine evalt14x:jmp evalt99 evalt14e:pop cx ; pop loop counter above cmp byte ptr[si],0 ; name starts with null char? jne evalt14f ; ne = no jmp evalt99 ; yes, TAKE file, ignore evalt14f:mov ax,[si-2] ; length of macro name add si,ax ; skip over name push es mov es,[si] ; segment of string structure xor si,si ; es:si = address of count + string mov ax,es mov cx,es:[si] ; length of string mov arglen+2,cx ; save length here mov argptr+2,ax ; save es of es:si here pop es call evmem ; get new memory, set new ES:DI cmp cx,arglen+2 ; enough to hold material? jae evalt14h ; ae = yes mov arglen+2,cx ; shrink string size too evalt14h:push ds mov bx,takadr mov cx,arglen+2 ; length of string mov [bx].takcnt,cx ; macro length mov [bx].takptr,2 ; offset of text in destination mov ax,argptr+2 ; seg of string mov ds,ax mov si,2 ; offset of string cld rep movsb ; copy string pop ds jmp evalt99 evalt15:cmp evaltype,F_maximum ; \fmaximum(n1, n2)? je evalt15a ; e = yes cmp evaltype,F_minimum ; \fminimum(n1, n2)? jne evalt16 ; ne = no evalt15a:push si mov si,argptr[0] ; point to n1 string mov domath_ptr,si mov ax,arglen[0] mov domath_cnt,ax call domath pop si jc evalt15x ; c = failure push ax ; save result push dx push si mov si,argptr[2] ; point to n2 string mov domath_ptr,si mov ax,arglen[2] mov domath_cnt,ax call domath pop si pop cx ; high first value pop bx ; low first value jc evalt15x ; c = failure mov temp,0 ; assume reporting n1, subscript 0 cmp cx,dx ; n1 > n2? jg evalt15e ; g = yes jl evalt15d ; l = no, but less than cmp bx,ax ; low order n1 >= n2? jge evalt15e ; ge = yes evalt15d:mov temp,2 ; say n2 is larger evalt15e:cmp evaltype,F_maximum ; return max of the pair? je evalt15f ; e = yes xor temp,2 ; change subscript evalt15f:push si mov bx,temp ; get subscript of reportable mov si,argptr[bx] mov cx,arglen[bx] cld rep movsb ; copy original string xor al,al stosb ; and null terminate pop si mov bx,takadr sub di,[bx].takptr ; bytes consumed mov [bx].takcnt,di evalt15x:jmp evalt99 evalt16:cmp evaltype,F_index ; \findex(pat, string, [offset]) je evalt16common cmp evaltype,F_rindex ; \frindex(pat, string, [offset]) jne evalt17 evalt16common: ; used for \frindex too cmp arglen+4,0 ; optional offset given? je evalt16a ; e = no, use userlevel 1 push si mov si,argptr[4] ; evaluate optional offset mov domath_ptr,si mov ax,arglen[4] mov domath_cnt,ax call domath pop si jnc evalt16b ; nc = got a number evalt16a:mov ax,1 ; default to one (user style) xor dx,dx evalt16b:or dx,dx ; too large? jnz evalt16x ; nz = too large, fail dec ax ; offset from start (0 base it) mov temp,ax ; remember offset cmp evaltype,F_rindex ; \frindex? jne evalt16g ; ne = no mov cx,arglen+2 ; length of string sub cx,arglen ; minus length of pattern sub cx,ax ; minus displacement from right mov temp,cx ; new effective offset mov ax,cx evalt16g: push es push di mov di,ds ; string is in DS seg mov es,di mov cx,arglen+2 ; length of string sub cx,arglen ; minus length of pattern cmp ax,cx ; offset larger than this? jbe evalt16c ; be = no mov temp,-1 ; to report zero jmp short evalt16e ; fail evalt16c:mov si,argptr ; pattern mov di,argptr+2 ; string add di,temp ; plus offset mov cx,arglen ; pattern length mov ax,cx add ax,temp ; plus offset cmp ax,arglen+2 ; more than string? jbe evalt16d ; be = no, keep going mov temp,-1 ; to report zero jmp short evalt16e ; a = no match evalt16d:push cx cld repe cmpsb ; compare strings pop cx je evalt16e ; e = matched inc temp ; move pattern right one place cmp evaltype,F_rindex ; \frindex? jne evalt16c ; ne = no sub temp,2 ; work to the left instead jmp short evalt16c ; try again evalt16e:pop di pop es mov ax,temp ; report out number inc ax ; show 0 as 1 to user mov bx,takadr call fdec2di ; binary to ASCIIZ mov bx,takadr sub di,[bx].takptr mov [bx].takcnt,di ; length of ASCIIZ result evalt16x:jmp evalt99 evalt17:cmp evaltype,F_repeat ; \frepeat(text,n)? jne evalt18 ; ne = no cmp arglen+2,0 ; number given? je evalt17g ; e = no, fail mov si,argptr[2] ; n mov domath_ptr,si mov ax,arglen[2] mov domath_cnt,ax call domath jc evalt17g ; c = fail or dx,dx ; number overflowed? jz evalt17a ; z = no mov ax,1 ; default number evalt17a:mov arglen+2,ax ; save reasonable value here mul arglen ; string length * repeats or dx,dx ; overflowed (> 64KB)? jnz evalt17b ; nz = yes cmp ax,cmdblen ; more than parse buffer? jbe evalt17c ; be = no evalt17b:mov ax,cmdblen ; should be plenty (1000) evalt17c:mov cx,ax ; bytes wanted call evmem ; allocate memory mov dx,cx ; buffer length available evalt17d:mov cx,arglen ; length of text cmp cx,dx ; length vs buffer capacity jbe evalt17e ; be = capacity exceeds string mov cx,dx ; chop string to fit evalt17e:sub dx,cx ; deduct amount done mov si,argptr ; point at string cld rep movsb dec arglen+2 ; repeat count cmp arglen+2,0 ; any more repeats to do? je evalt17f ; e = no or dx,dx ; any space left? jg evalt17d ; g = yes evalt17f:mov bx,takadr sub di,[bx].takptr ; end minus start mov [bx].takcnt,di ; string length evalt17g:jmp evalt99 evalt18:cmp evaltype,F_reverse ; \freverse(text)? jne evalt19 ; ne = no mov cx,arglen ; length of string mov si,argptr ; string add di,cx dec di ; last byte in output string mov bx,takadr mov [bx].takcnt,cx ; length of output to buffer jcxz evalt18b ; z = empty string evalt18a:cld lodsb ; read left std stosb ; write left, displaced, backward cld loop evalt18a evalt18b:jmp evalt99 evalt19:cmp evaltype,F_date ; \fdate(filename)? je evalt19a ; e = yes cmp evaltype,F_size ; \fsize(filename)? jne evalt20 ; ne = no evalt19a:push di mov di,offset tmpbuf ; work buffer in this data seg mov byte ptr [di],0 ; terminator call filedate ; get info mov dx,offset tmpbuf call strlen ; get length of results to CX pop di mov bx,takadr mov [bx].takcnt,cx ; length of results mov si,offset tmpbuf cld rep movsb ; copy to Take buffer jmp evalt99 evalt20:cmp evaltype,F_files ; \ffiles(filespec)? jne evalt21 ; ne = no mov temp,0 ; count found files call filedate ; find first jc evalt20d ; c = none push di push es inc temp ; count found file mov dx,offset fileio.dta ; point at dta mov ah,setdma ; set the dta address int dos evalt20b:mov ah,next2 ; DOS 2.0 search for next int dos jc evalt20c ; c = no more inc temp ; count found file jmp short evalt20b ; try for next evalt20c:mov ah,setdma ; restore dta mov dx,offset buff int dos pop es pop di call filedate ; go back to find first again evalt20d:mov ax,temp ; count of files found call fdec2di ; convert to ASCII string mov bx,takadr sub di,[bx].takptr ; end minus start mov [bx].takcnt,di ; string length jmp evalt99 evalt21:cmp evaltype,F_nextfile ; \fnextfile()? jne evalt24 ; ne = no mov si,offset fileio.fname ; current filename from DOS mov dx,si call strlen ; length of name mov bx,takadr mov [bx].takcnt,cx ; length of result rep movsb ; copy to Take buffer push di push es mov dx,offset fileio.dta ; point at dta mov ah,setdma ; set the dta address int dos mov ah,next2 ; DOS 2.0 search for next int dos mov ah,setdma ; restore dta mov dx,offset buff int dos pop es pop di evalt21b:jmp evalt99 evalt24:cmp evaltype,F_replace ; \freplace(source,pat,replacement)? jne evalt25 ; ne = no cmp arglen+2,0 ; arg2 omitted? jne evalt24a ; ne = no mov cx,arglen ; source length mov si,argptr ; source jmp evalt24g ; copy source to output evalt24a:push es ; provided Take buffer, save push di mov ax,seg decbuf ; use local temp buf mov es,ax mov di,offset decbuf ; as es:di mov si,argptr ; make source ds:si mov cx,arglen ; length of source cld evalt24b:push cx ; save source counter push si ; save source pointer cmp cx,arglen+2 ; fewer source bytes than pattern? jb evalt24e ; b = yes, no replacement possible mov bx,argptr+2 ; pattern mov cx,arglen+2 ; length of pattern evalt24c:mov ah,[bx] ; pattern byte cmp [si],ah ; same as source byte? jne evalt24e ; ne = no, no match inc si ; next bytes to match inc bx loop evalt24c ; do all pattern bytes mov si,argptr+4 ; get replacement pattern mov cx,arglen+4 ; its length mov ax,di ; starting offset in temp buffer sub ax,offset decbuf ; minus start of buffer add ax,cx ; count of bytes to be in buffer cmp ax,decbuflen ; longer than buffer? jbe evalt24d ; be = no overflow sub ax,cx ; get back bytes already in buffer sub ax,decbuflen neg ax ; bytes available mov cx,ax ; move just that many evalt24d:rep movsb ; copy to output (may be nothing) pop si pop cx add si,arglen+2 ; length of pattern sub cx,arglen+2 ; bytes left in source jmp short evalt24f evalt24e:pop si ; mismatch comes here pop cx movsb ; write source byte to output buffer dec cx ; one less source byte evalt24f:or cx,cx ; qty of source bytes remaining jg evalt24b ; g = more source bytes to examine sub di,offset decbuf ; length of output material mov cx,di mov ax,cx ; save length in ax pop di pop es ; original Take buffer call evmem ; allocate new Take buffer, len cx mov si,offset decbuf mov cx,ax ; length used in decbuf evalt24g:cld rep movsb ; copy decbuf to Take buffer mov bx,takadr sub di,[bx].takptr mov [bx].takcnt,di ; length of ASCIIZ result jmp evalt99 evalt25:cmp evaltype,F_eval ; \feval(string)? jne evalt26 ; ne = no mov ax,argptr mov domath_ptr,ax mov ax,arglen or ax,ax jz evalt25b ; z = no argument mov domath_cnt,ax call domath ; return value in DX:AX jc evalt25b ; c = error, no value will be written push di ; save string destination es:di mov di,offset decbuf ; temp work space mov word ptr [di],0 ; clear it or dx,dx ; is result negative? jns evalt25a ; ns = no, positive or zero neg dx ; flip sign neg ax sbb dx,0 mov byte ptr [di],'-' ; show minus sign inc di evalt25a:call flnout ; convert DX:AX to ASCIIZ in DS:DI pop di ; recover original di mov dx,offset decbuf mov si,dx ; save as source pointer too call strlen ; get length of string to cx cld rep movsb ; copy to final buffer mov bx,takadr sub di,[bx].takptr ; end minus start mov [bx].takcnt,di ; string length evalt25b:jmp evalt99 evalt26:cmp evaltype,F_verify ; \fverify(pattern,string,offset)? jne evalt27 ; ne = no cld push es push di mov si,ds mov es,si mov di,argptr ; pattern pointer mov si,argptr+4 ; offset mov ax,arglen+4 ; length of offset string call domath jc evalt26b ; c = error mov bx,ax ; offset mov si,argptr+2 ; string add si,bx ; string plus offset mov cx,arglen+2 ; length of string sub cx,bx ; minus offset jg evalt26a ; g = have string left to compare mov bx,-1 ; error, report -1 for no string jmp short evalt26b evalt26a:push di lodsb ; get a byte of string inc bx ; count based 1 for users push cx ; one less string byte mov cx,arglen ; length of pattern repne scasb ; scan for match pop cx pop di jne evalt26b ; ne = mismatch loop evalt26a ; next string char xor bx,bx ; report 0 if all match evalt26b:mov ax,bx ; char position of first mismatch pop di pop es call fdec2di ; convert to ASCII string mov bx,takadr sub di,[bx].takptr ; end minus start mov [bx].takcnt,di ; string length jmp evalt99 evalt27:cmp evaltype,F_ipaddr ; \fipaddr(string, offset)? jne evalt29 ; ne = no mov si,argptr+2 ; offset mov ax,arglen+2 ; length of offset string mov domath_ptr,si ; convert offset to binary mov domath_cnt,ax call domath ; failure means use zero add argptr,ax sub arglen,ax evalt27a:mov si,argptr ; scan string mov cx,arglen cmp cx,7 ; enough bytes to qualify? jl evalt27x ; l = no, quit cld evalt27b:lodsb ; trim leading spaces sub al,'0' ; remove ASCII bias cmp al,9 ; numeric? jbe evalt27c ; be = yes loop evalt27b evalt27c:dec si ; break char inc cx ; byte count remaining mov argptr,si ; remember start of real text mov arglen,cx ; and its length call evalt27w ; scan for digits jc evalt27x ; c = fail cmp byte ptr [si],'.' ; looking at dot now? jne evalt27x ; ne = fail inc si call evalt27w ; scan for digits jc evalt27x ; c = fail cmp byte ptr [si],'.' ; looking at dot now? jne evalt27x ; ne = fail inc si call evalt27w ; scan for digits jc evalt27x ; c = fail cmp byte ptr [si],'.' ; looking at dot now? jne evalt27x ; ne = fail inc si call evalt27w ; scan for digits jc evalt27x ; c = fail mov al,[si] ; break byte sub al,'0' ; remove ASCII bias cmp al,9 jbe evalt27x ; be = is still numberic, fail mov cx,si ; break postion sub cx,argptr ; minus start jle evalt27x ; le = problems, fail mov si,argptr ; start rep movsb ; copy string to destination mov bx,takadr sub di,[bx].takptr ; end minus start mov [bx].takcnt,di ; string length jmp evalt99 evalt27x:inc argptr ; step to next source byte dec arglen ; one less to examine cmp arglen,7 ; any string left? jge evalt27a ; ge = yes, try again jmp evalt99 evalt27w proc near ; worker to read digits, check range mov cx,3 ; three digits xor dx,dx ; value of digits evalt27w1: mov ax,dx mul ten ; previous times ten to ax mov dx,ax lodsb sub al,'0' ; remove ASCII bias cmp al,9 ; validate decimal ja evalt27w2 ; a = no add dl,al ; number so far jc evalt27w3 ; c = overflow, fail loop evalt27w1 ; while in digits inc si ; inc to offset dec below evalt27w2: dec si ; back up to break byte cmp cx,3 ; must have at least one digit je evalt27w3 ; e = none, fail clc ; success, have digits ret evalt27w3: stc ; fail ret evalt27w endp evalt29:cmp evaltype,F_tod2secs ; \ftod2secs(hh:mm:ss)? jne evalt30 ; ne = no mov si,argptr ; hh:mm:ss string mov bx,arglen mov byte ptr [si+bx],0 ; terminate string call tod2secs ; convert to ASCII long in es:di jc evalt99 ; c = failed to convert mov bx,takadr sub di,[bx].takptr ; end minus start mov [bx].takcnt,di ; string length jmp evalt99 evalt30:cmp evaltype,F_chksum ; \fchecksum(string)? jne evalt31 mov si,argptr ; string mov cx,arglen xor bx,bx ; running sum, low and high portions xor dx,dx cld evalt30a:lodsb add bl,al ; accumulate checksum adc bh,0 ; with carry adc dx,0 loop evalt30a mov ax,bx push di push es mov di,ds mov es,di mov di,offset argptr call flnout ; 32 bit converter mov dx,offset argptr call strlen mov si,dx pop es pop di cld rep movsb mov bx,takadr sub di,[bx].takptr ; end minus start mov [bx].takcnt,di ; string length jmp evalt99 evalt31:cmp evaltype,F_basename ; \fbasename(string)? jne evalt99 mov dx,argptr ; string mov cx,arglen mov si,dx add si,cx mov byte ptr [si],0 ; force null termination push di mov di,offset decbuf+100 ; place for path part mov si,offset decbuf ; place for filename part call fparse ; split pieces pop di mov dx,si ; look only at filename call strlen rep movsb ; copy to result buffer mov bx,takadr sub di,[bx].takptr ; end minus start mov [bx].takcnt,di ; string length jmp evalt99 evalt99:pop temp pop si pop dx ; trailing space flag clc ; success or fail, consume source ret evaltoa endp ; Worker for evaltoa. ; Given start of string in DS:SI and string length in CMSIZ. ; Return pointers to up to three arguments (as offset of start of their ; non-white space) and the lengths of each argument field. Arrays argptr ; and arglen hold these 16 bit values. ; Bare commas separate arguments, curly braces and parentheses protect commas, ; curly braces protect strings, starting with a curly brace is considered to ; mean use material within braces. A closing curly brace closes parens. evarg proc near push ax push bx push cx push dx push si push temp mov al,taklev xor ah,ah mov temp,ax xor ax,ax mov argptr,ax ; clear arg pointer list mov argptr+2,ax mov argptr+4,ax mov arglen,ax ; clear arg length list mov arglen+2,ax mov arglen+4,ax xor bx,bx ; index of argument ; read start of argument evarg10:mov si,cmrptr ; last byte + 1 read xor dx,dx ; paren and curly brace counters mov argptr[bx],si ; where argument string starts mov cmsiz,0 ; argument length counter evarg12:mov cl,comand.cmper ; read next argument byte push cx ; preserve comand.cmper around call push temp ; preserve take level monitor push evaltype ; preserve, reading may recurse push bx ; preserve argument counter push dx ; preserve curly brace counter push cmsiz mov cmsflg,0 ; see leading spaces/tabs push argptr ; save our working variables push argptr+2 push argptr+4 push arglen push arglen+2 push arglen+4 cmp evaltype,F_contents ; \fcontents(macro)? je evarg13 ; e = yes cmp evaltype,F_literal ; \fliteral(string)? jne evarg14 ; ne = no evarg13:mov comand.cmper,1 ; arg, treat macros as literals evarg14:call cmgtch ; recursively read a character into ah pop arglen+4 pop arglen+2 pop arglen pop argptr+4 pop argptr+2 pop argptr pop cmsiz pop dx pop bx pop evaltype pop temp pop cx mov comand.cmper,cl ; restore macro expansion state pushf ; save carry flag from cmgtch inc cmsiz ; count argument byte push bx mov bl,taklev ; current take level cmp bl,byte ptr temp ; react to specials? pop bx jbe evarg15 ; be = yes, else in macro/take popf ; clear stack of cmgtch carry status jmp evarg12 ; read another byte evarg15:popf ; recover cmgtch carry status jnc evarg50 ; nc = non-terminator cmp ah,' ' ; just a space? jne evarg17 ; ne = no cmp cmsiz,1 ; is it a leading space? jne evarg12 ; ne = no, consider it to be data cmp evaltype,F_literal ; \fliteral(string)? je evarg12 ; e = yes, keep leading spaces jmp evarg10 ; skip space, get first arg byte evarg17:cmp ah,'?' ; need help? jne evarg20 ; ne = no call fcmdhlp ; compute cmhlp to proper string mov cmsiz,0 ; so help is with provided text call cmkyhlp jmp repars evarg20:dec cmsiz ; omit byte from count cmp ah,escape ; ESC? jne evarg22 ; ne = no cmp evaltype,F_contents ; \fcontents(macro)? je evarg21 ; e = yes cmp evaltype,F_definition ; \fdefinition(macro)? jne evarg22 ; ne = no evarg21:push cmptab ; save working pointers push cmsptr mov cmptab,offset mcctab ; look at table of macro names mov cx,argptr[bx] ; look at whole word todate mov cmsptr,cx ; start of word push bx call cmkyesc ; do escape word completion pop bx pop cmsptr pop cmptab xor ah,ah ; in case no match evarg22:call bufreset ; reset cmwptr cmp ah,escape ; escape? jne evarg30 ; ne = no push ax push dx mov ah,conout ; ring the bell mov dl,bell int dos pop dx pop ax jmp evarg12 ; get next arg byte evarg30:jmp repars ; reparse command evarg50:cmp ah,braceop ; opening curly brace? jne evarg52 ; ne = no inc dl ; count up curly brace jmp evarg12 ; get next argument byte evarg52:cmp ah,bracecl ; closing curly brace? jne evarg60 ; ne = no sub dl,1 ; count down curly braces jge evarg54 ; ge = no underflow xor dx,dx ; clamp at zero evarg54:jmp evarg12 evarg60:or dl,dl ; within curly braces jnz evarg3 ; nz = yes, don't count parens there cmp ah,'(' ; opening paren? jne evarg62 ; ne = no inc dh ; count up paren jmp evarg12 ; get next byte of argument evarg62:cmp ah,')' ; closing paren? jne evarg3 ; ne = no sub dh,1 ; count down closing paren jl evarg64 ; l = reached terminator jmp evarg12 ; get next byte of argument evarg64:mov cx,cmsiz dec cx ; don't count closing paren mov arglen[bx],cx cmp evaltype,F_literal ; \fliteral(string)? je evarg66 ; e = yes, keep trailing spaces mov si,argptr[bx] ; start of argument add si,cx dec si ; last byte of argument jcxz evarg66 ; z = no text to trim evarg65:cmp byte ptr [si],' ' ; trailing space? jne evarg66 ; ne = no dec si dec arglen[bx] ; one less argument byte loop evarg65 evarg66:jmp evarg7 ; ")" marks END of function evarg3: cmp ah,',' ; possible argument terminator? jne evarg5 ; ne = no or dx,dx ; within paren or curly braces? jne evarg5 ; ne = yes, treat commas as data cmp evaltype,F_literal ; doing \fliteral()? je evarg5 ; e = yes, commas are data cmp evaltype,F_eval ; doing \feval()? je evarg5 ; e = yes, commas are data dec cmsiz ; do not count comma evarg4: mov cx,cmsiz cmp bx,3*2 ; done three args? jae evarg4a ; ae = yes, add no more args mov arglen[bx],cx ; mark argument length add bx,2 ; next arg array element evarg4a:jmp evarg10 ; get next argument evarg5: jmp evarg12 ; get next byte in argument evarg7: pop temp pop si pop dx pop cx pop bx pop ax clc ret evarg endp ; Reallocates more memory than provided in the default takopen 128 byte buffer. ; Enter with desired byte quantity in CX and a Take structure already ; present at takadr. Returns with new buffer length in CX, ES:DI pointing ; at buffer+2 ([bx].takbuf:[bx].takptr) evmem proc near push ax push bx push cx mov bx,takadr mov ax,[bx].takbuf ; segment of preallocated memory mov bx,cx ; string length, in bytes add bx,2+1+15 ; count + null term + round up mov cl,4 shr bx,cl ; convert to paragraphs (divide by 16) mov cx,bx ; remember desired paragraphs mov ah,alloc ; allocate a memory block int dos ; ax=seg, bx=paragraphs jc evmem3 ; c = error, not enough memory cmp bx,cx ; obtained vs wanted jae evmem2 ; ae = enough push bx mov bx,takadr mov cx,[bx].takcnt ; old unread-bytes shr cx,1 shr cx,1 shr cx,1 shr cx,1 ; to paragraphs pop bx cmp bx,cx ; more than we had originally? ja evmem2 ; a = yes, so use the new larger buff push es mov es,ax ; new allocated segment mov ah,freemem ; free it int dos pop es jmp short evmem3 ; use original segment evmem2: mov cl,4 shl bx,cl ; paragraphs to bytes mov cx,bx ; return new bytes in CX sub cx,2 ; minus two for len field mov es,ax ; set new segment mov bx,takadr xchg [bx].takbuf,ax ; new segment, swap with old seg or ax,ax ; if any allocated jz evmem2a ; z = none push es mov es,ax ; old allocated segment mov ah,freemem ; free old segment int dos pop es evmem2a:mov di,2 ; new offset mov bx,takadr mov [bx].takptr,di ; new offset mov [bx].takcnt,0 ; total buffer, unread bytes or [bx].takattr,take_malloc ; malloc'd buffer to remove later pop bx ; discard old cx, return new cx pop bx pop ax ret evmem3: pop cx ; return original values pop bx pop ax ret evmem endp ; Worker for evaltoa. Sets cmhlp to help string matching evaltype fcmdhlp proc near mov ax,evaltype ; kind of \f command mov bx,offset f1hlp ; number cmp ax,F_char ; go through types je fcmdh1 ; e = matched cmp ax,F_maximum je fcmdh1 cmp ax,F_minimum je fcmdh1 mov bx,offset f2hlp ; char cmp ax,F_code je fcmdh1 mov bx,offset f3hlp cmp ax,F_contents je fcmdh1 cmp ax,F_definition je fcmdh1 mov bx,offset f8hlp cmp ax,F_index je fcmdh1 mov bx,offset f12hlp cmp ax,F_lpad je fcmdh1 cmp ax,F_rpad je fcmdh1 mov bx,offset f15hlp cmp ax,F_nextfile je fcmdh1 mov bx,offset f16hlp cmp ax,F_repeat je fcmdh1 mov bx,offset f18hlp cmp ax,F_right je fcmdh1 mov bx,offset f20hlp cmp ax,F_substr je fcmdh1 mov bx,offset f22hlp cmp ax,F_date je fcmdh1 cmp ax,F_basename je fcmdh1 cmp ax,F_size je fcmdh1 cmp ax,F_files je fcmdh1 mov bx,offset f25hlp cmp ax,F_eval je fcmdh1 mov bx,offset f8hlp cmp ax,F_rindex je fcmdh1 mov bx,offset f30hlp cmp ax,F_chksum je fcmdh1 mov bx,offset f9hlp ; default to text fcmdh1: mov cmhlp,bx ; set help text pointer ret fcmdhlp endp filedate proc near push di push es mov dx,offset fileio.dta ; data transfer address mov ah,setdma ; set disk transfer address int dos mov bx,argptr ; filename mov dx,bx ; for file open add bx,arglen ; length of it mov byte ptr [bx],0 ; make it ASCIIZ xor cx,cx ; attributes: find only normal files mov ah,first2 ; DOS 2.0 search for first int dos ; get file's characteristics pushf ; save status mov ah,setdma ; restore dta mov dx,offset buff int dos popf ; get status pop es pop di jnc filed1 ; nc = success ret ; fail filed1: cmp evaltype,F_date ; want date/time stamp? je filed3 ; e = yes cmp evaltype,F_size ; want file length? jne filed2 ; ne = no (probably \ffiles(..)) mov ax,fileio.sizelo ; file size to dx:ax mov dx,fileio.sizehi call flnout ; convert to ASCII mov byte ptr [di],0 ; terminate filed2: clc ret filed3: mov bx,offset fileio.dta ; set work pointer call filetdate ; do the work ret filedate endp ; Write file date and timestamp to ds:di, given DTA in ds:bx. filetdate proc far push es ; time/date stamp yyyymmdd hh:mm:ss mov ax,ds mov es,ax xor ah,ah mov al,[bx+25] ; yyyyyyym from DOS via file open shr al,1 ; get year add ax,1980 ; add bias xor dx,dx call flnout ; put year (1990) in buffer mov ax,[bx+24] ; yyyyyyyym mmmddddd year+month+day shr ax,1 ; month to al xor ah,ah mov cl,4 shr al,cl ; month to low nibble mov byte ptr[di],'0' ; leading digit inc di cmp al,9 ; more than one digit? jbe filed4 ; be = no mov byte ptr[di-1],'1' ; new leading digit sub al,10 ; get remainder filed4: add al,'0' ; to ascii stosb ; end of month mov al,[bx+24] ; get day of month and al,1fh ; select day bits xor ah,ah mov cl,10 div cl ; quot = al, rem = ah add ax,'00' ; add ascii bias stosw ; leading digit and end of date mov al,' ' ; space separator stosb mov al,[bx+23] ; hours hhhhhmmm mov cl,3 shr al,cl ; move to low nibble xor ah,ah mov cl,10 div cl ; quot = al, rem = ah add ax,'00' ; add ascii bias stosw ; store hours mov al,':' ; separator stosb mov ax,[bx+22] ; get minutes: hhhhhmmm mmmsssss mov cl,5 shr ax,cl ; minutes to low byte and al,3fh ; six bits for minutes xor ah,ah mov cl,10 div cl add ax,'00' ; add ascii bias stosw mov al,':' ; separator stosb mov al,[bx+22] ; get seconds (double secs really) and al,1fh shl al,1 ; DOS counts by two sec increments xor ah,ah mov cl,10 div cl add ax,'00' ; add ascii bias stosw mov byte ptr [di],0 ; terminate pop es ret filetdate endp ; Set envadr to the string following the keyword in the DOS ; Environment and set envlen to its length after removing leading and ; trailing whitespace. ; starts at ds:, of length cmsiz-1, and can be mixed case. ; Return carry set if can't find the line in the Environment. envvar proc near push es mov bx,valtmp ; start of variable name mov cx,cmsiz ; length of variable name, w/o ')' or cx,cx ; empty? jle envvar3 ; le = nothing to look for, fail push bx push cx envvar1:mov al,byte ptr [bx] ; scan variable name in our buffer cmp al,'a' ; lower case jb envvar2 ; b = no cmp al,'z' ; still in lower case range? ja envvar2 ; a = no and al,not 20h ; convert to DOS's upper case mov byte ptr [bx],al ; replace char envvar2:inc bx loop envvar1 pop cx pop bx ; find "=" in Environment call fgetenv ; dx = offset in Environment of "=" jnc envvar4 ; nc = success push bx push cx envvar1a:mov al,byte ptr [bx] ; scan variable name in our buffer cmp al,'A' ; upper case jb envvar2a ; b = no cmp al,'Z' ; still in upper case range? ja envvar2a ; a = no or al,20h ; convert to DOS's lower case mov byte ptr [bx],al ; replace char envvar2a:inc bx loop envvar1a pop cx pop bx ; find "=" in Environment call fgetenv ; dx = offset in Environment of "=" jnc envvar4 ; nc = success envvar3:pop es ; no such variable stc ; c = failure ret ; dx has offset in Environment of char "=" ; ds:valtmp is start of variable name, cmsiz is length + 1 of the name. ; Return seg:offset and length variables so we can copy from there in valtoa envvar4:push di push si xor ax,ax mov word ptr envadr,ax ; offset in env of variable's string mov word ptr envadr+2,ax ; seg of same mov envlen,ax ; length of string mov es,psp ; our Prog Seg Prefix segment mov ax,es:word ptr[env] ; pick up Environment address mov es,ax ; set es: to Environment segment mov di,dx ; line scan pointer cmp byte ptr es:[di],'=' ; did we stop on this? jne envvar5 ; ne = no inc di ; skip the "=" char envvar5:mov al,es:[di] ; scan over leading white space inc di or al,al ; end of line terminator? jz envvarf ; z = yes, fail cmp al,TAB ; HT? jne envvar6 ; ne = no mov al,' ' ; HT becomes a space envvar6:cmp al,' ' ; white space? je envvar5 ; scan off white space dec di ; backup to non-white char mov word ptr envadr,di ; offset of string in Environment mov word ptr envadr+2,es ; seg of string in Environment mov si,di ; remember starting offset here ; remove trailing spaces from string xor al,al ; a null mov cx,127 ; max length to search cld repne scasb ; skip over non-nulls dec di ; backup to null dec di ; backup to last string char mov cx,di ; ending offset inc cx ; count the char sub cx,si ; minus starting offset yields length jcxz envvar9 ; z = empty string envvar7:mov al,es:[di] ; last char dec di ; backup one char cmp al,' ' ; space? je envvar8 ; e = yes cmp al,TAB ; HT? jne envvar9 ; ne = no, end of white space envvar8:loop envvar7 ; keep looking envvar9:mov envlen,cx ; store the length clc ; say success jmp short envvarx envvarf:stc ; say failure envvarx:pop si pop di pop es ret envvar endp ; Worker for Remote Query System name in mssser. ; Enter with variable name in decbuf+6 et seq, name length byte in decbuf+5 ; with ' ' bias. fqryenv proc far mov valtmp,offset decbuf+6 ; start of variable name mov cl,decbuf+5 ; name length plus bias sub cl,' ' ; remove bias xor ch,ch mov cmsiz,cx ; name length call envvar ; get address of environmental string jnc fqryenv1 ; nc = success xor cx,cx ; failure ret fqryenv1:mov cx,envlen ; length of string in environment push es push ds lds si,envadr ; pointer to string mov di,seg encbuf ; destination buffer mov es,di mov di,offset encbuf cld rep movsb ; copy string pop ds pop es mov cx,envlen ; string length again ret fqryenv 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 read_source,0 ; assume direct reading mov endedbackslash,0 ; clear end of sub scan flag cmget01:cmp comand.cmdirect,0 ; read directly? jne cmget02 ; ne = yes cmp taklev,0 ; in a Take file? jne cmget1 ; ne = yes, do Take reader section cmget02: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 cmget12 ; 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 cmget12 ; nc = not EOF, process new CR cmget21:mov flags.extflg,1 ; EOF on disk file, set exit flag jmp cmget12 ; do echoing and return cmget1: push bx ; read from Take file mov bx,takadr ; offset of this Take structure mov al,[bx].taktyp mov read_source,al cmp [bx].takcnt,0 ; bytes remaining in Take buffer jne cmget4 ; ne = not empty cmp al,take_file ; type of Take (file?) jne cmget3 ; ne = no (macro) call takrd ; read another buffer cmp [bx].takcnt,0 ; anything in the buffer? jne cmget4 ; ne = yes cmget3: pop bx ; clear stack jmp short cmget5 ; close the Take file cmget4: push si ; read from Take non-empty buffer push es mov es,[bx].takbuf ; segment of Take buffer mov si,[bx].takptr ; current offset in Take buffer mov al,es:[si] ; read a char from Take buffer pop es inc si mov [bx].takptr,si ; move buffer pointer pop si dec [bx].takcnt ; decrease number of bytes remaining cmp read_source,take_sub ; substitution macro? jne cmget4b ; ne = no cmp [bx].takcnt,0 ; read last byte? jne cmget4b ; ne = no cmp al,'\' ; ended on a backsash? jne cmget4b ; ne = no mov endedbackslash,al ; setup break end of sub scan cmget4b:pop bx cmp read_source,take_file ; kind of Take/macro, file? jne cmget12 ; ne = no, not a file cmp al,LF ; LF? je cmget01 ; e = yes, ignore cmp al,ctlz ; Control-Z? jne cmget12 ; ne = no, else ^Z = EOF cmget5: push bx ; end of file on Take buffer mov bx,takadr ; offset of this Take structure mov ah,[bx].takattr ; save kind of autoCR pop bx cmp read_source,take_sub ; text subsititution? je cmget5a ; e = yes, cannot keep open cmp comand.cmkeep,0 ; keep Take/macro open after eof? jne cmget5b ; ne = yes cmget5a:push ax call ftakclos ; close take file pop ax test ah,take_autocr ; add CR on EOF? jnz cmget5b ; nz = yes jmp cmgetc ; internal macros have no auto CR cmget5b:mov al,CR ; report CR as last char jmp short cmget12 ; read from tty device cmget10:mov ah,coninq ; Get a char from device, not file int dos ; with no echoing or al,al jnz cmget11 ; ignore null bytes of special keys int dos ; read and discard scan code byte jmp short cmget10 ; try again cmget11:cmp al,LF ; LF? jne cmget12 ; ne = no mov al,CR ; replace interactive LF with CR 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:cmp in_showprompt,0 ; Non-zero when in showprompt jne cmget13 ; ne = yes, no editing test read_source,take_sub+take_macro ; substitution or macro? jnz cmget13 ; nz = yes, do not edit line mov bracecnt,0 mov ah,prstr ; Control-C handler push dx mov dx,offset ctcmsg ; show Control-C int dos pop dx mov flags.cxzflg,'C' ; tell others the news jmp cmexit ; fail immediately via longjmp cmgetc endp ; Read chars from user (cmgetc). Detect terminators. Reads from buffer ; cmbuf. Set read pointer 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 cmp cmwptr,offset cmdbuf+size cmdbuf-3 ; max buffer size - 3 jb cminb1 ; b = not full for writing mov ah,conout ; almost full, notify user push dx mov dl,bell int dos pop dx cmp cmrptr,offset cmdbuf+size cmdbuf ; reading beyond buffer? jb cminb1 ; b = no mov ah,prstr mov dx,offset cmer09 ; command too long int dos jmp prserr ; overflow = parse error cminb1: push bx mov bx,cmrptr ; read pointer mov ah,[bx] ; get current command char while here cmp bx,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' cmp read_source,take_sub ; substitution? je cminb1a ; e = yes push bx mov bx,cmrawptr ; where raw writes go (inc remakes) mov [bx],ah ; store byte inc cmrawptr pop bx cminb1a: push bx mov bx,cmwptr ; get the pointer into the buffer mov [bx],ah ; put it in the buffer inc bx mov cmwptr,bx ; inc write pointer pop bx jmp short cminb1 ; call cmgetc until cmwptr >= cmrptr ; Char to be delivered is in ah cminb2: cmp in_showprompt,0 ; Non-zero when in showprompt jne cminb5 ; ne = yes, no editing test read_source,take_sub+take_macro ; substitution or macro? jnz cminb4 ; nz = yes, do not edit line cmp ah,'W' and 1fh ; is it a ^W? jne cminb3 ; ne = no call cntrlw ; kill the previous word jmp repars ; need a new command scan (cleans stk) cminb3: cmp ah,'U' and 1fh ; is it a ^U? jne cminb3a ; ne = no mov cmwptr,offset cmdbuf ; reset buffer write pointer mov cmrptr,offset cmdbuf mov cmrawptr,offset rawbuf ; clear raw buffer 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 call crprot ; protect CR? jnc cminb4e ; nc = no ; cmtxt {continued lines} section dec cmwptr ; back over CR for both buffers dec cmrptr mov bx,cmwptr mov ah,',' ; replace CR with comma mov [bx-1],ah mov bx,cmrawptr mov [bx-1],ah mov bx,cmrptr mov [bx],ah ; deal with echoing cmp comand.cmquiet,0 ; quiet mode? jne cminb4g ; yes, skip echoing cmp comand.cmdirect,0 ; direct reading? jne cminb4f ; ne = yes, do echoing cmp taklev,0 ; in a take file? je cminb4f ; e = no cmp read_source,take_comand ; command reread macro? je cminb4f ; e = yes, echo cmp flags.takflg,0 ; echo take file? je cminb4g ; e = no cmp read_source,take_sub ; don't echo string substitution je cminb4g cminb4f: push ax push dx mov dl,ah mov ah,conout ; echo comma to screen now int dos mov ah,prstr ; and then visually break the line mov dx,offset crlf int dos pop dx pop ax cminb4g:pop bx clc ; returns ah = comma ret ; start regular hyphenation part cminb4e:mov bx,cmwptr ; get the pointer into the buffer cmp bx,offset 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 sub cmwptr,2 ; back over - for both buffers sub cmrawptr,2 jmp repars ; reparse what we now have cminb4b:pop bx ; Echoing done here cmp comand.cmquiet,0 ; quiet mode? jne cminb5 ; yes, skip echoing cmp comand.cmdirect,0 ; direct reading? jne cminb4a ; ne = yes, do echoing cmp taklev,0 ; in a take file? je cminb4a ; e = no cmp read_source,take_comand ; command reread macro? je cminb4a ; e = yes, echo cmp flags.takflg,0 ; echo take file? je cminb5 ; e = no cmp read_source,take_sub ; don't echo string substitution je cminb5 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 ; carriage return? je cminb6 ; e = yes cmp ah,LF ; line feed? je cminb6 cmp ah,FF ; formfeed? jne cminb7 ; none of the above, report bare char call fcmblnk ; FF: clear the screen and push bx push cx push dx call flocate ; Home the cursor mov bx,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 cmwptr,offset 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. Cmrptr points to next char to be read. ; Compresses repeated spaces if cmsflg is non-zero. Exit with cmrptr pointing ; at a terminator or otherwise at next free slot. ; Non-space then space acts as a terminator but cmrptr is incremented. ; Substitution variables, '\%x', and \numbers are detected and expanded. ; Return char in AH. ; If a \blah parse fails then report the \ to the caller and start over, ; without echo, on the char following that \. Replay_skip does the \ ; passage without calling subst, replay_cnt rereads bytes after it. CMGTCH proc near ; return char in AH, from rescan buf cmp replay_cnt,0 ; starting a replay? je cmgtc5 ; e = no mov replay_skip,1 ; set latch to skip subst on 1st byte cmgtc5: cmp in_reparse,0 ; replaying material? je cmgtc0 ; e = no call remake ; remake command line as macro cmgtc0: cmp replay_cnt,0 ; replay with no echo? jne cmgtc1 ; ne = yes, have in cmdbuf now call cminbf ; get char from buffer or user cmgtc1: push bx mov bx,cmrptr ; get read pointer into the buffer mov ah,[bx] ; read the next char inc bx mov cmrptr,bx ; where to read next time cmp replay_cnt,0 ; replaying? jne cmgtc6 ; ne = yes, don't take the branch cmp bx,cmwptr ; still reading analyzed text? cmgtc6: pop bx jb cmgtc2 ; b = yes, don't reexpand cmp replay_skip,0 ; replay latch set? je cmgtc7 ; e = no, re-read new bytes in subst mov replay_skip,0 ; clear latch after the first re-read byte jmp cmgtc2 ; and skip subst on that first byte cmp replay_cnt,0 ; replaying? jne cmgtc2 ; ne = yes cmgtc7: call subst ; examine for text substitution jc cmgtch ; c = reread input material cmp report_binary,0 ; report binary number directly? je cmgtc2b ; e = no mov report_binary,0 ; clear flag clc ; value is in ah, non-terminator ret cmgtc2: push bx mov bx,replay_cnt ; count down replay bytes or bx,bx jz cmgtc2a ; z = empty dec replay_cnt cmgtc2a:pop bx cmgtc2b:cmp ah,',' ; comma? jne cmgtc2c ; ne = no cmp comand.cmcomma,0 ; comma is space equivalent? je cmgtc2c ; e = no mov ah,' ' ; convert comma to space cmgtc2c:cmp ah,' ' ; space? jne cmgtc3 ; ne = no cmp cmsiz,0 ; started user data? je cmgtc2d ; e = no, this is a leading space cmp cmkind,cmline ; doing full lines of text? je cmgtc3 ; e = yes, retain all spaces cmgtc2d:cmp bracecnt,0 ; are we within braces? jne cmgtc3 ; ne = yes, treat space as literal cmp cmsflg,0 ; space flag, was last char a space? jne cmgtch ; ne = yes, get another char mov cmsflg,0FFH ; set the space(s)-seen flag stc ; set carry for terminator ret ; return space as a terminator cmgtc3: mov cmsflg,0 ; clear the space-seen flag cmp ah,braceop ; opening brace? jne cmgtc3b ; ne = no inc bracecnt ; count it jmp short cmgtc3c cmgtc3b:cmp ah,bracecl ; closing brace? jne cmgtc3c ; ne = no sub bracecnt,1 ; count down and get a sign bit jns cmgtc3c ; ns = no underflow mov bracecnt,0 ; catch underflows ; terminators remain in buffer but ; are ready to be overwritten cmgtc3c:test read_source,take_sub ; substitution macro? jnz cmgtc3d ; nz = yes, pass all bytes as data cmp ah,escape ; Escape expander? jne cmgtc3e ; ne = no cmp read_source,0 ; reading from keyboard directly? jne cmgtc4 ; ne = no, escape is regular char cmgtc3e:cmp ah,'?' ; is the user curious? jne cmgtc3a ; ne = no cmp taklev,0 ; in a Take file? jne cmgtc3d ; ne = yes, make query ordinary char je cmgtc4 cmgtc3a:cmp ah,CR ; these terminators? je cmgtc4 ; e = yes cmp ah,LF je cmgtc4 cmp ah,FF je cmgtc4 cmp ah,ESCAPE je cmgtc4 cmgtc3d:clc ; carry clear for non-terminator ret cmgtc4: dec cmrptr ; point at terminating char stc ; set carry to say it is a terminator ret cmgtch endp ; Reset cmdbuf write pointer (cmwptr) to where the read pointer ; (cmrptr) is now. Discards material not yet read. bufreset proc near push cmrptr ; where next visible char is read push ax ; count removed curly braces push si mov si,cmrptr ; where to look mov cx,cmwptr ; last place being removed xor ah,ah ; get a null sub cx,si ; length to examine jle bufres3a ; le = nothing to scan cld bufres1:lodsb cmp al,braceop ; opening brace, counted already? jne bufres2 ; ne = no dec bracecnt ; uncount it jmp short bufres3 bufres2:cmp al,bracecl ; closing brace, counted already? jne bufres3 ; jne = no inc bracecnt ; uncount it bufres3:loop bufres1 bufres3a:cmp bracecnt,ah ; negative? jge bufres4 ; ge = no mov bracecnt,ah bufres4:mov word ptr [si],0 ; terminate buffer pop si pop ax pop 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 cmp read_source,take_sub ; substitution macro? je bufdel0 ; e = yes, does not change cmrawptr cmp cmrawptr,offset rawbuf ; anything written to base level buf? ja bufdel7 ; a = yes mov cmrawptr,offset rawbuf ; safety mov ax,offset cmdbuf mov cmrptr,ax mov cmwptr,ax clc ; do nothing ret bufdel7:dec cmrawptr ; backup one byte push bx mov bx,cmrawptr cmp byte ptr [bx],BS ; last read was BackSpace? pop bx jne bufdel8 ; ne = not BS dec cmrawptr ; backup write pointer over BS too bufdel8:cmp cmrawptr,offset rawbuf ; sanity check, back too far? jae bufdel9 ; ae = not too far mov cmrawptr,offset rawbuf ; reset to start of buffer bufdel9:cmp comand.cmdirect,0 ; doing direct reading? jne bufdel0 ; ne = yes mov ax,offset cmdbuf mov cmwptr,ax ; reset normal parse buffer mov cmrptr,ax mov bx,ax mov word ptr [bx],0 ; terminate buffer mov in_reparse,1 ; say need to replay from rawbuf jmp repars ; reparse command bufdel0:cmp cmrptr,offset cmdbuf ; at start of buffer now? jbe bufdel5 ; be = yes push ax push si mov si,cmrptr mov al,[si] dec si xor ah,ah ; get a null cmp al,braceop ; opening brace, counted already? jne bufdel1 ; ne = no dec bracecnt ; uncount it jmp short bufdel2 bufdel1:cmp al,bracecl ; closing brace? jne bufdel2 ; ne = no inc bracecnt ; uncount it bufdel2:cmp bracecnt,ah ; negative? jge bufdel3 ; ge = no mov bracecnt,ah bufdel3:pop si pop ax mov replay_cnt,0 ; clear protected replay byte count dec cmrptr ; remove previous char from buffer cmp cmrptr,offset cmdbuf ; back too far? jae bufdel4 ; ae = no, material can be erased mov cmrptr,offset cmdbuf ; set to start of buffer mov bracecnt,ah ; ensure this is now cleared bufdel5:call bufreset ; reset buffer clc ; say no erasure ret bufdel4:call bufreset ; reset buffer stc ; say did erasure ret bufdel endp ; Remake the current command line, omitting substitution macro contents, ; as a macro (kind is comand, almost "macro"). Current command line is ; in rawbuf, with cmrawptr pointing next place to write. ; Closes currently active substitution macros. remake proc near push bx push cx push dx push di mov cmrptr,offset cmdbuf mov cmwptr,offset cmdbuf ; clear work buffer mov cx,cmrawptr ; where to write next mov cmrawptr,offset rawbuf sub cx,offset rawbuf ; length of string jle remake3 ; le = nothing to do remake1:cmp taklev,0 ; in a take file? je remake2 ; e = no mov bx,takadr cmp [bx].taktyp,take_sub ; sub macro? jne remake2 ; ne = no push cx call ftakclos ; close sub macros pop cx jmp short remake1 ; next victim remake2:call takopen_macro ; open take as macro (comand kind) mov bx,takadr ; pointer to new Take structure mov [bx].taktyp,take_comand ; that's us and [bx].takattr,not take_autocr ; no CR at end of macro please mov [bx].takcnt,cx ; number of unread bytes mov [bx].takbuf,seg rawbuf ; segment of Take buffer mov cx,offset rawbuf mov [bx].takptr,cx ; offset to read from mov cmrawptr,cx remake3:mov in_reparse,0 ; clear our flag mov bracecnt,0 mov replay_cnt,0 mov comand.cmkeep,0 pop di pop dx pop cx pop bx ret remake endp ; Come here is user types ^W when during input. Remove word from buffer. cntrlw proc near push ax push cx push dx call bufreset ; truncate buffer at cmrptr mov si,cmwptr ; assumed source pointer mov cx,cmrptr ; read pointer sub cx,offset cmdbuf ; compute chars in buffer cmp comand.cmdirect,0 ; direct reading? jne cntrl3 ; ne = yes, only one buffer dec cmrawptr ; back over ^W mov si,cmrawptr ; use raw buf as source pointer mov cx,si ; point at last written byte sub cx,offset rawbuf ; count of bytes before ^W jle ctlw2 ; le = none cntrl3: 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,si ; 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 cmwptr,di ; update pointer cmp comand.cmdirect,0 ; direct reading? jne ctlw2 ; ne = yes, only one buffer inc di ; leave a char for bufdel mov cmrawptr,di ; setup delete initial byte of word call bufdel ; delete item, reparse ctlw2: pop dx pop cx pop ax ret cntrlw endp ; returns carry set if CR is to be protected ; returns carry clear if CR is unprotected crprot proc near ; cmtxt {continued lines} section cmp comand.cmcnvkind,cmcnv_crprot ; braces span lines? jne crprotx ; ne = no, do regular hyphenation cmp cmkind,cmkey je crprotx cmp bracecnt,0 ; are curly braces matched yet? je crprotx ; e = yes, then this is ignorable stc ; CR needs protection ret crprotx:clc ret crprot 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 FAR mov cmwptr,offset cmdbuf ; initialize write pointer mov ah,prstr mov dx,offset crlf ; leave old line, start a new one int dos call rprompt ; restore master prompt level ; reparse current line REPARS: mov cmrptr,offset cmdbuf ; reinit read pointer xor ax,ax mov cmsflg,0ffh ; strip leading spaces mov subcnt,al ; clear substitution state variables mov subtype,al mov bracecnt,al call optionclr ; clear parser options cmp in_showprompt,al ; redoing the prompt itself? jne prser3 ; ne = yes, suppress display cmp comand.cmdirect,al ; doing direct reading? jne prser2 ; ne = yes cmp taklev,al ; in Take cmd? je prser2 ; e = no cmp flags.takflg,al ; echo contents of Take file? je prser3 ; e = no prser2: call fctlu ; clear display's line, reuse it mov dx,comand.cmprmp ; display the asciiz prompt call prtasz prser3: mov bx,0ffffh ; returned keyword value mov sp,comand.cmostp ; set new sp to old one jmp dword ptr comand.cmrprs ; jump to just after the prompt call PRSERR ENDP ; Restore prompt material to that of the master prompt. This removes settings ; of local PROMPT calls so we can reprompt at the main Kermit level. RPROMPT proc near push ax ; Must preserve AX mov replay_cnt,0 mov ax,mcmprmp ; address of prompt string or ax,ax ; any address given yet? jz rprompt1 ; z = none, not inited yet mov comand.cmprmp,ax ; set current address ptr mov ax,word ptr mcmrprs ; offset of reparse address mov word ptr comand.cmrprs,ax mov ax,word ptr mcmrprs+2 ; segment of reparse address mov word ptr comand.cmrprs+2,ax mov ax,mcmostp ; stack ptr at reparse time mov comand.cmostp,ax mov cmrawptr,offset rawbuf ; base/raw buffer write ptr, reset mov in_reparse,0 ; clear buffer remake flag rprompt1:pop ax ret RPROMPT endp ; Clear comand.* options to defaults optionclr proc near push ax xor ax,ax mov comand.cmwhite,al ; clear whitespace flag mov comand.cmper,al ; reset to variable recognition mov comand.cmkeep,al ; do not keep Take file open mov comand.impdo,al ; clear flag to prevent loops mov comand.cmquiet,al ; permit echoing again mov comand.cmblen,ax ; set user buffer length to unknown mov comand.cmdonum,al ; defeat \number expansion mov comand.cmcomma,al ; comma is not a space mov comand.cmarray,al ; disallow sub in [..] mov comand.cmcnvkind,cmcnv_none ; default is no conversion pop ax ret optionclr endp ; write \v(ARGC) contents to ds:di wrtargc proc near xor ax,ax cmp taklev,0 ; in a Take/Macro? je wrtarg1 ; e = no mov bx,takadr ; current Take structure mov ax,[bx].takargc ; get ARGC wrtarg1:call fdec2di ; write as ascii mov word ptr es:[di],0020h ; space and null terminator ret wrtargc endp ; write \v(COUNT) text to ds:di wrtcnt proc near xor ax,ax cmp taklev,0 ; in a Take/Macro? je wrtcnt1 ; e = no mov bx,takadr ; current Take structure mov ax,[bx].takctr ; get COUNT wrtcnt1:call fdec2di ; write as ascii ret wrtcnt endp ; write \v(DATE) text to ds:di wrtdate proc near push cx push dx mov ah,getdate ; DOS date (cx= yyyy, dh= mm, dl= dd) int dos xor ah,ah mov al,dl ; day call wrtdat5 mov byte ptr es:[di],' ' inc di xor bh,bh mov bl,dh ; month dec bx ; count from zero shl bx,1 ; count four byte groups shl bx,1 mov ax,word ptr month[bx] stosw mov ax,word ptr month[bx+2] stosw mov ax,cx ; year wrtdat3:call wrtdat5 mov word ptr es:[di],0020h ; space and null terminator pop dx pop cx ret wrtdat5:cmp ax,10 ; leading tens digit present? jae wrtdat6 ; ae = yes mov byte ptr es:[di],'0' ; insert leading 0 inc di wrtdat6:call fdec2di ; write decimal asciiz to buffer ret wrtdate endp ; write \v(ERRORLEVEL) text to ds:di wrterr proc near mov al,errlev ; current Errorlevel xor ah,ah call fdec2di ; write as ascii mov word ptr es:[di],0020h ; space and null terminator ret wrterr endp ; write \v(KEYBOARD) text to ds:di wrtkbd proc near mov ax,keyboard ; 88 or 101 keyboard keys call fdec2di ; write as ascii mov word ptr es:[di],0020h ; space and null terminator ret wrtkbd endp ; write \v(NDATE) text to ds:di ; where NDATE is YYYYMMDD wrtndate proc near mov ah,getdate ; DOS date (cx= yyyy, dh= mm, dl= dd) int dos push dx ; save dx mov ax,cx ; year call fdec2di ; convert it pop dx ; get mm:dd push dx mov al,dh ; months are next xor ah,ah cmp al,10 ; less than 10? jae wrtndat1 ; ae = no mov byte ptr es:[di],'0' ; leading 0 inc di wrtndat1:call fdec2di pop dx mov al,dl ; get days xor ah,ah cmp al,10 ; less than 10? jae wrtndat2 ; ae = no mov byte ptr es:[di],'0' ; leading 0 inc di wrtndat2:call fdec2di mov word ptr es:[di],0020h ; space and null terminator ret wrtndate endp ; write \v(DIRECTORY) text to es:di wrtdir proc near push si mov ah,gcurdsk ; get current disk int dos add al,'A' ; make al = 0 == 'A' stosb mov ax,'\:' stosw mov si,di ; work buffer push ds mov cx,es mov ds,cx mov ah,gcd ; get current directory xor dl,dl ; use current drive int dos ; get ds:si = asciiz path (no drive) pop ds mov dx,si push ds mov cx,es mov ds,cx call strlen pop ds add di,cx wrtdir1:mov word ptr es:[di],0020h ; space and null terminator pop si ret wrtdir endp fwrtdir proc far call wrtdir ret fwrtdir endp ; write \v(PLATFORM) text to ds:di wrtplat proc near push si mov si,offset machnam ; machine name in sys dep file cld wrtplat1:lodsb ; get a char cmp al,'$' ; terminator? je wrtplat2 ; e = yes stosb ; store char jmp short wrtplat1 ; keep going wrtplat2:mov word ptr es:[di],0020h ; space and null terminator pop si ret wrtplat endp ; write \v(PORT) text to ds:di wrtport proc near push bx push si mov al,flags.comflg ; get coms port indicator mov bx,offset comptab ; table of comms ports mov cl,[bx] ; number of entries xor ch,ch inc bx wrtpor3:mov dx,[bx] ; length of this entry mov si,bx add si,2 ; points to entry text string add si,dx ; point to qualifier cmp [si],al ; our port? je wrtpor4 ; e = yes add bx,[bx] ; add text length add bx,4 ; plus count and qualifier loop wrtpor3 ; next entry jmp short wrtpor5 ; no match, curious wrtpor4:mov si,bx ; point at entry add si,2 ; point at string mov cx,[bx] ; length of string cld rep movsb ; copy to DS:DI wrtpor5:mov word ptr es:[di],0020h ; space and null terminator pop si pop bx ret wrtport endp ; write \v(PROGRAM) text to ds:si wrtprog proc near push si mov si,offset progm ; source string cld wrtprg1:lodsb cmp al,'$' ; terminator? je wrtprg2 ; e = yes stosb ; store the char jmp short wrtprg1 wrtprg2:mov word ptr es:[di],0020h ; space and null terminator pop si ret wrtprog endp ; write \v(STATUS) text to ds:di wrtstat proc near mov ax,kstatus ; Kermit status word call fdec2di mov word ptr es:[di],0020h ; space and null terminator ret wrtstat endp ; write \v(SYSTEM) text to ds:di wrtsystem proc near push si mov si,offset system ; system string "MS-DOS", dollar sign cld jmp wrtplat1 ; use some common code wrtsystem endp ; write \v(TIME) text to ds:di wrttime proc near mov ah,gettim ; DOS tod (ch=hh, cl=mm, dh=ss, dl=.s) int dos push dx ; save dx xor ah,ah mov al,ch ; Hours cmp al,10 ; leading digit? jae wrttim1 ; ae = yes mov byte ptr es:[di],'0' ; make our own inc di wrttim1:call fdec2di ; write decimal asciiz to buffer mov byte ptr es:[di],':' inc di xor ah,ah mov al,cl ; Minutes cmp al,10 ; leading digit? jae wrttim2 ; ae = yes mov byte ptr es:[di],'0' ; make our own inc di wrttim2:call fdec2di ; write decimal asciiz to buffer mov byte ptr es:[di],':' inc di pop dx xor ah,ah mov al,dh ; Seconds cmp al,10 ; leading digit? jae wrttim3 ; ae = yes mov byte ptr es:[di],'0' ; make our own inc di wrttim3:call fdec2di ; write decimal asciiz to buffer mov word ptr es:[di],0020h ; space and null terminator ret wrttime endp ; write \v(Version) text to ds:di wrtver proc near mov si,offset verident ; MS Kermit version string in mssker cld wrtver1:lodsb stosb cmp al,'$' ; end of string? jne wrtver1 ; ne = no, continue copying dec di mov word ptr es:[di],0020h ; space and null terminator ret wrtver endp ; write \v(TERMINAL) text to ds:di wrtterm proc near mov ax,flags.vtflg ; current terminal type mov bx,offset termtb ; terminal type table msx... mov cl,[bx] ; number of entries in our table xor ch,ch inc bx ; point to the data wrtter1:mov si,[bx] ; length of keyword cmp ax,[bx+si+2] ; value fields match? je wrtter2 ; e = yes add bx,si ; add word length add bx,4 ; skip count and value fields loop wrtter1 ; keep searching jmp short wrtter4 ; no match, use just a space wrtter2:mov cx,[bx] ; get length of counted string mov si,bx add si,2 ; look at text cld rep movsb wrtter4:mov word ptr es:[di],0020h ; space and null terminator ret wrtterm endp fcmgtch proc far call cmgtch ret fcmgtch endp code1 ends code segment assume cs:code ; Set master prompt level. Enter with DX = offset of prompt string MPROMPT proc near mov mcmprmp,dx ; offset of prompt string pop ax ; get the return address mov word ptr mcmrprs,ax ; offset to go to on reparse mov mcmostp,sp ; stack pointer at reparse time push ax ; put it on the stack again mov ax,cs ; our current code segment mov word ptr mcmrprs+2,ax ; segment of reparse address mov comand.cmdirect,0 ; read normally (vs force to kbd/file) mov cmrawptr,offset rawbuf ; base/raw buffer write ptr, reset mov cmwptr,offset cmdbuf mov cmrptr,offset cmdbuf MPROMPT endp ; This routine prints the prompt and specifies the reparse address. ; Enter with pointer to prompt string in dx. PROMPT PROC NEAR xor ax,ax ; get a zero xchg al,comand.cmdirect ; set cmdirect to zero for showprompt xchg ah,comand.cmper push ax xor al,al xchg al,flags.takflg ; take echo flag, off in showprompt push ax call showprompt ; convert string with variables pop ax xchg al,flags.takflg ; restore take echo flag pop ax mov comand.cmdirect,al ; restore direct mode (if any) mov comand.cmper,ah mov comand.cmprmp,dx ; save the prompt pop ax ; get the return address mov word ptr comand.cmrprs,ax ; offset to go to on reparse mov comand.cmostp,sp ; save for later restoration push ax ; put it on the stack again mov ax,cs ; our current code segment mov word ptr comand.cmrprs+2,ax ; segment of reparse address mov ax,offset cmdbuf mov cmrptr,ax ; reset buffer read/write pointers mov cmwptr,ax mov cmrawptr,offset rawbuf mov in_reparse,0 xor ax,ax mov subcnt,al ; substitution variable state info mov subtype,al mov bracecnt,al mov replay_cnt,ax mov cmsiz,ax mov comand.cmper,al ; allow substitutions mov cmsflg,0ffh ; remove leading spaces cmp comand.cmdirect,al ; doing direct reading? jne promp1 ; ne = yes cmp flags.takflg,al ; look at Take flag, zero? jne promp1 ; ne=supposed to echo, skip this check cmp taklev,al ; inside a take file? je promp1 ; e = no, keep going clc ret ; yes, return promp1: mov ah,prstr mov dx,offset crlf int dos mov dx,comand.cmprmp ; prompt pointer call prtasz ; show asciiz prompt string clc ret PROMPT ENDP ; Convert ASCIIZ prompt string given in DS:DX to string in DS:comand.cmprmp ; with expansion of substitution variables. Final prompt is in DX and in ; comand.cmprmp = offset cmdbuf+cmdblen/2 (of a 1000 byte total buffer). showprompt proc far cmp in_showprompt,0 ; doing this routine already? je showp0 ; e = no mov dx,comand.cmprmp ; yes, return current prompt string ret showp0: mov word ptr cmdbuf+cmdblen/2,0 ; clear final prompt string mov comand.cmprmp,offset cmdbuf+cmdblen/2 ; prompt pointer mov si,dx ; raw prompt string ds:dx call strlen ; get string length or cx,cx ; empty? jnz showp1 ; nz = no ret ; return empty prompt string showp1: inc cx ; include null in length mov al,taklev ; entering taklev cmp al,maxtak ; room in take level? jb showp2 ; b = yes mov comand.cmprmp,dx ; original raw prompt offset ret ; return it showp2: push ax ; save take level mov si,offset cmdbuf ; reset parse buffer pointers mov cmrptr,si mov cmwptr,si mov cmrawptr,offset rawbuf xor ax,ax mov subcnt,al ; clear substitution state variables mov subtype,al mov bracecnt,al call takopen_sub ; open take as text substitution mov bx,takadr ; address of take structure mov ax,ds mov [bx].takbuf,ax ; segment of Take buffer mov [bx].takptr,dx ; offset of beginning of def text mov [bx].takcnt,cx ; # of chars in buffer (includes NUL) or [bx].takattr,take_autocr ; imply CR at end of prompt mov in_showprompt,1 ; say making new prompt xor ax,ax mov cmsflg,al ; get all spaces mov comand.cmper,al ; reset to variable recognition mov comand.cmquiet,1 ; no echo mov comand.cmwhite,1 ; leading white space is ok mov comand.cmcr,1 ; bare cr's allowed mov comand.cmostp,sp ; set reprompt stack pointer here mov comand.cmcnvkind,cmcnv_none ; default is no conversion mov cmkind,cmword ; say getting a word, not a line mov comand.cmdonum,0 ; disable \number expansion mov word ptr comand.cmrprs,offset showp3 ; offset for reparse mov ax,cs ; our current code segment mov word ptr comand.cmrprs+2,ax ; segment of reparse address showp3: mov cmptab,offset cmdbuf+cmdblen/2 ; buffer for new string mov comand.cmblen,(cmdblen/2)-100 ; length of our buffer (plenty) showp4: call fcmgtch ; get a char jnc showp5 ; nc = non-terminator cmp ah,' ' ; space terminator? je showp5 ; e = yes inc cmrptr ; 'read' other kinds of terminator showp5: mov bx,cmptab ; pointer into destination array mov [bx],ah ; put char into the buffer inc bx xor al,al mov [bx],al ; insert null terminator mov cmptab,bx cmp bx,offset cmdbuf+cmdblen ; end of buffer? jae showp6 ; ae = end of buffer, terminate mov cmsflg,0 ; still get spaces or ah,ah ; this terminator? jnz showp4 ; nz = no, keep reading showp6: pop ax ; saved entering taklev cmp al,taklev ; saved versus current taklev jae showp7 ; ae = nothing new to be removed push ax ; save again for loop call takclos ; close Take file jmp showp6 ; keep at it showp7: xor ax,ax mov comand.cmwhite,al ; clear leading whitespace flag mov comand.cmblen,ax ; set user buffer length to unknown mov comand.cmquiet,al ; enable echo mov comand.cmcr,al ; bare cr's not allowed mov comand.cmdonum,al ; disable \number expansion mov in_showprompt,al ; done making new prompt mov dx,offset cmdbuf+cmdblen/2 ; return in DX mov comand.cmprmp,dx ; returned prompt pointer mov subcnt,al mov subtype,al ret showprompt endp code ends code1 segment assume cs:code1 ISDEV PROC FAR ; Set carry if STDIN is non-disk push ax push bx push dx xor bx,bx ; handle 0 is stdin xor al,al ; get device info mov ah,ioctl int dos rcl dl,1 ; put ISDEV bit into the carry bit pop dx ; carry is set if device pop bx pop ax ret ; carry set if device ISDEV ENDP ISEOF PROC FAR ; Set carry if STDIN is at EOF push ax ; but only if stdin is a non-device push bx push dx xor bx,bx ; handle 0 is stdin xor al,al ; get device info mov ah,ioctl int dos mov ah,ioctl mov al,6 ; get handle input status, set al test dl,80h ; bit set if handle is for a device 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. ; All registers are preserved except AX, of course. TOLOWR PROC FAR 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 lower case tolow2: ret TOLOWR endp TOUPR PROC FAR cmp ah,'a' ; less that lower A? jb toup1 ; l = yes. leave untouched cmp ah,'z' ; more than lower Z? ja toup1 ; ns = yes and ah,not 20H ; convert to upper case toup1: cmp al,'a' ; less that lower A? jb toup2 ; l = yes. leave untouched cmp al,'z' ; more than lower Z? ja toup2 ; ns = yes and al,not 20H ; convert to upper case toup2: ret TOUPR endp ; Revise string in ds:si et seq. Expect incoming length to be in CX. ; Controlled by comand.cmcnvkind. ; Original string modified to replace bare commas with Carriage Returns. ; Top level braces removed, but only if the string begins with an ; opening brace, and hence commas revealed are also converted to CR. ; Converted text has a forced null termination to assist callers of cmtxt ; which don't remember returned byte count. ; All registers preserved except CX (returned length). unbrace proc far or cx,cx ; count of zero or less? jg unbrac1 ; g = no, but could be huge ret unbrac1:push ax push si ; src and dest are ds:si push di mov di,si ; si = source add di,cx ; skip to end of line + 1 unbrac2:cmp byte ptr [di-1],' ' ; trailing space? jne unbrac3 ; ne = no dec di ; backup another byte loop unbrac2 unbrac3:cmp byte ptr [si],braceop ; now opens with brace? jne unbrac4 ; ne = no, nothing to do cmp byte ptr [di-1],bracecl ; ended on brace? jne unbrac4 ; ne = no, nothing to do mov cx,di ; ending spot sub cx,si ; minus start mov di,si ; destination sub cx,2 ; minus opening and closing braces inc si ; skip opening brace push cx push es mov ax,ds mov es,ax cld rep movsb ; copy down text inside braces xor al,al mov bx,di ; report null in BX stosb ; forced null terminator pop es pop cx unbrac4:pop di pop si pop ax clc ret unbrace endp ; Parse control sequences and device control strings. ; Expect CSI, Escape [, or DCS lead-in characters to have been read. ; Puts numerical Parameters in array param (16 bits, count is nparam) and ; a single letter Parameter in lparam, (Parameters are all ASCII column 3) ; Intermediate characters in array inter (count is ninter), (ASCII column 2) ; Final character in AL (ASCII columns 4-7). ; Invoke by setting state to offset atparse, set pardone to offset of ; procedure to jump to after reading Final char (0 means do just ret) ; and optionally setting parfail to address to jump to if parsing failure. ; When the Final char has been accepted this routine jumps to label held in ; pardone for final action. Before the Final char has been read successful ; operations return carry clear. ; Failure exits are carry set, and an optional jump through parfail (if ; non-zero) or a return. atparse proc near mov bx,parstate ; get parsing state or bx,bx ; have any state? jnz atpars1 ; nz = have a state call atpclr ; do initialization mov bx,parstate ; get initial state atpars1:call bx ; execute it jc atpfail ; c = failure cmp parstate,offset atpdone ; parsed final char? je atpdone ; e = yes ret ; no, wait for another char ; successful conclusion, final char is in AL atpdone:mov parstate,0 ; reset parsing state cmp pardone,0 ; separate return address defined? jne atpdon1 ; ne = yes clc ret ; else just return atpdon1:clc jmp pardone ; jmp to supplied action routine atpfail:mov parstate,0 ; failed, reset parser to normal state cmp parfail,0 ; jump address specified? je atpfail1 ; e = no jmp parfail ; yes, exit this way atpfail1:stc ret ; parsing workers atparm: cmp ninter,0 ; Parameter, started intermediate yet? jne atinter ; ne = yes, no more parameters cmp al,';' ; argument separator? jne atparm3 ; ne = no mov ax,nparam ; number of Parameters inc ax ; say a new one cmp ax,maxparam ; too many? jb atparm2 ; b = no, continue stc ; set carry to say failed ret ; too many, ignore remainder atparm2:mov nparam,ax ; say doing another Parameter clc ret atparm3:mov ah,al ; copy char and ah,not 0fh ; ignore low nibble cmp ah,30h ; column 3, row 0? (30h='0') jne atparm6 ; ne = no, check Intermediate/Final cmp al,'9' ; digit? ja atparm5 ; a = no, check letter Parameters sub al,'0' ; ascii to binary mov bx,nparam ; current parameter number shl bx,1 ; convert to word index mov cx,param[bx] ; current parameter value shl cx,1 ; multiply by 10. 2 * cl push bx mov bx,cx ; save 2 * cl shl cx,1 ; 4 * cl shl cx,1 ; 8 * cl add cx,bx ; 10 * cl pop bx add cl,al ; add new digit adc ch,0 jnc atparm4 ; nc = no carry out (65K or below) mov cx,0ffffh ; set to max value atparm4:mov param[bx],cx ; current Parameter value clc ret ; check non-numeric Parameters atparm5:cmp al,'?' ; within column 3? ja atfinal ; a = no, check Final char mov lparam,al ; store non-numeric Parameter clc ret atparm6:cmp nparam,0 ; started a parameter yet? jne atparm7 ; ne = yes cmp param,0 ; got anything for param[0]? je atinter ; e = no atparm7:inc nparam ; yes, say finished with another atinter:mov parstate,offset atinter ; next state (intermediate) cmp al,';' ; argument separator? jne atinte1 ; ne = no cmp ninter,maxinter ; too many intermediates? jb atinte2 ; b = no, continue stc ; carry = failed ret ; too many, ignore remainder atinte1:test al,not 2fh ; column two = 20h - 2fh? jnz atfinal ; nz = not an Intermediate, try Final cmp ninter,maxinter ; too many intermediates? jb atinte1a ; b = no, continue stc ; carry = failed ret ; too many, ignore remainder atinte1a:mov bx,ninter ; current Intermediate slot number mov inter[bx],al ; current Intermediate value atinte2:inc ninter ; say doing another Intermediate clc ret atfinal:cmp al,40h ; Final character, range is 40h to 7fh jb atfina1 ; b = out of range cmp al,7fh ja atfina1 ; a = out of range mov parstate,offset atpdone ; next state is "done" clc ; success, final char is in AL ret atfina1:stc ; c = failed ret atparse endp ; Clear Parameter, Intermediate arrays in preparation for parsing atpclr proc near push ax push cx push di push es xor ax,ax ; get a null mov parstate,offset atparm ; init parser state mov lparam,al ; clear letter Parameter mov nparam,ax ; clear Parameter count mov cx,maxparam ; number of Parameter slots mov di,offset param ; Parameter slots push ds pop es ; use data segment for es:di below cld ; set direction forward rep stosw ; clear the slots mov ninter,ax ; clear Intermediate count mov cx,maxinter ; number of Intermediate slots mov di,offset inter ; Intermediate slots rep stosb ; clear the slots pop es pop di pop cx pop ax ret atpclr endp ; Dispatch table processor. Enter with BX pointing at table of {char count, ; address of action routines, characters}. Jump to matching routine or return. ; Enter with AL holding received Final char. atdispat proc near mov cl,[bx] ; get table length from first byte xor ch,ch mov di,bx ; main table add di,3 ; point di at first char in table push es push ds pop es ; use data segment for es:di below cld ; set direction forward repne scasb ; find matching character pop es je atdisp2 ; e = found a match, get action addr ret ; ignore escape sequence atdisp2:sub di,bx ; distance scanned in table sub di,4 ; skip count byte, address word, inc shl di,1 ; convert to word index inc bx ; point to address of action routines mov bx,[bx] ; get address of action table jmp word ptr [bx+di] ; dispatch to the routine atdispat endp code1 ends end