windows-nt/Source/XPSP1/NT/base/ntsetup/textmode/winnt/dnutil.c

1167 lines
22 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
dnutil.c
Abstract:
Miscellaneous routines for DOS-hosted NT setup program.
Author:
Ted Miller (tedm) 30-March-1992
Revision History:
--*/
#include "winnt.h"
#include <string.h>
#include <process.h>
#include <malloc.h>
#include <dos.h>
#include <direct.h>
#include <ctype.h>
BOOLEAN WarnedAboutSkip = FALSE;
ULONG
DnGetKey(
VOID
)
/*++
Routine Description:
Waits for any keypress.
Arguments:
None.
Return Value:
Actual key pressed.
--*/
{
USHORT c;
#if NEC_98
_asm {
xor ax,ax // function: read char from keyboard
int 18h
mov c,ax
}
switch(c) {
case 0x3d00: // down arrow
return(DN_KEY_DOWN);
case 0x3a00: // up arrow
return(DN_KEY_UP);
case 0x3e00: // home
return(DN_KEY_HOME);
case 0x3f00: // end
return(DN_KEY_END);
case 0x3600: // page up
return(DN_KEY_PAGEUP);
case 0x3700: // page down
return(DN_KEY_PAGEDOWN);
case 0x6200: // F1
return(DN_KEY_F1);
case 0x6300: // F2
return(DN_KEY_F2);
case 0x6400: // F3
return(DN_KEY_F3);
case 0x6500: // F4
return(DN_KEY_F4);
default:
return((ULONG)(c & ((c & 0x00ff) ? 0x00ff : 0xffff)));
}
#else // NEC_98
_asm {
mov ah,0 // function: read char from keyboard
int 16h
mov c,ax
}
switch(c) {
case 0x5000: // down arrow
return(DN_KEY_DOWN);
case 0x4800: // up arrow
return(DN_KEY_UP);
case 0x4700: // home
return(DN_KEY_HOME);
case 0x4f00: // end
return(DN_KEY_END);
case 0x4900: // page up
return(DN_KEY_PAGEUP);
case 0x5100: // page down
return(DN_KEY_PAGEDOWN);
case 0x3b00: // F1
return(DN_KEY_F1);
case 0x3c00: // F2
return(DN_KEY_F2);
case 0x3d00: // F3
return(DN_KEY_F3);
case 0x3e00: // F4
return(DN_KEY_F4);
default:
return((ULONG)(c & ((c & 0x00ff) ? 0x00ff : 0xffff)));
}
#endif // NEC_98
}
ULONG
DnGetValidKey(
IN PULONG ValidKeyList
)
/*++
Routine Description:
Waits for a keypress matching one of the values given in a list.
The list must be terminated with a 0 entry.
Arguments:
ValidKeyList - valid keys.
Return Value:
Actual key pressed.
--*/
{
ULONG key;
int i;
while(1) {
key = DnGetKey();
for(i=0; ValidKeyList[i]; i++) {
if(key == ValidKeyList[i]) {
return(key);
}
}
}
}
VOID
vDnDisplayScreen(
IN PSCREEN Screen,
IN va_list arglist
)
/*++
Routine Description:
Displays a screen.
Arguments:
Screen - supplies pointer to structure describing screen to display.
arglist - supplies list of arguments for printf-style formatting.
Return Value:
None.
--*/
{
UCHAR y;
PCHAR p;
PCHAR CurrentLine;
int i;
static CHAR FormatString[1600],FormattedString[1600];
//
// Take each line in the screen and put in into a buffer, to form
// one large string. Place newlines at the end of each string.
//
for(FormatString[0]=0,i=0; Screen->Strings[i]; i++) {
if(strlen(FormatString)+strlen(Screen->Strings[i])+2 < sizeof(FormatString)) {
strcat(FormatString,Screen->Strings[i]);
strcat(FormatString,"\n");
} else {
break;
}
}
//
// Format the string using given arguments.
//
vsprintf(FormattedString,FormatString,arglist);
for(y=Screen->Y,CurrentLine=FormattedString; CurrentLine && *CurrentLine; y++) {
if(p = strchr(CurrentLine,'\n')) {
*p = 0;
}
DnPositionCursor(Screen->X,y);
DnWriteString(CurrentLine);
CurrentLine = p ? p+1 : NULL;
}
}
VOID
DnDisplayScreen(
IN PSCREEN Screen,
...
)
/*++
Routine Description:
Displays a screen.
Arguments:
Screen - supplies pointer to structure describing screen to display.
Return Value:
None.
--*/
{
va_list arglist;
va_start(arglist,Screen);
vDnDisplayScreen(Screen,arglist);
va_end(arglist);
}
VOID
DnFatalError(
IN PSCREEN Screen,
...
)
/*++
Routine Description:
Displays a fatal error screen and prompts the user to press enter
to exit. DOES NOT RETURN.
Arguments:
Screen - supplies pointer to structure describing screen to display.
Return Value:
DOES NOT RETURN.
--*/
{
ULONG ExitOnlyKeyList[2] = { ASCI_CR,0 };
va_list arglist;
int i;
DnClearClientArea();
DnWriteStatusText(DntEnterEqualsExit);
va_start(arglist,Screen);
vDnDisplayScreen(Screen,arglist);
va_end(arglist);
for(i=0; Screen->Strings[i]; i++);
DnPositionCursor(Screen->X,(UCHAR)(Screen->Y + i + 1));
DnWriteString(DntPressEnterToExit);
DnGetValidKey(ExitOnlyKeyList);
DnExit(1);
}
BOOLEAN
DnCopyError(
IN PCHAR Filename,
IN PSCREEN ErrorScreen,
IN int FilenameLine
)
/*++
Routine Description:
Displays a screen informing the user that there has been an error copying
a file, and allows the options of continuing or exiting Setup.
Arguments:
Filename - supplies name of source file which could not be copied.
ErrorScreen - supplies the text to label the error.
FilenameLine - supplies line number on the ErrorScreen in which the
filename should be displayed.
Return Value:
TRUE if user elects to retry; FALSE if user elects to continue;
does not return if user chooses to exit.
--*/
{
ULONG KeyList[4] = { ASCI_CR,DN_KEY_F3,ASCI_ESC,0 };
ULONG KeyList2[4] = { 0,0,ASCI_CR,0 };
KeyList2[0] = DniAccelSkip1;
KeyList2[1] = DniAccelSkip2;
DnClearClientArea();
DnWriteStatusText("%s %s %s",DntEnterEqualsRetry,DntEscEqualsSkipFile,DntF3EqualsExit);
ErrorScreen->Strings[FilenameLine] = Filename;
DnDisplayScreen(ErrorScreen);
while(1) {
switch(DnGetValidKey(KeyList)) {
case DN_KEY_F3:
DnExitDialog();
break;
case ASCI_CR:
return(TRUE); // retry
case ASCI_ESC:
if(!WarnedAboutSkip) {
DnClearClientArea();
DnDisplayScreen(&DnsSureSkipFile);
DnWriteStatusText("%s %s",DntEnterEqualsRetry,DntXEqualsSkipFile);
if(DnGetValidKey(KeyList2) == ASCI_CR) {
//
// retry
//
return(TRUE);
} else {
//
// User elected to skip: prevent future warnings.
//
WarnedAboutSkip = TRUE;
}
}
return(FALSE); // skip file
}
}
}
PCHAR
DnDupString(
IN PCHAR String
)
/*++
Routine Description:
Duplicate a string. Do not return if not enough memory.
Arguments:
String - string to be duplicated
Return Value:
Pointer to new string. Does not return if insufficient memory.
--*/
{
if( String ) {
PCHAR p = MALLOC(strlen(String)+1,TRUE);
return(strcpy(p,String));
} else {
return( NULL );
}
}
VOID
DnGetString(
IN OUT PCHAR String,
IN UCHAR X,
IN UCHAR Y,
IN UCHAR W
)
/*++
Routine Description:
Allow the user to type a string in an edit field. Interpret F3
to allow him to exit.
Arguments:
String - on input, supplies the default string. On output, contains
the string entered by the user.
X,Y - coords of leftmost char of edit field.
W - width of edit field, and maximum length of the string.
Return Value:
None.
--*/
{
ULONG key;
int Position = strlen(String);
DnStartEditField(TRUE,X,Y,W);
DnPositionCursor(X,Y);
DnWriteString(String);
DnPositionCursor((UCHAR)(X+Position),Y);
while(1) {
key = DnGetKey();
switch(key) {
case DN_KEY_F3:
DnExitDialog();
break;
case ASCI_BS:
if(Position) {
String[--Position] = 0;
DnPositionCursor((UCHAR)(X+Position),Y);
DnWriteChar(' ');
}
break;
case ASCI_ESC:
Position = 0;
String[0] = 0;
DnStartEditField(TRUE,X,Y,W); // blanks edit field
DnPositionCursor(X,Y);
break;
case ASCI_CR:
DnStartEditField(FALSE,X,Y,W);
return;
default:
if(((UCHAR)Position < W) && !(key & 0xffffff00)) {
DnWriteChar((CHAR)key);
String[Position++] = (CHAR)key;
String[Position] = 0;
DnPositionCursor((UCHAR)(X+Position),Y);
}
}
}
}
BOOLEAN
DnIsDriveValid(
IN unsigned Drive
)
/*++
Routine Description:
Determine whether a drive is valid (ie, exists and is accessible).
Arguments:
Drive - drive (1=A, 2=B, etc).
Return Value:
TRUE if drive is valid.
FALSE if not.
--*/
{
int CurrentDrive = _getdrive();
int Status;
//
// We'll make the determination of whether the drive is valid by
// attempting to switch to it. If this succeeds, assume the drive
// is valid.
//
Status = _chdrive(Drive);
_chdrive(CurrentDrive);
return((BOOLEAN)(Status == 0));
}
BOOLEAN
DnIsDriveRemote(
IN unsigned Drive,
OUT PCHAR UncPath OPTIONAL
)
/*++
Routine Description:
Determine whether a drive is remote and optionally retrieve the
UNC path of the target.
Arguments:
Drive - drive (1=A, 2=B, etc).
UncPath - if specified and the drive is a network drive, the
UNC path the drive is connected to is returned in this buffer.
The caller must make sure the buffer is large enough (128 bytes).
If this string is empty then the unc path could not be determined.
Return Value:
TRUE if drive is remote.
FALSE if not (or we couldn't determine whether the drive is remote).
--*/
{
union REGS RegIn,RegOut;
BOOLEAN OK;
CHAR LocalBuffer[16];
unsigned LocalBufferOffset;
unsigned Index;
//
// Call IOCTL function 09.
// If carry set (error), assume not remote.
// If no error, bit 12 of dx set if remote.
//
RegIn.x.ax = 0x4409;
RegIn.h.bl = (unsigned char)Drive;
intdos(&RegIn,&RegOut);
if(!RegOut.x.cflag && (RegOut.x.dx & 0x1000)) {
if(UncPath) {
//
// If we don't do this and try instead to use
// offset LocalBuffer inside the _asm block
// we get the wrong address.
//
LocalBufferOffset = (unsigned)LocalBuffer;
Index = 0;
do {
OK = TRUE;
_asm {
push ds
push es
mov ax,0x5f02
mov bx,Index
mov cx,word ptr [UncPath+2]
mov di,word ptr [UncPath]
mov si,LocalBufferOffset
push ss
pop ds
mov es,cx
int 21h
pop es
pop ds
jnc x
mov OK,0
x:
}
if(OK) {
if(((unsigned)toupper(LocalBuffer[0]) == (Drive + (unsigned)'A' - 1))
&& (LocalBuffer[1] == ':')) {
//
// Found it. We're done.
//
return(TRUE);
}
Index++;
}
} while(OK);
//
// Couldn't find it.
//
UncPath[0] = 0;
}
return(TRUE);
} else {
return(FALSE);
}
}
BOOLEAN
DnIsDriveRemovable(
IN unsigned Drive
)
/*++
Routine Description:
Determine whether a drive is removable.
Arguments:
Drive - drive (1=A, 2=B, etc).
Return Value:
TRUE if drive is removable.
FALSE if not removable.
If an error occurs making the determination, the drive is assumed
not removable.
--*/
{
int ax;
union REGS RegIn,RegOut;
//
// Call IOCTL function 08.
//
RegIn.x.ax = 0x4408;
RegIn.h.bl = (unsigned char)Drive;
ax = intdos(&RegIn,&RegOut);
//
// If an error occured, assume not removable.
// If no error, ax = 0 if removable, ax = 1 if not removable.
//
return((BOOLEAN)(!RegOut.x.cflag && !ax));
}
BOOLEAN
DnCanonicalizePath(
IN PCHAR PathIn,
OUT PCHAR PathOut
)
/*++
Routine Description:
Canonicalize a path. The result is a fully-qualified path
that always starts with the x:\ for local or local-redirected
drives, or \\ for remote paths.
Arguments:
PathIn - supplies the path to be canonicalized. The path must be
valid (ie, it must be some form of syntactically correct MS-DOS path
and must actually exist and be accessible) for this function
to succeed.
PathOut - receives the canonicalized path if successful. The function
assumes that the buffer has space for 128 bytes.
Return Value:
Boolean value indicating outcome. If TRUE, PathOut will be filled in.
--*/
{
BOOLEAN OK;
int olddrive;
char olddir[128];
int drive;
//
// If the input path is UNC then we assume
// the path is remote and note that the dos canonicalize
// int21 function will do what we want.
//
if((PathIn[0] == '\\') && (PathIn[1] == '\\')) {
OK = TRUE;
_asm {
push ds
push es
mov si,word ptr [PathIn]
mov ax,word ptr [PathIn+2]
mov di,word ptr [PathOut]
mov cx,word ptr [PathOut+2]
mov ds,ax
mov es,cx
mov ax,0x6000
int 21h
pop es
pop ds
jnc x
mov OK,0
x:
}
} else {
//
// Not UNC. We set and read the cwd to simulate what we want.
// Preserve current cwd if possible.
//
olddrive = _getdrive();
if(!getcwd(olddir,sizeof(olddir))) {
olddir[0] = 0;
}
//
// If we have x: at the front, strip it and set the current drive.
// We do this because chdir does not set the current drive.
//
OK = TRUE;
if(isalpha(PathIn[0]) && (PathIn[1] == ':')) {
if(_chdrive(toupper(PathIn[0]) + 1 - (int)'A')) {
OK = FALSE;
} else {
PathIn += 2;
}
}
if(OK) {
OK = (BOOLEAN)(!chdir(PathIn) && (drive = _getdrive()) && _getdcwd(drive,PathOut,128));
}
if(olddir[0]) {
chdir(olddir);
}
if(olddrive) {
_chdrive(olddrive);
}
}
return(OK);
}
#if DBG
long allocated;
long allocs;
#define MEMSIG 0xa3f8
#define callerinfo() printf(" -- Caller: %s, line %u\n",file,line)
#endif
PVOID
Malloc(
IN unsigned Size,
IN BOOLEAN MustSucceed
#if DBG
,IN char *file,
IN int line
#endif
)
/*++
Routine Description:
Allocates memory and fatal errors if none is available.
Arguments:
Size - number of bytes to allocate
MustSucceed - if true, then the allocation must succeed. If memory
cannot be allocated to satisfy the request, the program displays
a fatal error message and exits.
Return Value:
Pointer to memory. If MustSucceed is TRUE, DOES NOT RETURN
if memory is not available.
--*/
{
unsigned *p;
#if DBG
p = malloc(Size+(2*sizeof(unsigned)));
if(p) {
*p++ = Size;
*(unsigned *)(((PCHAR)p)+Size) = MEMSIG;
allocated += Size;
allocs++;
} else {
if(MustSucceed) {
DnFatalError(&DnsOutOfMemory);
}
}
#else
if((p = malloc(Size)) == NULL) {
if(MustSucceed) {
DnFatalError(&DnsOutOfMemory);
}
}
#endif
return(p);
}
VOID
Free(
IN PVOID Block
#if DBG
,IN char *file,
IN int line
#endif
)
/*++
Routine Description:
Free a block of memory previously allocated with Malloc().
Arguments:
Block - supplies pointer to block to free.
Return Value:
None.
--*/
{
#if 0
extern char *CommonStrings[];
int i;
for( i = 0; i < sizeof(CommonStrings)/sizeof(char *); i++ ) {
if( (char *)Block == CommonStrings[i] ) {
return;
}
}
#endif
#if DBG
unsigned *p;
if(!allocs) {
printf("Free: allocation count going negative!\n");
callerinfo();
_asm { int 3 }
}
allocs--;
p = ((unsigned *)Block) - 1;
allocated -= *p;
if(allocated < 0) {
printf("Free: total allocation going negative!\n");
callerinfo();
_asm { int 3 }
}
if(*(unsigned *)((PCHAR)Block+(*p)) != MEMSIG) {
printf("Free: memory block lacks MEMSIG!\n");
callerinfo();
_asm { int 3 }
}
free(p);
#else
free(Block);
#endif
}
PVOID
Realloc(
IN PVOID Block,
IN unsigned Size,
IN BOOLEAN MustSucceed
#if DBG
,IN char *file,
IN int line
#endif
)
/*++
Routine Description:
Reallocates a block of memory previously allocated with Malloc();
fatal errors if none is available.
Arguments:
Block - supplies pointer to block to resize
Size - number of bytes to allocate
MustSucceed - if true, then the allocation must succeed. If memory
cannot be allocated to satisfy the request, the program displays
a fatal error message and exits.
Return Value:
Pointer to memory. If MustSucceed is TRUE,
DOES NOT RETURN if memory is not available.
--*/
{
PVOID p;
#if DBG
unsigned BlockSize;
BlockSize = ((unsigned *)Block)[-1];
allocated -= BlockSize;
allocated += Size;
if(*(unsigned *)((PCHAR)Block + BlockSize) != MEMSIG) {
printf("Realloc: memory block lacks MEMSIG!\n");
callerinfo();
_asm { int 3 }
}
p = realloc((unsigned *)Block - 1,Size + (2*sizeof(unsigned)));
if(p == NULL) {
if(MustSucceed) {
DnFatalError(&DnsOutOfMemory);
}
} else {
*(unsigned *)p = Size;
(unsigned *)p += 1;
*(unsigned *)((PCHAR)p + Size) = MEMSIG;
}
#else
if((p = realloc(Block,Size)) == NULL) {
if(MustSucceed) {
DnFatalError(&DnsOutOfMemory);
}
}
#endif
return(p);
}
VOID
DnExit(
IN int ExitStatus
)
/*++
Routine Description:
Exits back to DOS in an orderly fashion.
Arguments:
ExitStatus - supplies value to be passed to exit()
Return Value:
None. Does not return.
--*/
{
unsigned DriveCount;
//
// Do a video mode switch to clear the screen.
//
#if NEC_98
_asm {
mov al,5
out 0a2h,al
mov ax,0a00h
int 18h
mov ah,11h // function -- cursor on
int 18h
}
if(ExitStatus != 2){
printf("\x1b[>3l");
printf("\x1b[>1l");
}
else { ExitStatus = 0; }
#else // NEC_98
_asm {
mov ax,3
int 10h
}
#endif // NEC_98
// restore current drive
_dos_setdrive(DngOriginalCurrentDrive,&DriveCount);
exit(ExitStatus);
}
BOOLEAN
DnWriteSmallIniFile(
IN PCHAR Filename,
IN PCHAR *Lines,
OUT FILE **FileHandle OPTIONAL
)
{
FILE *fileHandle;
unsigned i,len;
BOOLEAN rc;
//
// If the file is already there, change attributes to normal
// so we can overwrite it.
//
_dos_setfileattr(Filename,_A_NORMAL);
//
// Open/truncate the file.
//
fileHandle = fopen(Filename,"wt");
if(fileHandle == NULL) {
return(FALSE);
}
//
// Assume success.
//
rc = TRUE;
//
// Write lines into the file indicating that this is
// a winnt setup. On a doublespaced floppy, there should
// be room for a single sector outside the CVF.
//
for(i=0; Lines[i]; i++) {
len = strlen(Lines[i]);
if(fwrite(Lines[i],1,len,fileHandle) != len) {
rc = FALSE;
break;
}
}
//
// Leave the file open if the caller wants the handle.
//
if(rc && FileHandle) {
*FileHandle = fileHandle;
} else {
fclose(fileHandle);
}
return(rc);
}
int
vsnprintf( char* target, size_t bufsize, const char *format, va_list val )
{
static CHAR BigFormatBuffer[4096];
size_t retval;
//
// Print into the big format buffer, which we're sure will be large enough
// to store 99.44% of all strings printed through here.
//
retval = vsprintf( BigFormatBuffer, format, val );
BigFormatBuffer[retval++] = '\0';
//
// And then transfer it over to the output buffer, but only as much as they
// wanted.
//
memcpy( target, BigFormatBuffer, retval < bufsize ? retval : bufsize );
//
// Zero out the end as well.
//
target[bufsize-1] = '\0';
return retval;
}
int
snprintf( char* target, size_t bufsize, const char *format, ... )
{
int retval;
va_list val;
va_start(val, format);
retval = vsnprintf( target, bufsize, format, val );
va_end( val );
return retval;
}