;=============================================================================== ; vecho.asm ; Echoes command line to stdout, interpreting special symbols ; to echo escape characters, prompt args, and environment symbols. ; Usage ; VECHO char... ; ; Notes ; Assemble with 'has_help' equated to true if program usage help text ; printing is desired, false if not. ; ;------------------------------------------------------------------------------- ; Designed for Microsoft MS-DOS 2.x (16-bit). ; There is no guarantee that this code will operate as expected on 32-bit DOS ; systems. ; ; Copyright ŠAug 1986 by David R Tribble, all rights reserved. ; Permission is granted to use, distribute, and modify this source code provided ; that the original copyright (authorship) notice remains intact. ; ; History ; 1.0 1986-08-14 drt. ; First version. ; 1.1 1986-08-15 drt. ; Added $ and more \ functions. ; 1.2 1986-08-15 drt. ; Removed $ and % functions, added $name$ interpretation. ; 1.3 1986-08-16 drt. ; Added version (\V) option. ; 1.4 1986-08-20 drt. ; Fixed $name$ arg handling. ; 1.5 1987-01-31 drt. ; Added usage msg text. ; Made i/o simpler. ; 1.6 1987-04-01 drt. ; Compressed memory size (pathspec). ; 1.7 1987-07-24 drt. ; Added more escape seqs. ; 1.8 1987-08-06 drt. ; Added time (\T), date (\D), and weekday (\W) escape seqs. ; 1.9 1987-09-04 drt. ; Added day (\I), month (\M), and year (\Y) escape seqs. ; 1.10 2004-04-25 drt. ; Cleaned up quite a bit, added better comments. ; Fixed Y2K bug in date (\D) handling. ;------------------------------------------------------------------------------- name vecho true equ 1 false equ 0 has_help equ true ;usage help text printed nul equ 0 bel equ 7 bs equ 8 ht equ 9 lf equ 10 vt equ 11 ff equ 12 cr equ 13 subz equ 26 escc equ 27 del equ 127 int_dos equ 21h ;dos function interrupt dos_getdisk equ 19h ;get current drive dos_getdate equ 2Ah ;get current date dos_gettime equ 2Ch ;get current time dos_version equ 30h ;get dos version dos_writeh equ 40h ;write to handle dos_getpath equ 47h ;get directory path dos_term equ 4Ch ;terminate bios_mem equ 12h ;system memory size stat_okay equ 0 ;return status: no error stat_err equ 1 ;return status: error stdout equ 1 ;standard output handle stderr equ 2 ;standard error output ;------------------------------------------------------------------------------- ; code code segment assume cs: code assume ds: code assume es: code assume ss: nothing org 0000h ; program segment prefix psp_int db 2 dup (?) ;int 20h instruction psp_top dw 1 dup (?) ;top of memory psp_1 db 1 dup (?) ;reserved psp_dos db 1 dup (?) ;long call to dos psp_segsz dw 1 dup (?) ;available bytes in segment psp_2 db 2 dup (?) ;dos call cont'd psp_term dw 2 dup (?) ;terminate address psp_break dw 2 dup (?) ;ctrl-break address psp_error dw 2 dup (?) ;critical dos error address psp_3 db 22 dup (?) ;reserved psp_env dw 1 dup (?) ;environment segment para psp_4 db 46 dup (?) ;reserved psp_fcb1 db 16 dup (?) ;unopened arg 1 fcb psp_fcb2 db 20 dup (?) ;unopened arg 2 fcb psp_cmdlen db 1 dup (?) ;command length psp_cmd db 127 dup (?) ;command sans program name ;------------------------------------------------------------------------------- ; entry from dos org 0100h main proc far jmp vecho ;main program code main endp ;------------------------------------------------------------------------------- ; data dateyr dw 0 ;current date datemon db 0 dateday db 0 datewday db 0 timehr db 0 ;current time timemin db 0 timesec db 0 timetic db 0 outc db 0, 0, 0, 0 ;small output buffer lastnl db true ;print trailing newline crlf db cr, lf ;newline pair ;------------------------------------------------------------------------------- ; vecho ; Echoes command line to stdout. Also interprets arguments ; beginning with one of the special characters '\' or '$'. ; All other command line characters (including whitespace) are ; echoed as is. ; If no args are given, then usage help text is printed. ;------------------------------------------------------------------------------- vecho proc ; entry push cs ;ds = cs pop ds ; begin ; get time mov ah, dos_gettime int int_dos mov timehr, ch mov timemin, cl mov timesec, dh mov timetic, dl ; get date mov ah, dos_getdate int int_dos mov dateyr, cx mov datemon, dh mov dateday, dl mov datewday, al ; parse command line mov si, offset psp_cmd+1 ;ds:si = command line ptr sub cx, cx ;cx = command length mov cl, psp_cmdlen if has_help or cl, cl ;any args? jz m_noargs ;no endif m_nextarg: mov dx, si ;dx = start of next arg dec cx ;dec count jle m_nomore lodsb ;al = cmd[si] cmp al, '\' ;char = '\' ? je m_slash ;yes cmp al, '$' ;char = '$' ? je m_dollar ;yes m_norm: push cx ;print char as is mov cx, 1 call putbuf pop cx jmp m_nextarg m_slash: call slash ;print \ escape seq jmp m_nextarg m_dollar: call dollar ;print $ escape seq jmp m_nextarg if has_help m_noargs: ; no args at all, print usage msg mov dx, offset usagetxt ;write usage text mov cx, usagetxtz - usagetxt mov bx, stderr mov ah, dos_writeh int int_dos jmp short m_term endif m_nomore: ; no more command line chars ; print trailing newline cmp lastnl, false ;\s given? je m_term ;yes mov dx, offset crlf ;write newline pair mov cx, 2 mov bx, stdout mov ah, dos_writeh int int_dos m_term: mov al, stat_okay ;return status m_exit: ; terminate mov ah, dos_term ;terminate int int_dos ; just in case ret far vecho endp ;------------------------------------------------------------------------------- ; slash ; Interpret command line arg beginning with '\'. Unrecognized ; characters are printed as is. ;------------------------------------------------------------------------------- slash proc ; enter push ax push bx push dx ; begin lodsb ;al = cmd[si] dec cx push cx jg s_nchk ;jle s_exit jmp s_exit s_nchk: cmp al, 'n' ;newline? jne s_schk mov dx, offset crlf mov cx, 2 jmp short s_putbuf s_schk: cmp al, 's' ;suppress? jne s_ochk mov lastnl, false jmp short s_exit s_ochk: cmp al, '0' ;octal digit? jb s_cchk cmp al, '7' ja s_cchk sub cx, 2 pop bx push cx sub al, '0' mov ah, al lodsb sub al, '0' mov cl, 3 shl ah, cl or ah, al lodsb sub al, '0' mov cl, 3 shl ah, cl or ah, al mov bx, dx mov [bx], ah mov cx, 1 jmp short s_putbuf s_cchk: mov bx, offset slchar ;check for char in table mov cx, 1 s_cloop: mov ah, [bx] inc bx mov dx, bx or ah, ah ;end of table? jz s_fchk cmp al, ah je s_putbuf ;found it inc bx jmp s_cloop s_fchk: mov bx, offset slfunc ;check for char in func table mov cx, 1 s_floop: mov ah, [bx] inc bx or ah, ah ;end of table? jz s_norm cmp al, ah je s_ffound ;found it add bx, 2 jmp s_floop s_ffound: call word ptr [bx] jmp s_exit s_norm: mov outc, al ;print char as is mov dx, offset outc s_putbuf: call putbuf s_exit: ; return pop cx pop dx pop bx pop ax ret slash endp ;------------------------------------------------------------------------------- slchar db 'a', bel ;'\' character table db 'b', bs db 'd', del db 'e', escc db 'f', ff db 'l', lf db 'q', "'" db 'Q', '"' db 'r', cr db 't', ht db 'v', vt db 'w', '?' db 'z', subz db '!', '|' db '(', '<' db ')', '>' db '-', '=' db '#', '%' db '.', ' ' db 0 ; '\' character function table slfunc: db 'D' dw offset date db 'I' dw offset day db 'M' dw offset month db 'N' dw offset drive db 'P' dw offset path db 'T' dw offset time db 'V' dw offset version db 'W' dw offset wkday db 'Y' dw offset year db 0 ;------------------------------------------------------------------------------- ; drive ; Print current drive letter (upper case). ;------------------------------------------------------------------------------- drive proc ; enter push dx ; begin mov ah, dos_getdisk int int_dos add al, 'A' mov outc, al mov dx, offset outc mov cx, 1 call putbuf ; return pop dx ret drive endp ;------------------------------------------------------------------------------- ; path ; Print current directory path name to stdout. ;------------------------------------------------------------------------------- path proc ; enter push si push dx ; begin mov dl, 0 ;default drive mov si, offset pathspec mov ah, dos_getpath int int_dos jc pt_exit ;error, no action pt_ends: cmp byte ptr [si], nul ;end of name? je pt_print inc si jmp pt_ends pt_print: mov cx, si mov dx, offset pathspec sub cx, dx call putbuf pt_exit: ; return pop dx pop si ret path endp ;------------------------------------------------------------------------------- ; version ; Print DOS version number to stdout. ;------------------------------------------------------------------------------- version proc ; enter push ax push bx push cx push dx ; begin mov ah, dos_version int int_dos ;al = major, ah = minor cmp al, 0 ;version 1.x? jne v_norm mov ax, 0001h ;1.00 v_norm: add al, '0' mov ver, al mov al, ah mov ah, 0 mov bl, 10 div bl add al, '0' mov ver+2, al add ah, '0' mov ver+4, ah v_put: mov cx, versz - ver mov dx, offset ver call putbuf ; return pop dx pop cx pop bx pop ax ret version endp ;------------------------------------------------------------------------------- ; time ; Print time of day to stdout. ;------------------------------------------------------------------------------- time proc ; use time from start ; convert hours sub ax, ax mov al, timehr mov dx, offset timebuf call dig2 ; convert minutes mov al, timemin add dx, 3 call dig2 ; convert seconds mov al, timesec add dx, 3 call dig2 ; print it sub dx, 6 mov cx, timebufsz - timebuf call putbuf ; return ret time endp ;------------------------------------------------------------------------------- ; date ; Print current date to stdout. ;------------------------------------------------------------------------------- date proc ; use date from start ; convert month sub ax, ax mov al, datemon mov dx, offset datebuf call dig2 ; convert day mov al, dateday add dx, 3 call dig2 ; convert year mov ax, dateyr mov bl, 100 div bl mov al, ah ;al = dateyr mod 100 add dx, 3 call dig2 ; print it sub dx, 6 mov cx, datebufsz - datebuf call putbuf ; return ret date endp ;------------------------------------------------------------------------------- ; year ; Print current year to stdout. ;------------------------------------------------------------------------------- year proc ; use date from start ; convert year mov ax, dateyr mov bl, 100 div bl mov bl, ah ;convert high digits mov ah, 0 mov dx, offset yearbuf call dig2 add dx, 2 ;convert low digits mov al, bl call dig2 ; print it sub dx, 2 mov cx, yearbufsz - yearbuf call putbuf ; return ret year endp ;------------------------------------------------------------------------------- ; day ; Print current day to stdout. ;------------------------------------------------------------------------------- day proc ; use date from start ; convert day sub ax, ax mov al, dateday mov dx, offset daybuf call dig2 ; convert leading zero to space cmp byte ptr daybuf, '0' jne day_p mov byte ptr daybuf, ' ' day_p: ; print it mov cx, daybufsz - daybuf call putbuf ; return ret day endp ;------------------------------------------------------------------------------- ; wkday ; Print current week day name to stdout. ;------------------------------------------------------------------------------- ; week day names wkdaysz equ 3 wkdays: db 'Sun', 'Mon', 'Tue', 'Wed' db 'Thu', 'Fri', 'Sat' wkday proc ; use date from start ; look up week day name sub ax, ax mov al, datewday mov cx, wkdaysz mul cx mov dx, offset wkdays add dx, ax ; print it call putbuf ; return ret wkday endp ;------------------------------------------------------------------------------- ; month ; Print current month name to stdout. ;------------------------------------------------------------------------------- ; week day names monthsz equ 3 months: db 'Jan', 'Feb', 'Mar', 'Apr' db 'May', 'Jun', 'Jul', 'Aug' db 'Sep', 'Oct', 'Nov', 'Dec' month proc ; use date from start ; look up month name sub ax, ax mov al, datemon dec al mov cx, monthsz mul cx mov dx, offset months add dx, ax ; print it call putbuf ; return ret month endp ;------------------------------------------------------------------------------- ; dollar ; Interpret command line arg delimited with '$'s. ; Meaning is: ; $name$ environment variable 'name' ; If environment variable name does not exist, nothing is printed. ;------------------------------------------------------------------------------- dollar proc ; enter push ds push ax push bx push dx push di ; begin ; get environment variable name mov di, si ;di = name[1] t_scan: mov al, [si] ;al = name[si] dec cx jle t_exit cmp al, 'a' ;cvt to upper case jb t_endt cmp al, 'z' ja t_endt add al, 'A' - 'a' mov [si], al t_endt: inc si cmp al, '$' ;end of name? jne t_scan ; search for matching environment name t_search: mov es, psp_env ;es:bx = env table ptr sub bx, bx push di ;name[1] ptr t_snext: pop di ;di = name[1] ptr mov al, es:[bx] cmp al, nul ;end of table? je t_exit push di t_sloop: mov al, es:[bx] inc bx cmp al, '=' ;end of env name? jne t_scont cmp byte ptr [di], '$' ;end of name[di]? je t_found jmp short t_sskip t_scont: cmp byte ptr [di], '$' ;end of name[di]? je t_sskip cmp al, [di] ;[es:bx] = name[di]? jne t_sskip inc di jmp t_sloop t_sskip: mov al, es:[bx] ;no, skip to next name inc bx cmp al, nul ;end of name? jne t_sskip jmp short t_snext t_found: pop di ;clean stack mov dx, bx ;es:dx = env str ptr push cx sub cx, cx t_nameend: cmp byte ptr es:[bx], nul ;end of env name? je t_put inc bx inc cx jmp short t_nameend t_put: push es ;ds:dx = env str ptr pop ds call putbuf pop cx t_exit: ; return pop di pop dx pop bx pop ax pop ds ret dollar endp ;------------------------------------------------------------------------------- ; dig2 ; Convert 2-digit value ax into [ds:dx] character buffer. ;------------------------------------------------------------------------------- dig2 proc ; enter push ax push bx xchg dx, si ; begin sub ah, ah mov bl, 10 div bl ;ah:al = ah:al / 10 add al, '0' ;high digit mov 0[si], al add ah, '0' ;low digit mov 1[si], ah ; return xchg dx, si pop bx pop ax ret dig2 endp ;------------------------------------------------------------------------------- ; putbuf ; Print out buffer [ds:dx], cx bytes, to stdout. ;------------------------------------------------------------------------------- putbuf proc ; enter push ax push bx ; begin jcxz pb_exit mov bx, stdout mov ah, dos_writeh int int_dos call wcheck pb_exit: ; return pop bx pop ax ret putbuf endp ;------------------------------------------------------------------------------- ; wcheck ; Check dos return from dos_writeh for error. If error, ; call abort. ;------------------------------------------------------------------------------- wcheck proc jc c_err cmp ax, cx je c_okay c_err: jmp abort c_okay: ret wcheck endp ;------------------------------------------------------------------------------- ; abort ; Abort program prematurely. ;------------------------------------------------------------------------------- abort proc ; write error msg to stderr push cs pop ds mov dx, offset errmsg mov cx, errsz - errmsg mov bx, stderr mov ah, dos_writeh int int_dos ; exit program mov al, stat_err jmp m_exit abort endp errmsg db cr, lf db 'vecho: write error', cr, lf errsz label byte ;------------------------------------------------------------------------------- ; data ; print buffers ; note: these buffers overlap ver db '0.' ;used by version timebuf db '00' ;used by time versz label byte db ':00:' daybuf label byte ;used by day datebuf db '00' ;used by date timebufsz label byte daybufsz label byte db '-00-' yearbuf db '00' ;used by year datebufsz label byte db '00' yearbufsz label byte ;------------------------------------------------------------------------------- ; data ; identification stamp rev_id db "@(#)" db "drt/src/cmd/vecho.asm " db "$Revision: 1.10 $ $Date: 2004/04/25 17:24:08 $" db cr, lf, nul usagetxt db "[vecho 1.10, David R Tribble, 2004-04-25]" pathspec label byte ;directory path name ;up to 65 bytes used ; usage message help text if has_help db cr, lf, lf db "usage:", ht, "vecho {c}", cr, lf db "where c can be a normal character or an" db " escape sequence.", cr, lf, lf db "Escape sequence $abc$ is replaced by the" db " value of environment variable", cr, lf db "ABC. Escape sequences preceded by a \ are" db " interpreted as:", cr, lf db ht, "\a", ht, "bell (beep)", ht db ht, "\T", ht, "time (HH:MM:SS)", cr, lf db ht, "\b", ht, "backspace", ht db ht, "\v", ht, "vertical tab", cr, lf db ht, "\d", ht, "delete", ht, ht db ht, "\V", ht, "DOS version (N.NN)", cr, lf db ht, "\D", ht, "date (MM-DD-YY)", ht db ht, "\w", ht, "'?'", cr, lf db ht, "\e", ht, "escape", ht, ht db ht, "\W", ht, "weekday (Aaa)", cr, lf db ht, "\f", ht, "formfeed", ht db ht, "\Y", ht, "year (NNNN)", cr, lf db ht, "\I", ht, "day of month (NN)" db ht, "\z", ht, "control-Z", cr, lf db ht, "\l", ht, "linefeed", ht db ht, "\.", ht, "space", cr, lf db ht, "\M", ht, "month (Aaa)", ht db ht, "\(", ht, "'<'", cr, lf db ht, "\n", ht, "newline (\r\l)", ht db ht, "\)", ht, "'>'", cr, lf db ht, "\N", ht, "current drive", ht db ht, "\!", ht, "'|'", cr, lf db ht, "\P", ht, "current path", ht db ht, "\#", ht, "'%'", cr, lf db ht, "\q", ht, "single quote (')" db ht, "\-", ht, "'='", cr, lf db ht, "\Q", ht, 'double quote (")' db ht, "\$", ht, "'$'", cr, lf db ht, "\r", ht, "return", ht, ht db ht, "\\", ht, "'\'", cr, lf db ht, "\s", ht, "suppress newline" db ht, "\ddd", ht, "octal ddd", cr, lf db ht, "\t", ht, "tab" db cr, lf usagetxtz label byte endif code ends end main ; end vecho.asm