Source file: /~heha/hs/dos/mrec_src.zip/STRINGS.ASM

	masm
COMMENT `	(haftmann#software)		+++FREEWARE+++
WRSTR	Ausgabe nullterminierter String
WRSTRLN	Ausgabe nullterminierter String mit NewLine
_ZKOLF	für WrStrLn erforderlich...
SKEND	Stringende nullterminierter Strings DS:SI suchen, KEIN Unterprogramm!
_GETENV	PE: DS:SI ASCIIZ (Großbuchst. nicht zwingend!) ohne '=', DS auf PSP-Seg.
	PA: ES:DI ASCOOZ oder CY=1: Nicht gefunden
	VR: AX,ES,DI
_PARAM0	PA: ES:DI ASCIIZ ParamStr(0) - eigener Dateiname
_SEEKNAME PE: DS:SI: ASCIIZ [Pfad\]Dateiname
	  PA: DS:SI: Zeigt auf Dateiname allein
_SEEKEXT  PE: DS:SI: ASCIIZ [Pfad\]Dateiname
	  PA: DS:SI: Zeigt auf Extension ab dem Punkt
_PATHCOPY PE: DS:SI: ASCIIZ [Pfad\]Dateiname
	      ES:DI: Puffer für ASCIIZ Pfad (80 Bytes o.ä.)
	  PA: DS:SI: zeigt auf Dateiname allein
	      ES:DI: zeigt auf Byte nach der Null im Puffer
_ISNAMECHAR Testet AL auf gültiges Zeichen für Dateiname, '.' eingeschlossen
_GLOBBING   Vergleicht Maske DS:SI mit Dateiname ES:DI; CY=1: unpassend
	    (UNIX-Globbing!) VR: AX
_SEEKINPATH PE: DS:SI Dateiname
		ES:DI Puffer ca.128 Zeichen
	    PA: Gefüllter Puffer; CY=1: Nicht gefunden
	- Enthält der Dateiname bereits einen Pfad, wird nur StrCpy ausgeführt
	- Zuerst wird das aktuelle Vrz. durchsucht
	- Ist nur ein Laufwerk angegeben, wird ebenfalls der Pfad abgesucht
	- Wurde keine Extension angegeben, wird .COM und .EXE angenommen;
	  niemals .BAT!
	- Eine leere Extension wird erzwungen durch nachgestellten Punkt
	- Die Umgebungsvariable, nach der gesucht wird, lautet PATH
	HINWEIS: Es erfolgt FindFirst! DTA lokal im Stack!
	(Gesicherte Funktion unter Novell Netware mit Executables,
	die nicht lesbar sind! Geschwindigkeitsgewinn bei residentem DIET.)
_ZKOUT	Ausgabe nullterminierter String auf StdOut, PE: DS:SI, VR: AL,F
	Zieht ZKOUT2 nach sich!
_ZKOUT2 Ausgabe nullterminierter String auf StdOut, PE: DS:DX, VR: AL,F
	Innerhalb von ZKOUT2 definiert: ZKOUTH Ausgabe nullterminierter
	String auf Handle-File, PE: DS:DX, BX=Handle (offen!) VR: AL,F
_STRCPY	StringCopy normal ds:si->es:di
_STRCPR	StringCopy revers es:di->ds:si für Environmentvariablen
_STRCPX	StringCopy bis Weißraum ds:si->es:di, zieht _CHKWS nach sich
	Bei allen StringCopy-Aktionen:	PA: SI,DI zeigen HINTER die Null
					VR: SI,DI,AL,F
_STRCMP	StringCopmare case-sensitiv
_STRICMP StringCompare case-insensitiv
_INSTR	Stringsuche case-sensitiv
	ES:DI Langer String, DS:SI Kurzer String, PA: BX: Position
_INSTRI	Stringsuche case-insensitiv
_YESNO	Gibt '? [Y/N] ' auf Bildschirm aus und wartet auf genau diese beiden
	Tasten. Anschließend Ausgabe des geupcasten Zeichens und CRLF
	Zieht _CRLF nach sich!
_JANEIN	Dito in deutsch
_INLIN	Primitive Zeileneingabe mit Ausgabe nullterminierter String
	PE: ES:DI=Puffer, CX: Puffergröße VR:AX; InSec definiert
	echolose Eingabe für Paßwörter
_ERRMG	DOS-Nummer-Fehlermeldungen deutsch, AL=Fehler-Nummer
`
	ideal

macro SKEND	;;Suche Stringende von DS:SI
	local	sk1
sk1:	lodsb
	or	al,al
	jnz	sk1
	endm

macro WRSTR w1	;;Write nullterminierter String
	mov	dx,ofs w1
	call	zkout2
	endm

macro WRSTRLN w1	;;Write nullterminierter String mit NewLine
	mov	dx,ofs w1
	call	zkonl
	endm

macro _ZKONL		;;Ausgabe ASCIIZ (DX) mit anschließendem CRLF
			;;zieht CRLF aus PROLOG und ZKOUT2 nach sich!
zkonl:	call	zkout2
	_CRLF
	_ZKOUT2
	endm

macro _GETENV		;GetEnvVar(PChar):PChar, CY=1: Fehler
			;Voraussetzung: ENV existiert!
proc getenv
	mov	es,[2ch]
	xor	di,di
	xor	ax,ax
	cld
	jr	@@f
@@l:
	push	si
@@1:	 lodsb
	 call	UpCase
	 or	al,al
	 jz	@@2
	 scasb
	 jz	@@1
	pop	si
	jr	@@3
@@2:
	 mov	al,'='
	 scasb
	pop	si
	je	@@e		;gleich
@@3:				;ungleich
	dec	di
	mov	al,ah		;AL=0
@@4:	scasb
	jnz	@@4
	jr	@@l
@@f:	;Fußgesteuerte While-Schleife
	cmp	[by es:di],1	;Ende Env?
	jnc	@@l
@@e:	ret
	endp
	endm

macro _PARAM0		;ParamStr(0) ermitteln, CY=1: Fehler
proc param0
	mov	es,[2ch]
	xor	ax,ax
	cld
	jr	@@2	;falls ENV leer! - könnt ja sein...
@@1:	scasb
	jnz	@@1
@@2:	scasb
	jnz	@@1
	inc	ax	;ax=1
	scasw
	ret
	endp
	endm

macro _ISNAMECHAR
proc IsNameChar
	call	UpCase
	cmp	al,'!'
	jc	@@e
	cmp	al,''''
	cmc
	jnc	@@e
	cmp	al,'-'
	jz	@@e
	cmp	al,'.'
	jz	@@e
	cmp	al,'0'
	jc	@@e
	cmp	al,':'
	cmc
	jnc	@@e
	cmp	al,'@'
	jc	@@e
	cmp	al,'['
	cmc
	jnc	@@e
	cmp	al,80h
@@e:	ret
	endp
	endm

macro _GLOBBING		;DS:SI=Maske, ES:DI=Name
proc Globbing
	cld
@@r:	push	si di
@@l:	lodsb			;Maske
	mov	ah,[es:di]	;Name
	or	al,al
	jnz	@@1
	cmp	al,ah		;Auch Null? CY=1 wenn ah<>0! (Stringenden
				; fallen nicht zusammen!)
	jr	@@e		;okay oder auch nicht!
@@1:
	cmp	al,'*'
	jnz	@@2
@@3:	call	@@r		;rekursiv!
	jnc	@@e
	xor	al,al
	scasb
	jc	@@3
	jr	@@f		;Fehler
@@2:
	inc	di
	or	ah,ah
	jz	@@f
	cmp	al,'?'
	jz	@@l
	call	UpCase
	xchg	ah,al
	call	UpCase
	cmp	al,ah
	jz	@@l
@@f:	stc
@@e:	pop	di si
	ret
	endp
	endm

macro _SEEKNAME		;Sucht Dateinamen selbst im kompletten Pfad (DS:SI)
proc SeekName
	cld
	push	dx
	mov	dx,si	;merken
	SKEND		;Ende suchen
	jr	@@f
@@l:
	mov	al,[si-1]
	cmp	al,'/'
	jz	@@e
	cmp	al,'\'
	jz	@@e
	cmp	al,':'
	jz	@@e
@@f:	dec	si
	cmp	si,dx
	jnz	@@l
@@e:	pop	dx
	ret
	endp
	endm

macro _PATHCOPY		;Pfad-Copy kopiert nur den Pfad aus DS:SI nach ES:DI
			;DI zeigt am Ende hinter die Null, SI zeigt auf Name
			;(fast schon ein perfektes FSPLIT, oder?)
	_SEEKNAME
proc PathCopy
	push	cx dx
	mov	dx,si
	call	SeekName
	mov	cx,si
	sub	cx,dx
	xchg	si,dx
	rep	movsb		;schaufeln, notfalls 0 Bytes
	xor	al,al
	stosb			;Abschluß-Null
	xchg	dx,si
	pop	dx cx
	ret
	endp
	endm

macro _SEEKEXT		;Extension suchen, CY=1 wenn keine vorhanden
proc SeekExt
	cld
	call	SeekName
@@se1:	lodsb
	cmp	al,1
	jc	@@see
	cmp	al,'.'
	jnz	@@se1
@@see:	dec	si
	ret
	endp
	endm

macro _SEEKINPATH	;Sucht DS:DI im Pfad (endlich mal was richtiges!)
	;benötigt _STRCPX, _STRCPR, _SEEKNAME und _SEEKEXT
proc SeekInPath
@@dta	equ	bp-2ch
@@oldta	equ	dword bp-30h
@@path	equ	dword bp-34h
@@src	equ	dword bp-38h
@@dest	equ	dword bp-3ch
@@name	equ	dword bp-40h
	cld
	entr	40h	;2ch für DTA, 4 für Adresse zum alten DTA?
			;4 für PATH-Adresse, 4 für Quell-Adresse
	push	bx dx
	sds	<@@src>,si
	ses	<@@dest>,di
	DOS	2fh	;GetDTA
	ses	<@@oldta>,bx
	LD	ds,ss
	lea	dx,[@@dta]
	DOS	1ah	;SetDTA
	lds	si,[@@src]
	les	di,[@@dest]
	call	StrCpy
	call	@@ff	;hidden, system, r/o seien erlaubt!
	jnc	@@e	;bereits alles OKAY!
	lds	si,[@@src]
	mov	dx,si
	call	SeekName
	sds	<@@name>,si
	sub	si,dx
	jz	@@1
	cmp	si,2
	stc
	jnz	@@e	;Fehler, da Pfad angegeben wurde
	mov	si,dx
	cmp	[by si+1],':'	;Laufwerksbezeichner?
	stc
	jnz	@@e
@@1:	LD	ds,cs
	mov	si,ofs @@path$
	call	GetEnv
	jc	@@e	;Armer Rechner! (Kein PATH definiert)
	ses	<@@path>,di

@@3:	lds	si,[@@path]
	cmp	[by si-1],1
	jc	@@e		;raus mit Fehler
	les	di,[@@dest]
	mov	ah,';'
	call	StrCpH
	sds	<@@path>,si	;Neue Position speichern
	mov	[by es:di-1],'\';auf die Null drauf
	lds	si,[@@name]
	call	StrCpy
	call	@@ff		;FindFirst
	jc	@@3
@@e:	pushf
	  lds	dx,[@@oldta]
	  DOS	1ah	;SetDTA
	popf
	lds	si,[@@src]
	les	di,[@@dest]
	pop	dx bx
	leav
	ret

@@ff:	;Spezielles FindFirst via @@dest und evtl. Suffix anbammeln
	 lds	si,[@@dest]
	 mov	dx,si
	 call	SeekExt
	 jnc	@@ff1	;Yeah, da is shoa eine Extension!
	 LD	es,cs
	 mov	di,ofs @@com$
	 push	si
	  call	StrCpR	;Revers!
	  call	@@ffu
	 pop	si
	 jnc	@@ffe	;Schon gefunden!
	 call	StrCpR	;jetzt kommt die Echse!
@@ff1:	 call	@@ffu
@@ffe:	ret

@@ffu:	push	cx
	 mov	cx,7
	 DOS	4eh
	pop	cx
	ret

@@com$	db	'.COM',0
	db	'.EXE',0
@@path$	db	'PATH',0
	endp
	endm

macro _zkout2
proc zkout2
	push	bx
	mov	bx,1		;Standardausgabe
	call	zkouth		;NEU: schneller als Einzelzeichenausgabe!
	pop	bx
	ret
	endp

proc zkouth			;Ausgabe via Handle BX
	push	cx si
	mov	si,dx
	mov	cx,-1
	cld
@@1:	inc	cx		;Länge in CX berechnen
	lodsb
	or	al,al
	jnz	@@1
	pop	si
	DOS	40h		;Blockwrite ist schneller!!
	pop	cx
	ret
	endp
	endm

macro _zkout		;Ausgabe nullterminierte Zeichenkette via SI:DX
			;VR: AL,F (Stark rudimentär...)
proc zkout
	xchg	dx,si
	call	zkout2
	xchg	si,dx
	ret
	endp
	endm

macro _STRCPY		;StringCopy normal dx:si->es:di
strcpy:
	lodsb
	stosb
	or	al,al
	jnz	strcpy
	ret
	endm

macro _STRCPR		;StringCopy revers
	_STRCPY
strcpr:	xchg	si,di
	EX	ds,es
	call	strcpy
	EX	ds,es
	xchg	si,di
	ret
	endm

macro _STRCPX		;erweitertes StrCpy: mit Weißraum am Ende
proc strcpx
	xor	ah,ah
strcph:	;kopiert bis Weißraum ODER dem Zeichen in AH!
@@2:	lodsb
	cmp	al,ah
	jz	@@3
	call	ChkWS
	jnz	@@1
@@3:	mov	al,0
@@1:	stosb
	jnz	@@2
	ret
	endp
	_CHKWS
	endm

macro _STRCOMP
proc StrComp	;richtiges StringCompare! Z=0: Strings ungleich
	;CY=1: String ES:DI größer als String DS:SI   VR: Flags
	push	si di
	cld
	jr	@@1
@@2:	cmp	[by si-1],0
	jz	@@e		;Beide Strings zu Ende
@@1:	cmpsb
	jz	@@2
@@e:	pop	di si
	ret
	endp
	endm

macro _STRICOMP
proc StrIComp	;StringCompare! mit simplem UpCase, Z=0: Strings ungleich
	;CY=1: String ES:DI größer als String DS:SI   VR: Flags
	push	ax si di
	cld
	jr	@@1
@@2:
	or	ax,ax
	jz	@@e		;Beide Strings zu Ende
@@1:	lodsb
	call	UpCase
	xchg	ah,al
	mov	al,[es:di]
	inc	di
	call	UpCase
	cmp	ah,al
	jz	@@2
@@e:	pop	di si ax
	ret
	endp
	endm

macro _INSTR
proc InStr	;InStr (Pascal: POS) liefert in BX Position des
		;Strings DS:SI in ES:DI
		;DS:SI darf nicht leer sein!
		;BX=0: Position Null!
		;CY=1: Nicht gefunden, dann BX unverändert
	push	ax cx si di
	mov	cx,-1
	cld
	lodsb
	cmp	al,1
	jc	@@e		;Fehler: Suchstring leer
@@3:	dec	si		;aufs 1. Zeichen kurzer String!
	lodsb
@@0:	cmp	[by es:di],1
	jc	@@e		;Großer String zu Ende
	inc	cx
	scasb
	jnz	@@0		;Ungleicher 1. Buchstabe
	push	si di
@@1:	 lodsb
	 or	al,al
	 jz	@@2
	 scasb
	 jz	@@1
@@2:	pop	di si
	jnz	@@3
	xchg	bx,cx
@@e:	pop	di si cx ax
	ret
	endp
	endm

macro _INSTRI
proc InStrI	;InStr (Pascal: POS) liefert in BX Position des
		;Strings DS:SI in ES:DI
		;DS:SI darf nicht leer sein!
		;BX=0: Position Null!
		;CY=1: Nicht gefunden, dann BX unverändert
	push	ax cx si di
	mov	cx,-1
	cld
	lodsb
	cmp	al,1
	jc	@@e		;Fehler: Suchstring leer
@@3:	dec	si		;aufs 1. Zeichen kurzer String!
	lodsb
	call	UpCase
	xchg	ah,al		;'hochladen'
@@0:	mov	al,[es:di]
	inc	di
	cmp	al,1
	jc	@@e		;Großer String zu Ende
	inc	cx
	call	UpCase
	cmp	ah,al
	jnz	@@0		;Ungleicher 1. Buchstabe
	push	si di
@@1:	 lodsb
	 or	al,al
	 jz	@@2
	 call	UpCase
	 xchg	ah,al		;'hochladen'
	 mov	al,[es:di]
	 inc	di
	 call	UpCase
	 cmp	ah,al
	 jz	@@1
@@2:	pop	di si
	jnz	@@3
	xchg	bx,cx
@@e:	pop	di si cx ax
	ret
	endp
	endm

macro _YESNO
proc YesNo			;Z=1 wenn JA
	push	dx ds cs
	pop	ds
	PRINT	@@3
@@1:
	DOS	8
	call	UpCase
	cmp	al,'Y'
	jz	@@2
	cmp	al,'N'
	jnz	@@1
@@2:	call	ochr
	call	crlf
	cmp	al,'Y'
	pop	ds dx
	ret
@@3:	db	'? [Y/N] $'
	endp
	_CRLF
	endm

macro _JANEIN
proc JaNein			;Z=1 wenn JA
	push	dx ds cs
	pop	ds
	PRINT	@@3
@@1:
	DOS	8
	call	UpCase
	cmp	al,'J'
	jz	@@2
	cmp	al,'N'
	jnz	@@1
@@2:	call	ochr
	call	crlf
	cmp	al,'J'
	pop	ds dx
	ret
@@3:	db	'? [J/N] $'
	endp
	_CRLF
	endm

macro _INLIN
proc Inlin	;Primitive Stringeingabe mit Echo, VR: AX
	mov	ah,1		;Funktions-Nummer!
	jr	InLine
InSec:	mov	ah,8		;ohne Echo fürs Paßwort
InLine:	;ES:DI=Stringpuffer, CX=max. Zeichenzahl+1 = Puffergröße
	push	dx cx
	mov	dx,di		;Anfang merken
@@4:	dec	cx
@@3:	mov	[by es:di],0	;Ende-Null
	DOS			;ohne Echo
	cmp	al,8		;Backstep?
	jz	@@1
	cmp	al,13		;Enter?
	jz	@@2
	jcxz	@@31		;Puffer voll
	stosb
	jr	@@4
@@31:	cmp	ah,8		;echolos?
	jz	@@3
	jr	@@5
@@1:	cmp	dx,di
	jz	@@3		;wirkungslos verpuffen lassen
	inc	cx
	dec	di
	mov	al,' '
	call	ochr
@@5:	mov	al,8
	call	ochr
	jr	@@3		;Null trägt sich von selbst ein
@@2:	call	crlf		;Zweckmäßig: neue Zeile!
	xchg	di,dx
	pop	dx cx
	ret
	endp
	endm

macro _INSEC
proc InSec	;Primitive Stringeingabe ohne Echo fürs Paßwort
	;ES:DI=Stringpuffer, CX=max. Zeichenzahl+1 = Puffergröße
	push	dx cx
	mov	dx,di		;Anfang merken
	cld
@@4:	dec	cx
@@3:	mov	[by es:di],0	;Ende-Null
	DOS	8		;ohne Echo
	cmp	al,ah		;Backstep?
	jz	@@1
	cmp	al,13		;Enter?
	jz	@@2
	jcxz	@@3		;Puffer voll
	stosb
	jr	@@4
@@1:	cmp	dx,di
	jz	@@3		;wirkungslos verpuffen lassen
	inc	cx
	dec	di
	jz	@@3
@@2:	call	crlf		;Zweckmäßig: neue Zeile!
	xchg	di,dx
	pop	dx cx
	ret
	endp
	endm

macro _ERRMG		;Deutsche DOS-Fehlermeldungen
			;erfordert ZKOUT2, AHEX
proc errmg
	mov	cl,al
	mov	ch,0
	WrStr	@@1$
	mov	al,cl
	call	ahex
	or	al,al
	jz	@@2
	cmp	al,9
	jnc	@@2
	mov	si,ofs @@2$
@@1:	SKEND
	loop	@@1
	WrStr	@@3$
	mov	dx,si
	call	zkout2	;Zeichenkettenausgabe
@@2:	jmp	crlf

@@1$:	db	'DOS-Fehler $',0
@@3$:	db	': ',0
@@2$:	db	'Unbekannte Funktionsnummer',0
	db	'Datei nicht gefunden',0
	db	'Pfad nicht gefunden',0
	db	'Kein Handle verfügbar',0
	db	'Zugriff verweigert',0
	db	'Ungültiges Handle',0
	db	'Speichersteuerblöcke zerstört',0
	db	'Nicht genügend Speicher',0
	endp
	endm

macro _ERRME		;Englische DOS-Fehlermeldungen
			;erfordert ZKOUT2, AHEX
proc errme
	mov	cl,al
	mov	ch,0
	WrStr	@@1$
	mov	al,cl
	call	ahex
	or	al,al
	jz	@@2
	cmp	al,9
	jnc	@@2
	mov	si,ofs @@2$
@@1:	SKEND
	loop	@@1
	WrStr	@@3$
	mov	dx,si
	call	zkout2	;Zeichenkettenausgabe
@@2:	jmp	crlf

@@1$:	db	'DOS-error 0x',0
@@3$:	db	': ',0
@@2$:	db	'Invalid function number',0
	db	'File not found',0
	db	'Path not found',0
	db	'No handle available',0
	db	'Access denied',0
	db	'Invalid handle',0
	db	'MCB's destroyed',0
	db	'Insufficient memory',0
	endp
	endm

Detected encoding: OEM (CP437)1
Wrong umlauts? - Assume file is ANSI (CP1252) encoded