;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Module to create and call batch file '$$$ap.bat'. ; Written for Inprise Corporation, Educational Sales, AP Library install. ; ; George Cross, Borland C++ Developer Support, June 1998 (birthday month !) ; ; Build with: ; ; tasm32 /mx /la /m /dMEMMODEL=xxxx /dMEMMODIFIER=xxxx /dSTACKMODIFIER=xxxx ; ; One public function: ; ; addToRtl ; - C calling convention ; - C naming convention ; - returns nothing ; - arguments (on stack): ; 1. 4-byte address of null-terminated character string ; which denotes the Borland/Turbo C++ directory ; eg."d:\bc45\" ; Note the trailing backslash ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .386p % .model MEMMODIFIER MEMMODEL,nolanguage,STACKMODIFIER nosmart jumps BUFFERSIZE = 512 BATCHFILE EQU '$$$ap.bat' PROGRAMNAME EQU 'APINST.EXE' extrn MESSAGEBOX:far extrn WINEXEC:far extrn GETDOSENVIRONMENT:far extrn __argv:far .data libBuffer db BUFFERSIZE dup(?) p_libBuffer dd libBuffer binBuffer db BUFFERSIZE dup(?) p_binBuffer dd binBuffer incBuffer db BUFFERSIZE dup(?) p_incBuffer dd incBuffer tlibCommand db BUFFERSIZE dup(?) p_tlibCommand dd tlibCommand fileName db BATCHFILE,0 fileNameLength dw $ - fileName p_fileName dd fileName eMsgCreatingFile db 'Can''t create temporary batch file ' db BATCHFILE,0dh,0ah db 'Please enable directory privileges ' db 'and rerun ',PROGRAMNAME,0 eMsgCFLength dw $ - eMsgCreatingFile p_eMsgCF dd eMsgCreatingFile eMsgComSpec db 'Value for %COMSPEC% not found.',0dh,0ah db 'Please add it to the environment ' db 'and rerun ',PROGRAMNAME,0 p_eMsgComSpec dd eMsgComSpec eMsgCC db 'No command-line compiler found.',0dh,0ah db 'You will need to add apstring.cpp to the RTL ' db 'using the IDE.',0 p_eMsgCC dd eMsgCC eMsgTLib db 'TLib.exe not found in \bin directory.' db 0dh,0ah db 'Please ensure it is copied there and re-run ' db PROGRAMNAME,0 p_eMsgTLib dd eMsgTLib eMsgWritingFile db 'Error writing to batch file ',BATCHFILE,0 p_eMsgWF dd eMsgWritingFile eMsgCurrentDir db 'The directory to this executable ',PROGRAMNAME db ' is too long. Please run it from a shorter ' db 'directory.',0 p_eMsgCurrentDir dd eMsgCurrentDir eMsgSettingDir db 'There was an error setting the current' db ' Please run ',BATCHFILE,'from the command ' db ' prompt.',0 p_eMsgSettingDir dd eMsgSettingDir eMsgProcessingLibs db 'Fatal error ProcessingLibs. BUFFERSIZE too ' db 'small. Increase it and re-assemble.',0 p_eMsgProcessingL dd eMsgProcessingLibs comspec db 'COMSPEC=',0 p_comspec dd comspec comspecSwitches db ' /K ',0 p_comspecSwitches dd comspecSwitches ; ; Below are some directory and filenames we use ; Please be careful about changing these as the lengths are hard ; coded any place they are used. Sorry, I know, bad style. ; bindir db 'bin\' libdir db 'lib\' lib db '.lib' exe db '.exe' tlib db 'tlib' apstringcpp db 'include\apstring.cpp',0 len_apstringcpp = $ - apstringcpp apstringobj db ' -+apstring.obj',0 len_apstringobj = $ - apstringobj str_call db 0dh,0ah,'call ' len_call = $ - str_call ; ;The following are lists of libs for the particular compiler platforms: ;16 or 32-bit. At the front of each list is a byte which will be ;1 or 0, indicating whether or not this list has yet been traversed. ;Following that is a byte with a number indicating the number of ;entries in this list. ; ;Each subsequent entry in the list is comprised of, ; ; the root filename of the RTL lib for a particular memory model, ; a terminating 0, ; a string of switches for the compiler for that memory model, ; a terminating 0, ; libs16 db 0,0bh db 'ct',0,' -c -mt ',0 db 'cs',0,' -c -ms ',0 db 'cc',0,' -c -mc ',0 db 'cm',0,' -c -mm ',0 db 'cl',0,' -c -ml ',0 db 'ch',0,' -c -mh ',0 db 'cws',0,' -c -ms ',0 db 'cwc',0,' -c -mc ',0 db 'cwm',0,' -c -mm ',0 db 'cwl',0,' -c -ml ',0 db 'crtldll',0,' -c -ml ',0 libs32 db 0,6 db 'cw32',0,' -c ',0 db 'cw32i',0,' -c ',0 db 'cw32mt',0,' -c ',0 db 'cw32mti',0,' -c ',0 db 'cp32mt',0,' -c ',0 db 'cp32mti',0,' -c ',0 ; ; The following is a list of compiler executable names - ; bcc.exe, tcc.exe, bcc32.exe, etc. The first byte indicates the ; number of entries in the list. ; ; Each entry is comprised of: ; the compiler file name ; the offset of the list of libs it compiles for. ; compilers db 3 db 'bcc',0 dw libs16 db 'tcc',0 dw libs16 db 'bcc32',0 dw libs32 .code public _addToRtl _addToRtl label codeptr push bp mov bp,sp push ds ;save regs push di push si ; ; Local variables: ; ; Location Size Description ; -------- ---- ----------- ; bp-8 word length of string argument passed to _addToRtl ; bp-0ah word current position in compilers table ; bp-0ch word file handle to batch file ; bp-0eh word address of last byte in bin dir + 1 ; bp-10h word address of last byte in lib dir + 1 ; bp-12h word address of last byte in tlib command + 1 ; LOCALDATASIZE equ 0ch sub sp,LOCALDATASIZE ; ; Here we will copy the path passed on the stack, to 4 buffers: ; one buffer to assemble the lib\c*.lib filenames, and ; one buffer to assemble the bin\bcc.exe (tcc,bcc32 etc.) commands ; one buffer to assemble the include\apstring.cpp filename ; one buffer to assemble the bin\tlib.exe command ; lds si,[bp+4+(@CodeSize*2)] ;get seg:offset of path to BC++ mov ax,@data mov es,ax mov di,offset libBuffer cld call CopyString ;copy path mov ax,di ;store the length of the string sub ax,offset libBuffer ; in a stack variable. mov [bp-8],ax mov ax,@data ;append 'lib\' to it mov ds,ax mov si,offset libdir mov cx,4 rep movsb mov [bp-10h],di ;save address of last byte + 1 lds si,[bp+4+(@CodeSize*2)] ;do same for second buffer mov di,offset binBuffer call CopyString mov ax,@data ;append 'bin\' to it mov ds,ax mov si,offset bindir mov cx,4 rep movsb mov [bp-0eh],di ;save address of last byte + 1 lds si,[bp+4+(@CodeSize*2)] ;do same for third buffer mov di,offset incBuffer call CopyString mov ax,@data ;append 'include\apstring.cpp' mov ds,ax mov si,offset apstringcpp mov cx,len_apstringcpp rep movsb lds si,[bp+4+(@CodeSize*2)] ;do same for fourth buffer mov di,offset tlibCommand call CopyString mov ax,@data ;append 'bin\tlib.exe' mov ds,ax mov si,offset bindir mov cx,4 rep movsb mov si,offset tlib mov cx,4 rep movsb mov si, offset exe mov cx,4 rep movsb mov [bp-12h],di ;save address of last byte + 1 mov dx,offset tlibCommand ;check for presence mov byte ptr [di],0 ;null terminate mov cx,0 mov ax,4e00h int 21h jc ErrorNoTLib ; ; Set the current directory to the one for this executable ; ;call SetDefaultDirToCurrentDir ; ; Here we create and open the batch file. ; mov dx,offset fileName ;create the batch file mov cx,20h ; handle will be returned in AX mov ah,3ch int 21h jc ErrorFile mov [bp-0ch],ax ;save file handle mov ax,3d21h ;open file, full access r/w int 21h ;ds:dx points to filename ; ; Here we will iterate through the compiler executables, ; tcc.exe, bcc.exe, etc. We will check if each compiler exists, ; and if so, proceed to traverse the associated libs table. ; mov di,[bp-0eh] ;set di to last byte in bin dir + 1 mov si,offset compilers ;set ds:si to compilers table xor cx,cx mov cl,[si] ;get number of entries in list inc si ;set si to first entry cld ;copy strings going forward IterateCompilers: call CopyString inc si ;save address of corresponding mov [bp-0ah],si ; libs table push cx ;save loop counter mov si,offset exe ;add '.exe'to compiler name mov cx,4 rep movsb mov dx,offset binBuffer ;check to see compiler exists mov byte ptr [di],0 ; null terminate filename mov cx,0 ; set attributes mov ax,4e00h ; AH=4eh is findfirst function int 21h pop cx jc ProcessNextCompiler ; ; At this point we know the compiler for this particular platform ; exists, so we will proceed to traverse the table of RTL libs, ; check which ones exist and generate batch file commands to update ; those that do exist. ; ; es,ds are set to @data ; di points to the last byte in the compiler command ; push cx ;save our loop counter call ProcessLibs test ax,ax jnz Finished pop cx ;restore our loop counter ProcessNextCompiler: mov di,[bp-0eh] ;set di to last byte in bin dir + 1 mov si,[bp-0ah] ;jump to next record & continue add si,2 loop IterateCompilers ; ; Close the batch file ; mov bx,[bp-0ch] mov ah,3eh int 21h ; ; Here we get the comspec environment variable (eg. \command.com) ; and prepare the command string to launch the batch file. The command ; string will be something like, ; c:\windows\command.com /K $$$ap.bat ; lds si,[p_comspec] call SearchEnvironment ;get COMSPEC env variable test al,al ; returned in es:di jnz ErrorNoComSpec mov si,di ;copy to buffer and assemble mov ax,es ; command to execute les di,[p_binBuffer] mov ds,ax cld call CopyString mov ax,@data ;append compspec switches mov ds,ax mov si,offset comspecSwitches call CopyString mov si,offset fileName ;append filename call CopyString movsb ;null terminate push [p_binBuffer] push 1 ;execute batch file call WINEXEC ; (cheaper that int 21,ah=4b) jmp Finished ErrorFile: push 0 push [p_eMsgCF] push LARGE 0 push 0 call MESSAGEBOX ;handle error jmp Finished ErrorNoComSpec: push 0 push [p_eMsgComSpec] push LARGE 0 push 0 call MESSAGEBOX ;handle error jmp Finished ErrorNoTLib: push 0 push [p_eMsgTLib] push LARGE 0 push 0 call MESSAGEBOX ;handle error jmp Finished ErrorNoCC: push 0 push [p_eMsgCC] push LARGE 0 push 0 call MESSAGEBOX ;handle error jmp Finished Finished: add sp,LOCALDATASIZE pop si pop di pop ds ;restore regs & return pop bp retcode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; CopyString / CopyBoundedString ; ; Copies the null-terminated string in ds:si to es:di destination. ; ; Input ; ds:si - beginning address of source string ; es:di - beginning of destination address ; cx - maximum bytes to copy. (CopyBoundedString only) ; cld - must be 0 ; Output ; es:di - points to one byte past last character in destination. ; This byte will be zero. ; Truncates the string to CX bytes. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; CopyString label near movsb cmp byte ptr [si],0 jnz CopyString jmp finishedCopyString CopyStringBounded label near movsb cmp byte ptr [si],0 loopnz CopyStringBounded finishedCopyString: mov es:byte ptr [di],0 ;copy zero but don't increment ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; SetDefaultDirToCurrentDir (SDDTCD) ; ; Sets the default directory to the one where this executable is located. ; ; Input ; ; Output ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SetDefaultDirToCurrentDir label near SDDTCD_LOCALDATASIZE equ BUFFERSIZE ;to hold the dir name CURRENTDIRNAME equ bp-BUFFERSIZE push bp ;set up stack frame mov bp,sp sub sp,SDDTCD_LOCALDATASIZE lds si,dword ptr [__argv] ;get this executable name lds si,dword ptr [si] mov ax,ss ;copy it to local buffer mov es,ax lea di,[CURRENTDIRNAME] mov cx,BUFFERSIZE cld call CopyStringBounded jcxz errorCurrentDirectory std mov al,'\' mov cx,BUFFERSIZE repnz scasb add di,2 mov es:byte ptr [di],0 mov ax,es mov ds,ax lea dx,[CURRENTDIRNAME] mov ah,3bh int 21h jc errorSettingDirectory jmp finishedSDDTCD errorSettingDirectory: push 0 push [p_eMsgSettingDir] push LARGE 0 push 0 call MESSAGEBOX ;handle error jmp finishedSDDTCD errorCurrentDirectory: push 0 push [p_eMsgCurrentDir] push LARGE 0 push 0 call MESSAGEBOX ;handle error jmp finishedSDDTCD finishedSDDTCD: add sp,SDDTCD_LOCALDATASIZE pop bp ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ProcessLibs ; ; Iterates through the list of RTL lib names and writes appropriate ; compile and tlib commands to the batch file. ; ; Sorry, I didn't set up a stack frame for this one. I know, it makes it ; hard to read, (not to say for bad design!) but I was feeling like ; optimizing something. You can change it if you like. ; ; Input ; ds,es - default data segment, all address below relative to this ; di - last byte in path and file name of compiler ; bp-08 - length of BC++/TC++ lib path ; bp-0a - address of libs table (described in .data section) ; bp-0c - handle to batch file ; bp-0e - address of last byte of bin dir + 1 ; bp-10h - address of last byte of lib dir + 1 ; bp-12h - address of last byte in tlib command + 1 ; libBuffer - full path to lib\ directory ; binBuffer - path and filename for the compiler ; incBuffer - path and filename for 'include\apstring.obj' ; tlibCommand - path and filename for 'bin\tlib.exe' ; ; Output ; AX - 0 if successful, 1 if not ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ProcessLibs label near ; ; Later we'll use the address of the last byte in path and filename of ; compiler. Meanwhile, we need to use di for other things, so we'll ; save it on the stack. ; CCEND equ bp-18h push di ; We will use other local data in this routine. ; ; LIBTBLPOS will be the 2-byte offset of the current position ; in the libs table at which we are processing. ; LIBTBLPOS equ bp-1Ah PROCESSLIBSDATASIZE equ 2 sub sp,PROCESSLIBSDATASIZE ;allocate local data space ; ; Now on with the show ; mov si,[bp-0ah] ;get libs table mov si,[si] mov al,byte ptr [si] ;if first byte is 1, list test al,al ; has already been processed jnz LibsDone mov byte ptr [si],1 ;flag that list has now been processed inc si ;get number of entries in list xor cx,cx mov cl,byte ptr [si] inc si ;set si to first libname mov di,[bp-10h] ;set es:di to last byte in lib dir + 1 IterateRTLibs: call CopyString inc si ;save address of next entry mov [LIBTBLPOS],si ; in libs list mov si,offset lib ;add '.lib' push cx ;save loop counter for libs list mov cx,4 rep movsb pop cx ;restore loop counter mov dx,offset libBuffer ;check to see lib exists mov byte ptr [di],0 ; null terminate filename push cx ;save loop counter for libs list mov cx,0 mov ax,4e00h int 21h pop cx ;restore loop counter jc ProcessNextLib ; ; At this point we know the lib exists, so assemble the compiler ; command. ; mov si,[LIBTBLPOS] ;set si to compiler switches for this lib's memory model mov di,[CCEND] ;set di to end of compiler cmd call CopyString mov si,offset incBuffer ;append \include\apstring.cpp call CopyString ; ; Compiler command is now assembled, so write it to the batch file ; push cx ;save loop counter mov bx,[bp-0ch] ;put file handle in BX mov si,offset binBuffer call WriteCmdToBatchFile pop cx ;restore loop counter jc ErrorWritingFile ; ; Now assemble the appropriate tlib command ; mov di,[bp-12h] ;set es:di to end of tlib cmd mov al,' ' ;now add ' ' and libname stosb mov si, offset libBuffer call CopyString mov si,offset apstringobj ;append ' -+apstring.obj' push cx ;must save our loop counter mov cx,len_apstringobj rep movsb pop cx ;restore loop counter call AppendCRLF ;append CR/LF ; ; Tlib command is now assembled, so write it to the batch file ; push cx ;save loop counter mov bx,[bp-0ch] ;put file handle in BX mov si,offset tlibCommand call WriteCmdToBatchFile pop cx ;restore loop counter jc ErrorWritingFile ; ; Here we check if we have exhausted the list of libs and return if ; so. Otherwise, jump to the next entry in the libs list and loop again ; ProcessNextLib: dec cx test cx,cx jz LibsDone push cx ;save our loop counter mov cx,BUFFERSIZE mov di,[LIBTBLPOS] ;get current location in libs mov al,0 ;search forward for null repnz scasb ; terminator jcxz ErrorProcessingLibs pop cx ;restore loop counter mov si,di mov di,[bp-10h] ;set es:di to last byte in lib dir + 1 jmp IterateRTLibs ErrorProcessingLibs: pop cx mov cx,1 push 0 push [p_eMsgProcessingL] push LARGE 0 push 0 call MESSAGEBOX ;handle error jmp LibsDone ErrorWritingFile: mov cx,1 push 0 push [p_eMsgWF] push LARGE 0 push 0 call MESSAGEBOX ;handle error jmp LibsDone LibsDone: mov ax,cx add sp,PROCESSLIBSDATASIZE pop di ;restore regs ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; AddCRLF ; ; Appends the string in ES:DI with the bytes 0d0a. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; AppendCRLF label near mov al,0dh stosb mov al,0ah stosb ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; WriteCmdToBatchFile ; ; This function writes a command in our batch file. The string passed is ; a bcc/tcc (etc.) or tlib command. This routine first writes '0d0acall ' ; to the batch file and then the string passed. ; ; Input: ; bx Handle to the batch file. ; ds:si Beginning of command string to write. ; ds:di Last byte of command string to write. Need not be '0'. ; ; Output: ; ax Number of bytes written if CF not set. ; Error code if CF set. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; WriteCmdToBatchFile label near mov dx,offset str_call mov cx,len_call mov ah,40h int 21h mov dx,si mov cx,di ;dx --> beginning di-->end sub cx,dx ; length of command string mov ah,40h ; will be ds:di - ds:dx int 21h ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; SearchEnvironment ; ; Note: The following is taken from tasm5\examples\whereis\iexecdos.asm ; and cut up a bit. ; ; This routine searches for a variable in the environment block. ; ; Input ; DS:SI - Points to a string like "NAME=" which is to be ; found in the environment. It should be an ASCIIZ string. ; Output ; If the variable is found, ; AL - 0 ; ES:DI - Points to string after the = sign in the environment. ; If the variable is not found, ; AL is nonzero. ; Registers modified ; all ; ; The environment block is a group of null terminated strings. ; A string beginning with zero signals the end of the eviroment block. ; ; Please see "PC Interrupts", Addison Wesley, documentation on int 21, ; function 26h for a description of PSP header. You can also see ; tasm5\examples\whereis\idos.inc or HELPPC.COM ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SearchEnvironment label near cld ;Set direction for scanning to increment ; Set ES:DI to environment block push ds mov ax,@data ; Reset to our datasegment since the "NAME=" ; might be in another segment. mov ds,ax call GETDOSENVIRONMENT mov es,dx xor di,di pop ds CheckEnvironmentEnd: mov bx,si ;initialize BX pointer to name to find mov al,byte ptr es:[di] or al,al je MatchFailed ;jump if end is found CheckNextByte: mov al,[bx] ;get character to match. or al,al ;if end of name we are done! jz MatchCompleted ; (AL will be zero) cmp al,byte ptr es:[di] ;compare to char in environment block jne FindNextString ;jump if match failed inc bx inc di jmp CheckNextByte FindNextString: xor al,al ;scan forward in Environment Block mov cx,0FFFFh ;for zero byte. repnz scasb jmp CheckEnvironmentEnd ;go compare next string MatchFailed: inc al ;return al<>0 as failure flag MatchCompleted: ;all matched, return ES:DI pointing ; to parameter. al = 0 ret end