[comp.sources.misc] v19i043: dmake - dmake version 3.7, Part22/37

dvadura@watdragon.waterloo.edu (Dennis Vadura) (05/13/91)

Submitted-by: Dennis Vadura <dvadura@watdragon.waterloo.edu>
Posting-number: Volume 19, Issue 43
Archive-name: dmake/part22
Supersedes: dmake-3.6: Volume 15, Issue 52-77

---- Cut Here and feed the following to sh ----
#!/bin/sh
# this is dmake.shar.22 (part 22 of a multipart archive)
# do not concatenate these parts, unpack them in order with /bin/sh
# file dmake/msdos/config.mk continued
#
if test ! -r _shar_seq_.tmp; then
	echo 'Please unpack part 1 first!'
	exit 1
fi
(read Scheck
 if test "$Scheck" != 22; then
	echo Please unpack part "$Scheck" next!
	exit 1
 else
	exit 0
 fi
) < _shar_seq_.tmp || exit 1
if test -f _shar_wnt_.tmp; then
sed 's/^X//' << 'SHAR_EOF' >> 'dmake/msdos/config.mk' &&
# config file.
.SOURCE.h : $(OS)
X
# See if we modify anything in the lower levels.
.IF $(OSRELEASE) != $(NULL)
X   .INCLUDE .IGNORE : $(OS)$(DIRSEPSTR)$(OSRELEASE)$(DIRSEPSTR)config.mk
.END
SHAR_EOF
chmod 0640 dmake/msdos/config.mk ||
echo 'restore of dmake/msdos/config.mk failed'
Wc_c="`wc -c < 'dmake/msdos/config.mk'`"
test 1663 -eq "$Wc_c" ||
	echo 'dmake/msdos/config.mk: original size 1663, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= dmake/msdos/dirbrk.c ==============
if test -f 'dmake/msdos/dirbrk.c' -a X"$1" != X"-c"; then
	echo 'x - skipping dmake/msdos/dirbrk.c (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
sed 's/^X//' << 'SHAR_EOF' > 'dmake/msdos/dirbrk.c' &&
/* RCS      -- $Header: /u2/dvadura/src/generic/dmake/src/msdos/RCS/dirbrk.c,v 1.1 91/05/06 15:25:29 dvadura Exp $
-- SYNOPSIS -- define the directory separator string.
-- 
-- DESCRIPTION
-- 	Define this string for any character that may appear in a path name
--	and can be used as a directory separator.
--
-- AUTHOR
--      Dennis Vadura, dvadura@watdragon.uwaterloo.ca
--      CS DEPT, University of Waterloo, Waterloo, Ont., Canada
--
-- COPYRIGHT
--      Copyright (c) 1990 by Dennis Vadura.  All rights reserved.
-- 
--      This program is free software; you can redistribute it and/or
--      modify it under the terms of the GNU General Public License
--      (version 1), as published by the Free Software Foundation, and
--      found in the file 'LICENSE' included with this distribution.
-- 
--      This program is distributed in the hope that it will be useful,
--      but WITHOUT ANY WARRANTY; without even the implied warrant of
--      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--      GNU General Public License for more details.
-- 
--      You should have received a copy of the GNU General Public License
--      along with this program;  if not, write to the Free Software
--      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
--
-- LOG
--     $Log:	dirbrk.c,v $
X * Revision 1.1  91/05/06  15:25:29  dvadura
X * dmake Release Version 3.7
X * 
*/
X
#include "extern.h"
X
/* dos uses /, \, and : */
char*	DirBrkStr = "/\\:";
X
/*
** Return TRUE if the name is the full specification of a path name to a file
** starting at the root of the file system, otherwise return FALSE
*/
PUBLIC int
If_root_path(name)
char *name;
{
X   return( (strchr(DirBrkStr, *name) != NIL(char)) ||
X           (isalpha(*name) && name[1] == ':') );
}
SHAR_EOF
chmod 0640 dmake/msdos/dirbrk.c ||
echo 'restore of dmake/msdos/dirbrk.c failed'
Wc_c="`wc -c < 'dmake/msdos/dirbrk.c'`"
test 1776 -eq "$Wc_c" ||
	echo 'dmake/msdos/dirbrk.c: original size 1776, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= dmake/msdos/dirlib.h ==============
if test -f 'dmake/msdos/dirlib.h' -a X"$1" != X"-c"; then
	echo 'x - skipping dmake/msdos/dirlib.h (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
sed 's/^X//' << 'SHAR_EOF' > 'dmake/msdos/dirlib.h' &&
/* DIRLIB.H by M. J. Weinstein   Released to public domain 1-Jan-89 */
X
#ifndef _DIRLIB_h_
#define _DIRLIB_h_
X
#include <stdio.h>
#include "stdmacs.h"
X
#define MAXNAMLEN   15
X
struct direct {
X   long              d_ino;
X   unsigned short    d_reclen;
X   unsigned short    d_namlen;
X   char              d_name[MAXNAMLEN+1];
};
X
typedef struct {
X   char   fcb[21];
X   char   attr;
X   short  time;
X   short  date;
X   long   size;
X   char   name[13];
} DTA;
X
typedef struct {
X   DTA    dd_dta;       /* disk transfer area for this dir.        */
X   short  dd_stat;      /* status return from last lookup          */
X   char   dd_name[1];   /* full name of file -- struct is extended */
} DIR;
X
extern DIR           *opendir   ANSI((char *));
extern struct direct *readdir   ANSI((DIR *));
extern long          telldir    ANSI((DIR *));
extern void          seekdir    ANSI((DIR *, long));
extern void          closedir   ANSI((DIR *));
extern DTA           *findfirst ANSI((char *, DTA *));
extern DTA           *findnext  ANSI((DTA *));
X
#define rewinddir(dirp)   seekdir(dirp,0L)
#endif
SHAR_EOF
chmod 0640 dmake/msdos/dirlib.h ||
echo 'restore of dmake/msdos/dirlib.h failed'
Wc_c="`wc -c < 'dmake/msdos/dirlib.h'`"
test 1086 -eq "$Wc_c" ||
	echo 'dmake/msdos/dirlib.h: original size 1086, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= dmake/msdos/exec.asm ==============
if test -f 'dmake/msdos/exec.asm' -a X"$1" != X"-c"; then
	echo 'x - skipping dmake/msdos/exec.asm (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
sed 's/^X//' << 'SHAR_EOF' > 'dmake/msdos/exec.asm' &&
; 
; DESCRIPTION
;      This code is a model independent version of DOS exec that will swap
;      the calling process out to secondary storage prior to running the
;      child.  The prototype for calling the exec function is below.
;
;      exec( int swap, char far *program, char far *cmdtail,
;	     int environment_seg, int env_size, char far *tmpfilename );
;
;
;      To assemble this file issue the command:
;
;         tasm /mx /t /dmmodel exec.asm
;
;      where 'model' is one of {small, compact, medium, large}, you may
;      also use MASM 5.1 to assemble this file, in this case simply replace
;      'tasm' with 'masm' in the above command line.
;
; AUTHOR
;      Dennis Vadura, dvadura@watdragon.uwaterloo.ca
;      CS DEPT, University of Waterloo, Waterloo, Ont., Canada
;
; COPYRIGHT
;      Copyright (c) 1990 by Dennis Vadura.  All rights reserved.
; 
;      This program is free software; you can redistribute it and/or
;      modify it under the terms of the GNU General Public License
;      (version 1), as published by the Free Software Foundation, and
;      found in the file 'LICENSE' included with this distribution.
; 
;      This program is distributed in the hope that it will be useful,
;      but WITHOUT ANY WARRANTY; without even the implied warrant of
;      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;      GNU General Public License for more details.
; 
;      You should have received a copy of the GNU General Public License
;      along with this program;  if not, write to the Free Software
;      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
;
ifdef have286
X .286    ; define have286 with -D for 80286 processor or better
X
else	; 8088/8086 compatible
X    mpusha Macro
X    	push ax
X	push cx
X	push dx
X	push bx
X	push sp
X	push bp
X	push si
X	push di
X    Endm
X
X    mpopa Macro
X    	pop di
X	pop si
X	pop bp
X	add sp,2
X	pop bx
X	pop dx
X	pop cx
X	pop ax
X    Endm
endif
X
ifdef msmall
X	        .model	small
argbase		equ	4
endif
ifdef mcompact
X		.model  compact
argbase		equ	4
endif
ifdef mmedium
X		.model	medium
argbase		equ	6
endif
ifdef mlarge
X		.model	large
argbase		equ	6
endif
a_swap		equ	<bp+argbase+0>
a_prog		equ	<bp+argbase+2>
a_tail		equ	<bp+argbase+6>
a_env 		equ	<bp+argbase+10>
a_tmp		equ	<bp+argbase+12>
X
a_handle	equ	<bp+argbase>
X
X
; Define all useful equ's
swap_xms	equ	0		; we swapped it out to xms
swap_ems	equ	2		; we swapped it out to ems
swap_file	equ	4		; we swapped it out to a file
seg_no_alloc	equ	0		; this is part of a segment
seg_alloc	equ	1		; this is a full segment header
seg_data	equ	2		; this is data for part of a segment
X
X
; Define any global/external variables that we will be accessing from here.
X	        .data
X		extrn	_errno:word		; Set to dos ret code from exec
X		public  _Interrupted		; Set to 1 if interrupted 0
_Interrupted	dw	0			; otherwise
X
X	        .code
X		assume	cs:@code, ds:@code, ss:@code, es:@code
X
X		even
execstack	dw	64  dup (?)	; put the temporary exec stack right
exec_sp		label	word		; at the start.
X
old_ss		dw	?		; save stack seg across exec
old_sp		dw	?		; save stack ptr across exec
progsize	dw	?		; original size of the program
rootsize	dw	?		; size of base root kept during swap
resend		dw	?		; paragraph where resident code ends
envseg		dw	?		; paragraph of environment segment
psp		dw	?		; our own psp
swap		dw	?		; swapping selection flag
eretcode	dw	?		; return code from exec
interrupted	dw	?		; interrupted flag for exec
arenahead	dw	?		; start of memory block list
alstr		dw	?		; allocation strategy save spot
in_exec		dw	0		; flag, 1 ==> in exec
X
cmdpath		db	65  dup(?)	; file to exec
cmdtail		db	129 dup(?)	; its command tail
fcb		db	37  dup(0)	; dummy fcb
tmpseg		db	7   dup(?)	; block header buffer
X
tmpname		db	65  dup(0)	; name of temporary file resource
X
X		even
tmphandle	dw	?		; handle for temporary file
real_21h	dd	0		; will be DOS's 21h vector if doing -C
X
std_fil_handle	dw	?		; file handle for -C file
std_fil_number	db	?		; system file number for -C file
our_stdout	db	?		; sys file number our stdout handle
X
error_rhdr	db	"exec: Failure reading header block", 0DH, 0AH, '$'
error_rseg	db	"exec: Failure reading segment data", 0DH, 0AH, '$'
error_resize	db	"exec: Failure on resize", 0DH, 0AH, '$'
error_free	db	"exec: Failure to free a block", 0DH, 0AH, '$'
error_string	db	"exec: Program swap failure", 0DH, 0AH, '$'
error_alloc	db	"exec: Memory blocks don't match", 0DH, 0AH, '$'
X
X		even
write_header label word
X   whdr_xms_ptr		dw	word ptr whdr_xms
X   whdr_ems_ptr		dw	word ptr whdr_ems
X   whdr_file_ptr	dw	word ptr whdr_file
X
write_seg label word
X   wseg_xms_ptr		dw	word ptr wseg_xms
X   wseg_ems_ptr		dw	word ptr wseg_ems
X   wseg_file_ptr	dw	word ptr wseg_file
X
read_header label word
X   rhdr_xms_ptr		dw	word ptr rhdr_xms
X   rhdr_ems_ptr		dw	word ptr rhdr_ems
X   rhdr_file_ptr	dw	word ptr rhdr_file
X
read_seg label word
X   rseg_xms_ptr		dw	word ptr rseg_xms
X   rseg_ems_ptr		dw	word ptr rseg_ems
X   rseg_file_ptr	dw	word ptr rseg_file
X
free_resource label word
X   free_xms_ptr		dw	word ptr free_xms_resource
X   free_ems_ptr		dw	word ptr free_ems_resource
X   free_file_ptr	dw	word ptr free_file_resource
X
reset_resource label word
X   reset_xms_ptr	dw	word ptr reset_xms_resource
X   reset_ems_ptr	dw	word ptr reset_ems_resource
X   reset_file_ptr	dw	word ptr reset_file_resource
X
old_ctl_brk label dword
X   old_ctl_brk_off	dw	?
X   old_ctl_brk_seg 	dw	?
X
old_crit_err label dword
X   old_crit_err_off	dw	?
X   old_crit_err_seg 	dw	?
X
exec_block label word
X  ex_envseg	dw	?			; env seg, use parent's if 0
X  ex_cmdtail	dd	?			; command tail for exec
X  ex_fcb1	dd	far ptr fcb		; fcb's aren't used by dmake
X  ex_fcb2	dd	far ptr fcb
X  ex_ss		dw	?			; saved ss for exec
X  ex_sp		dw	?			; saved sp for exec
X  ex_error	dw	0			; error code for dos exec
X
X
; Special 21h (DOS call) handler to tee stdout/stderr writes to the -C file.
; Ignore 21h calls that aren't writes to 1 or 2; i.e., pass them to DOS handler.
; If write call was from this process, it's pretty simple to duplicate it
; to the -C file.  If it's from another process, we try to write to its
; inherited handle.  Worst case is where the handle wasn't inherited: someone
; closed it.  In that instance we have to switch to dmake's PSP to do the
; duplicate write.
X
; Subprocesses do not get their stdout/stderr teed to the -C file if
; their stdout/stderr no longer points to the file/device that dmake's
; stdout points to.  This is tested by looking at the process's job
; file table, which is a table that maps process handles to DOS system file
; table numbers.  (The far pointer to the JFT is at the PSP offset 34h.)
; The JFT is also queried to see if the -C file was inherited.
X
; O_BINARY, O_TEXT problems are ignored here.  These are fudged by the
; C library before it calls DOS; since we're working below that level
; we don't have to worry about it.
X
simulate_21h Macro
X    pushf			;; direct call to DOS
X    call cs:[real_21h]
X    Endm
X
X	assume cs:@code, ds:nothing, es:nothing, ss:nothing
our_21h_handler proc far
X    pushf
X    cmp ah,40h		; is this a write?
X    jne call_dos	; --no
X    cmp bx,1		; write on handle 1 (stdout?)
X    je duplicate_it
X    cmp bx,2		; stderr?
X    je duplicate_it
X
call_dos:
X    popf
X    jmp [real_21h]	; far jump to real handler, which will do the sys call
X    			; and return to the original caller
X
duplicate_it:
X    mpusha
X    push ds
X    push es
X    mov bp,sp
X
X    mov di,std_fil_handle	; handle of the -C file
X
X  If @codesize eq 0
X  	; Small/compact models allow for quick test of us versus subprocess.
X	; False negative (it's us with a different CS) will be picked
X	; up by code just below.  (Might happen due to call from C library.)
X	; False positives would be bad, but can't happen.
X    mov ax,[bp+24]	; caller's CS
X    cmp ax,@code	; same as us?
X    je call_from_dmake
X  Endif
X
X    mov ah,51h		; get PSP ("undocumented version" works in DOS 2.0+)
X    simulate_21h	; PSP segment returned in BX
X    cmp bx,psp		; our PSP?
X    je call_from_dmake	; --yes, no PSP changing needed
X
X    mov es,bx		; set ES to current (caller's) PSP
X    lds bx,es:[34h]	; set DS:BX pointing to caller's job file table
X
X    mov si,[bp+12]	; file handle caller passed in (known to be 1 or 2)
X    mov al,[bx+si]	; system file number corresponding to caller's handle
X    cmp al,our_stdout	; same as our stdout?
X    jne do_real_write	; no--subprocess must have redirected it
X
X    mov al,[bx+di]	; see if caller has dup of -C file still open
X    cmp al,std_fil_number
X    je use_dup		; yes--we can write using caller's PSP
X
X    	; Calling process (or some intermediate process) has closed
X	; the -C descriptor.  We'll use dmake's (our) -C descriptor, but
X	; to do so we'll have to change the PSP.  Disable BREAK handling
X	; so that ^break doesn't kill the wrong process.
X
X    mov ax,3300h	; get BREAK flag
X    simulate_21h
X    mov si,dx		; save BREAK state in SI
X    sub dx,dx		; now turn break flag off
X    mov ax,3301h
X    simulate_21h	; don't want ^Break recoginized while PSP changed
X    mov bx,psp		; set dmake's PSP
X    mov ah,50h
X    simulate_21h
X
X    mov bx,di			; handle of -C file
X    ; CX still has caller's count
X    mov ds,[bp+2]		; restore caller's DS
X    mov dx,[bp+14]		; DS:DX again points to caller's buffer
X    mov ah,40h
X    simulate_21h		; write the copy
X
X    mov bx,es		; caller's PSP
X    mov ah,50h		; set PSP
X    simulate_21h	; restore caller's PSP
X    mov dx,si		; break state before we changed it
X    mov ax,3301h
X    simulate_21h	; restore break state
X
X    jmp short do_real_write
X
use_dup:
X    mov ds,[bp+2]		; restore caller's DS
X    mov dx,[bp+14]		; DS:DX again points to caller's buffer
X
call_from_dmake:
X    mov bx,di			; handle of -C file
X    mov ah,40h			; write
X    ; CX still has caller's count
X    simulate_21h		; write to the file
X
do_real_write:
X    pop es
X    pop ds
X    mpopa
X    popf
X    jmp [real_21h]	; far jump to real handler, which will do the sys call
X    			; and return to the original caller
our_21h_handler endp
X
X	assume	cs:@code, ds:@code, ss:@code, es:@code
X
;-----------------------------------------------------------------------------
; First define the critical-error and control-brk handlers. 
; The critical error handler simply pops the machine state and returns an
; access denied result code.
crit_err_handler proc far
X		add	sp, 6		; ip/cs/flags ...
X		pop	ax
X		pop	bx
X		pop	cx
X		pop	dx
X		pop	si
X		pop	di
X		pop	bp
X		pop	ds
X		pop	es
X		push	bp		; fix up the return flags
X		mov	bp, sp
X		xchg	ax, [bp+6]	; get the flag byte.
X		or	ax, 1		; set the carry bit
X		xchg	ax, [bp+6]	; put it back.
X		pop	bp
X		mov	ax, 5		; access denied
X		iret
crit_err_handler endp
X
X
;-----------------------------------------------------------------------------
; Here we set the interrupted flag, and terminate the currently running
; process.
ctl_brk_handler proc far
X		clc				; make sure carry is clear
X		inc	cs:interrupted		; set the flag
X
; Make certain it isn't us that is going to get terminated.
; There is a small window where the in_exec flag is set but the child is
; not running yet, I assume that DOS doesn't test for ctl_brk at that time
; as it is bussily creating a new process.
X		cmp	cs:in_exec,0
X		je	just_return		; note this implies CF == 0
X		stc				; set CF to abort child
just_return:	iret
ctl_brk_handler endp
X
X
;-----------------------------------------------------------------------------
; Something really nasty happened, so abort the exec call and exit.
; This kills the calling process altogether, and is a very nasty way of
; termination since files may still be open etc.
abort_exec_rhdr label near
X		mov	dx, offset error_rhdr
X		jmp	print_it
abort_exec_rseg label near
X		mov	dx, offset error_rseg
X		jmp	print_it
abort_exec_resize label near
X		mov	dx, offset error_resize
X		jmp	print_it
abort_exec_free label near
X		mov	dx, offset error_free
X		jmp	print_it
abort_exec_alloc label near
X		mov	dx, offset error_alloc
X		jmp	print_it
abort_exec proc near
X		mov	dx, offset error_string
print_it:	push	dx
X		mov	bx, [swap]
X		call	[free_resource+bx]
X		mov	ax, cs
X		mov	ds, ax
X		pop	dx
X		mov	ah, 9
X		int	21H
kill_program:	mov	ax, 04cffH			; nuke it!
X		int	21H
abort_exec endp
X
X
;-----------------------------------------------------------------------------
; lodsw/stosw loop to copy data.  Called only for word copy operations.
; 	ds:si  - point at source
;	es:di  - point at destination
;	cx     - count of bytes to copy.
copy_data proc near
X		shr	cx, 1		; convert to word count
X		jnc	copy_words
X		movsb
copy_words:	rep	movsw		; copy the words.
X		ret
copy_data endp
X
X
X
;=============================================================================
; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO READ XMS RECORDS.
;=============================================================================
rhdr_xms proc near
X		ret
rhdr_xms endp
X
rseg_xms proc near
X		ret
rseg_xms endp
X
reset_xms_resource proc near
X		ret
reset_xms_resource endp
X
free_xms_resource proc near
X		ret
free_xms_resource endp
;=============================================================================
X
X
X
;=============================================================================
; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO READ EMS RECORDS.
;=============================================================================
rhdr_ems proc near
X		ret
rhdr_ems endp
X
rseg_ems proc near
X		ret
rseg_ems endp
X
reset_ems_resource proc near
X		ret
reset_ems_resource endp
X
free_ems_resource proc near
X		ret
free_ems_resource endp
;=============================================================================
X
X
X
;=============================================================================
; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO READ FILE RECORDS.
;=============================================================================
; This routine reads a segment header from a file.
; The header is a seven byte record formatted as follows:
;	segment address		- of data
;	offset address		- of data
; 	length in paragraphs	- of data
;	mode			- 1 => segment header (allocate seg on read)
;				  0 => subsegment, don't allocate on read.
; The information is placed into the tmpseg data area in the code segment.
; The routine aborts if an error is detected.
rhdr_file proc near
X		mov	dx, offset tmpseg	; read the header record out
X		mov	cx, 7
X		mov	bx, [tmphandle]
X		mov	ah, 03fH
X		int	21H
X		jnc	rhdr_done		; make sure it worked
X		jmp	abort_exec_rhdr
X
rhdr_done:	cmp	ax, 7
X		je	exit_rhdr_file
X		or	ax, ax
X		je	signal_eof
X		jmp	abort_exec_rhdr
X
signal_eof:	stc
exit_rhdr_file:	ret
rhdr_file endp
X
X
;-----------------------------------------------------------------------------
; Read a segment from the temporary file whose handle is in cs:tmphandle.
; The routine aborts if an error is detected.
rseg_file proc near
X		push	ds
X		mov	ds, word ptr cs:tmpseg; Now read the whole segment
X		mov	dx, word ptr cs:tmpseg+2
X		mov	cx, word ptr cs:tmpseg+4
X		mov	bx, cs:tmphandle
X		mov	ah, 03fH
X		int	21H
X		pop	ds
X		jnc	rseg_done
X		jmp	abort_exec_rseg
X
rseg_done:	cmp	ax, [word ptr tmpseg+4]
X		je	exit_rseg_file
X		jmp	abort_exec_rseg		; If we didn't get read full
exit_rseg_file:	ret				; segment then abort
rseg_file endp
X
X
;-----------------------------------------------------------------------------
; Seek to the beginning of the file.
reset_file_resource proc near
X		mov	bx, [tmphandle]
X		xor	cx, cx
X		mov	dx, cx
X		mov	ax, 04200H		; seek to begining of file
X		int	21H
X		ret
reset_file_resource endp
X
X
;-----------------------------------------------------------------------------
; unlink the temporary file allocated for swapping.
; We close the file first, and then delete it.   We ignore errors here since
; we can't do anything about them anyway.
free_file_resource proc near
X		mov	bx, [tmphandle]		; get the file handle
X		mov	ah, 03eH		; close the file
X		int	21H
X		mov	dx, offset tmpname	; Now delete the temp file
X		mov	ah, 041H
X		int	21H
X		ret
free_file_resource endp
;=============================================================================
X
X
X
;=============================================================================
; CODE TO SWAP THE IMAGE IN FROM SECONDARY STORAGE
;=============================================================================
swap_in proc near
X		mov	bx, [alstr]		; get previous alloc strategy
X		mov	ax, 5801H		; and set it back
X		int	21H
X		mov	bx, [swap]		; get type of resource
X		call	[reset_resource+bx]	; reset the resource
X		mov	es, [psp]		; resize the program back
X		mov	bx, [progsize]		; to original size
X		mov	ah, 04AH
X		int	21H
X		jnc	read_seg_loop
X		jmp	abort_exec
X
read_seg_loop:	mov	bx, [swap]		; get type of resource
X		call	[read_header+bx]	; get seg header
X		jc	exit_swap_in		; all done
X		mov	al, [tmpseg+6]
X		cmp	al, seg_no_alloc	; see if dummy segment header
X		je	read_seg_loop
X		cmp	al, seg_alloc		; do we need to do an alloc?
X		jne	read_data		; nope
X
; Allocate back the memory for a segment that is not the [psp], note that this
; must come back to the same segment we had previously since other segments
; may have pointers stored in their variables that point to this segment using
; segment:offset long pointers.
X		mov	bx, [word ptr tmpseg+4]	; get count of paragraphs
X		mov	ah, 048H		; dos_alloc
X		int	21H
X		jc	alloc_error		; oops!
X		cmp	ax, [word ptr tmpseg]	; did we get the same segment?
X		je	read_seg_loop		; yup!
alloc_error:	jmp	abort_exec_alloc
X
read_data:	mov	bx, [swap]
X		call	[read_seg+bx]		; this must succeed, if fail
X		jmp	read_seg_loop		; we never come back here
X
exit_swap_in:	mov	bx, [swap]		; all done, so free resource
X		call	[free_resource+bx]
X		ret
swap_in endp
X
X
;=============================================================================
; CODE TO SWAP THE IMAGE OUT TO SECONDARY STORAGE
;=============================================================================
; This routine is called to swap the non-resident portion of the program
; out to the resource specified by the value of [cs:swap].  If the swap out
; fails, then appropriate routines are called to free the resources allocated
; up to that point.
;
; The steps used to swap the program out are as follows:
;	- calculate new size of program to remain resident and size to swap
;	  out.
;	- write out non-resident portion of current segment
;	- walk DOS allocation chain and write out all other segments owned by
;	  the current program that are contiguous with the _psp segment
;	- copy the environment down to low memory
;	- resize the current _psp segment to savesize
;	- free all segments belonging to program except current _psp segment
swap_out proc near
X		mov	ax, 05800H	; get memory alocation strategy
X		int	021H
X		mov	[alstr], ax	; and save it for future restoration.
X		mov	di, [psp]	; compute length of program to current
X		mov	bx, cs		; value of cs, and find program size
X		sub	bx, di		; by looking at length stored in
X		mov	ax, di		; arena header found in front of psp
X		dec	ax
X		mov	es, ax
X		mov	si, es:3	; si is size of program in paragraphs
X		mov	[progsize], si	; progsize now contains the size.
X
; Now compute length of program segment to save.
; Length is:   cs - psp + (offset overlay_code_here+15 >> 4)
X		mov	ax, offset overlay_code_here+15
X		shr	ax, 1
X		shr	ax, 1
X		shr	ax, 1
X		shr	ax, 1
X		add	bx, ax			; bx is size of program to keep
X		sub	si, bx			; si is # of paragraphs to save.
X		add	di, bx			; di is paragraph to start at
X		mov	rootsize, bx
X		mov	resend, di		; cs:resend is saved start para
X		mov	al, seg_no_alloc	; set no allocation for segment
X		call	write_segment
X		jc	abort_swap_out
X
; We have now saved the portion of the program segment that will not remain
; resident during the exec.  We should now walk the DOS allocation chain and
; write out all other segments owned by the current process.
save_segments:	mov	ax, [psp]
X		dec	ax
X		mov	es, ax
X		mov	bx, offset write_segment_data
X		call	walk_arena_chain
X		jc	abort_swap_out
X
; Now we must walk the chain of allocated memory blocks again and free
; all those that are owned by the current process, except the one that is
; the current process' psp.
free_segments:	mov	ax, [psp]
X		dec	ax
X		mov	es,ax
X		mov	bx, offset free_dos_segment
X		call	walk_arena_chain
X		jnc	resize_program
X		jmp	abort_exec_free		; can't fix it up now.
X
; We now resize the program to the size specified by cs:rootsize.  This will
; free most of the memory taken up by the current program segment.
resize_program: mov	es, [psp]		; es is segment to resize.
X		mov	bx, [rootsize]		; bx is size of segment.
X		mov	ah, 04aH		; resize memory block
X		int	21H
X		jnc	swap_out_ok
X		jmp	abort_exec_resize	; disaster
swap_out_ok:	ret
X
; The swap out failed for some reason, so free any allocated resources
; and set the carry bit.
abort_swap_out:	mov	bx, [swap]
X		call	[free_resource+bx]
X		xor	ax, ax
X		mov	[swap], ax		; clear the swap flag
X		stc
X		ret
swap_out endp
X
X
;=============================================================================
; CODE TO SET-UP FOR AND EXEC THE CHILD PROCESS
;=============================================================================
; Actually execute the program.  If cs:swap is set, this code will invoke the
; swap-out/swap-in code as required.
do_exec proc near
X		cmp	[swap], 0		; does the user want to swap?
X		je	no_swap_out		; nope
X		call	init_swap		; figger out where to swap to
X		jc	no_swap_out		; if carry set then don't swap
X		call	swap_out
X
no_swap_out:	cmp	[interrupted], 0	; were we interrupted?
X		jne	leave_exec		; yep, so clean up, don't exec
X
; free passed in environment block if it is non zero.
; This way the parent program does not need to free it.
X		mov	ax, [envseg]
X		or	ax, ax
X		je	setup_block
X		push	ax
X		mov	es, ax
X		mov	ah, 49H
X		int	21H
X		pop	ax
X
; set up the parameter block for the DOS exec call.
;    offset  contents
;        00  segment address of environment to be passed,
; 	     0 => use parents env.
;        02  pointer to command tail for new process.
;        06  pointer to fcb1
;        0a  pointer to fcb2
setup_block:	mov	ax, [envseg]
X		mov	[ex_envseg], ax
X		mov	cx, cs
X		mov	[word ptr ex_cmdtail], offset cmdtail
X		mov	[word ptr ex_cmdtail+2], cx
X
; set up registers for exec call
;	ds:dx	- pointer to pathname of program to execute
;	es:bx	- pointer to above parameter block
X		mov	dx, offset cmdpath
X		mov	es, cx
X		mov	bx, offset exec_block
X
; Under DOS 2.x exec is notorious for clobbering registers and guarantees
; to preserve only cs:ip.
X		push	ds
X		mov	[ex_sp], sp
X		mov	[ex_ss], ss
X		mov	[ex_error], 0		; clear exec error code
X		inc	[in_exec]		; set internal flag
X		mov	ax, 04b00H
X		int	21H
X
; returned from exec, so restore possibly clobbered registers.
X		mov	ss, cs:ex_ss
X		mov	sp, cs:ex_sp
X		pop	ds
X
; check to make certain the exec call worked.
X		jnc	it_worked
X
; exec call failed.  Save return code from msdos.
X		mov	[ex_error], ax
X		jmp	leave_exec
X
it_worked:	mov	ah, 04dH	; get the return code
X		int	21H
X		cbw
X		mov	[eretcode], ax
X
leave_exec:	cmp	[swap], 0	; check swap, if non-zero swap back in
X		je	no_swap_in
X		call	swap_in
X
; Clear the in_exec after the swap back in.  This way we are guaranteed to
; get parent in and the resources freed should a ^C be hit when we are reading
; the image in.
no_swap_in:	mov	[in_exec], 0
X		ret
do_exec endp				
X
X
X
;==============================================================================
; Everything past this point is overwriten with the environment and new
; program after the currently executing program is swapped out.
;==============================================================================
overlay_code_here label word
X
;-----------------------------------------------------------------------------
; Figure out where we can swap to and initialize the resource we are going to
; use.  We try XMS, EMS, and a tempfile (if specified), in that order.  We set
; [cs:swap] to the correct value based on which of the resources exists.
; If none can be used, then [cs:swap] is set to 0, and no swap takes place.
; The exec code will still attempt to execute the child in this instance, but
; may fail due to lack of resources.   Each swap_out_* routine must provide
; its own clean-up handler should it not be able to write all program
; segments to the swap resource.
init_swap proc near
X		mov	[swap], 0
;call	init_xms
;jnc	init_done
;call	init_ems
;jnc	init_done
X		call	init_file
init_done:	ret
init_swap endp
X
X
;-----------------------------------------------------------------------------
; This routine is used to walk the DOS allocated memory block chain
; starting at address supplied in the es register.  For each block it
; calls the routine specified by the bx register with the segment length
; in si, and its address in di.  It does not apply the routine to the
; segment if the segment is the same as the current program's [cs:psp] value.
memheader struc
X   magic	db	?	; either 'Z' for end or 'M' for allocated
X   owner	dw	?	; psp of owner block
X   len		dw	?	; length in paragraphs of segment
memheader ends
X
walk_arena_chain proc near
X		mov	si, word ptr es:3		; get length
X		mov	di, es
X		inc	di
X		mov	ax, word ptr es:1
X
; Stop the search if the block is NOT owned by us.  Ignore our own psp block
; and our environment segment block.
X		cmp	ax, cs:psp			; is it owned by us?
X		jne	walk_done			; NOPE!  -- all done
X		cmp	di, cs:envseg			; skip our environment
X		je	next_block
X		cmp	di, cs:psp			; skip our psp
X		je	next_block
X
; Now save state and call the routine pointed at by [bx].
X		push	di
X		push	si
X		push	bx
X		call	bx
X		pop	bx
X		pop	si
X		pop	di
X		jc	exit_walk			; if error then stop
X		mov	al, byte ptr es:0		; check if at end 
X		cmp	al, 'Z'
X		je	walk_done
X
next_block:	add	di, si				; go on to next segment
X		mov	es, di
X		jmp	walk_arena_chain
walk_done:	clc
exit_walk:	ret
walk_arena_chain endp
X
X
;-----------------------------------------------------------------------------
; This routine takes a dos segment found in the di register and free's it.
free_dos_segment proc near
X		mov	es, di		; free dos memory block
X		mov	ah, 49H
X		int	21H
X		ret
free_dos_segment endp
X
X
;-----------------------------------------------------------------------------
; Called to invoke write_segment with proper values in the al register.  Only
; ever called from walk_arena_chain, and so al should be set to seg_alloc.
write_segment_data label near
X		mov	al, seg_alloc	; and fall through into write_segment
;-----------------------------------------------------------------------------
; This routine writes a segment as a block of data segments if the number of
; paragraphs to write exceeds 0x0fff (rarely the case).
; It stuffs the info into tmpseg, and then calls wheader and wseg to get the
; data out.
;
;	di:dx	segment:offset of segment;  offset is ALWAYS zero.
;	si	number of paragraphs to write.
;	al	mode of header to write
write_segment proc near
X		push	di
X		push	si
X		xor	dx,dx
X		mov	bx, [swap]
X		call	[write_header+bx]
X		pop	si
X		pop	di
X		jc	exit_wseg
X
do_io_loop:	cmp	si, 0		; are we done yet?
X		je	exit_wseg	; yup so leave.
X		mov	cx, si		; # of paragraphs to move
X		cmp	cx, 0fffH	; see if we have lots to move?
X		jle	do_io
X		mov	cx, 0fffH	; reset to max I/O size
X
do_io:		push	cx		; save # of paragraphs we are writing
X		shl	cx, 1		; shift cx by four to the left
X		shl	cx, 1
X		shl	cx, 1
X		shl	cx, 1
X		push    di		; save the start, and count left
X		push    si
X		mov	si, cx
X		xor	dx,dx
X		mov	al, seg_data
X		mov	bx, [swap]
X		push	bx
X		call	[write_header+bx]
X		pop	bx
X		call	[write_seg+bx]
X		pop	si
X		pop	di
X		pop	dx		; original paragraph count in dx
X		jc	exit_wseg	; it failed so exit.
X		add	di, dx		; adjust the pointers, and continue.
X		sub	si, dx
X		jmp     do_io_loop
exit_wseg:	ret
write_segment endp
X
X
;=============================================================================
; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO WRITE XMS RECORDS.
;=============================================================================
init_xms proc near
X		ret
init_xms endp
X
whdr_xms proc near
X		ret
whdr_xms endp
X
wseg_xms proc near
X		ret
wseg_xms endp
;=============================================================================
X
X
;=============================================================================
; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO WRITE EMS RECORDS.
;=============================================================================
init_ems proc near
X		ret
init_ems endp
X
whdr_ems proc near
X		ret
whdr_ems endp
X
wseg_ems proc near
X		ret
wseg_ems endp
;=============================================================================
X
X
;=============================================================================
; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO WRITE FILES.
;=============================================================================
;-----------------------------------------------------------------------------
; Attempt to create a temporary file.  If the tempfile name is NIL then return
; with the cary flag set.
init_file proc near
X		mov	al, [tmpname]
X		or	al, al
X		je	err_init_file
X		mov	dx, offset tmpname
X		xor 	cx, cx
X		mov	ah, 03cH
X		int	21H
X		jc	err_init_file		; if carry set then failure
X		mov	[tmphandle], ax		; init swapping
X		mov	[swap], swap_file
X		jmp	exit_init_file
err_init_file:	stc
exit_init_file: ret
init_file endp
X
X
;-----------------------------------------------------------------------------
; This routine writes a segment header to a file.
; The header is a seven byte record formatted as follows:
;	segment address		- of data
;	offset address		- of data
; 	length in paragraphs	- of data
;	mode			- 1 => segment header (allocate seg on read)
;				  0 => subsegment, don't allocate on read.
; Routine takes three arguments:
;	di:dx	segment:offset of segment
;	si	number of paragraphs to write.
;	al	mode of header to write
whdr_file proc near
X		mov	[word ptr tmpseg], di	; save the segment/offset
X		mov	[word ptr tmpseg+2], dx
X		mov	[word ptr tmpseg+4], si	; save the segment length
X		mov	[tmpseg+6], al
X		mov	dx, offset tmpseg	; write the header record out
X		mov	cx, 7
X		mov	bx, [tmphandle]
X		mov	ah, 040H
X		int	21H
X		jc	exit_whdr_file		; make sure it worked
X		cmp	ax, 7
X		je	exit_whdr_file		; oh oh, disk is full!
err_whdr_file:	stc
exit_whdr_file:	ret
whdr_file endp
X
X
;-----------------------------------------------------------------------------
; Write a segment to the temporary file whose handle is in cs:tmphandle
; Parameters for the write are assumed to be stored in the tmpseg data area.
; function returns carry set if failed, carry clear otherwise.
wseg_file proc near
X		push	ds
X		mov	ds, word ptr cs:tmpseg ; Now write the whole segment
X		mov	dx, word ptr cs:tmpseg+2
X		mov	cx, word ptr cs:tmpseg+4
X		mov	bx, cs:tmphandle
X		mov	ah, 040H
X		int	21H
X		pop	ds
X		jc	exit_wseg_file		; make sure it worked
X		cmp	ax, [word ptr tmpseg+4]
X		je	exit_wseg_file
err_wseg_file:	stc				; it failed (usually disk full)
exit_wseg_file:	ret
wseg_file endp
;=============================================================================
X
X
;=============================================================================
; _exec: THIS IS THE MAIN ENTRY ROUTINE TO THIS MODULE
;=============================================================================
; This is the main entry routine into the swap code and corresponds to the
; following C function call:
;
; exec( int swap, char far *program, char far *cmdtail, int environment_seg,
;	char far *tmpfilename );
;
; Exec performs the following:
;	1. set up the local code segment copies of arguments to the exec call.
;	2. switch to a local stack frame so that we don't clobber the user
;	   stack.
;	3. save old interrupt vectors for ctrl-brk.
;	4. install our own handler for the ctrl-brk interrupt, our handler
;	   terminates the current running process, and returns with non-zero
;	   status code.
;	5. get our psp
;	6. setup arguments for exec call
;	7. exec the program, save result code on return.
;       8. restore previous ctrl-brk and crit-error handler.
;       9. restore previous process stack, and segment registers.
;      10. return from exec with child result code in AX
;	   and global _Interrupted flag set to true if child execution was
;	   interrupted.
X
; NOTE:  When first called the segments here assume the standard segment
;        settings.
X		assume cs:@code, ds:DGROUP,es:DGROUP,ss:DGROUP
X
X		public	_exec
_exec proc
X	    	push	bp		; set up the stack frame
X		mov	bp, sp
X		push	si		; save registers we shouldn't step on.
X		push	di
X		push	ds
X
; set up for copying of parameters passed in with long pointers.
X		push	cs		; going to use lodsb/stosb, set up es
X		pop	es		; as destination.
X		assume  es:@code	; let the assembler know :-)
X		cld			; make sure direction is right
X
; Copy all parameters into the bottom of the code segment.  After doing so we
; will immediately switch stacks, so that the user stack is preserved intact.
X		mov	ax, ss:[a_swap]		; save swap
X		mov	es:swap, ax
X		mov	ax, ss:[a_env]		; save env seg to use
X		mov	es:envseg, ax
X
X		mov 	di, offset cs:cmdpath	; copy the command
X		lds 	si, ss:[a_prog]		; 65 bytes worth
X		mov	cx, 65
X		call	copy_data
X
X		mov	di, offset cs:cmdtail	; copy the command tail
X		lds	si, ss:[a_tail]		; 129 bytes worth
X		mov	cx, 129
X		call	copy_data
X
X		mov	di, offset cs:tmpname	; copy the temp file name
X		lds	si, ss:[a_tmp]		; 65 bytes worth.
X		mov	cx, 65
X		call	copy_data
X
; Now we save the current ss:sp stack pointer and swap stack to our temporary
; stack located in the current code segment.  At the same time we reset the
; segment pointers to point into the code segment only.
swap_stacks:	mov	ax, ss
X		mov	es:old_ss, ax
X		mov	es:old_sp, sp
X		mov	ax, cs
X		mov	ds, ax
X		mov	ss, ax			; set ss first, ints are then
X		mov	sp, offset cs:exec_sp	; disabled for this instr too
X		assume  ds:@code, ss:@code	; let the assembler know :-)
X
; Now we save the old control break and critical error handler addresses.
; We replace them by our own routines found in the resident portion of the
; swapping exec code.
set_handlers:	mov	[interrupted], 0	; clear interrupted flag
X		mov	[eretcode], 0		; clear the return code
X		mov	ax, 03523H		; get int 23 handler address
X		int	21H
X		mov	cs:old_ctl_brk_off, bx
X		mov	cs:old_ctl_brk_seg, es
X		mov	dx, offset ctl_brk_handler
X		mov	ax, 02523H		; set int 23 handler address
X		int	21H
X
X		mov	ax, 03524H		; get int 24 handler address
X		int	21H
X		mov	cs:old_crit_err_off, bx
X		mov	cs:old_crit_err_seg, es
SHAR_EOF
true || echo 'restore of dmake/msdos/exec.asm failed'
fi
echo 'End of part 22, continue with part 23'
echo 23 > _shar_seq_.tmp
exit 0

exit 0 # Just in case...
-- 
Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
Sterling Software, IMD           UUCP:     uunet!sparky!kent
Phone:    (402) 291-8300         FAX:      (402) 291-4362
Please send comp.sources.misc-related mail to kent@uunet.uu.net.