windows-nt/Source/XPSP1/NT/base/cmd/ctools3.c
2020-09-26 16:20:57 +08:00

982 lines
24 KiB
C

/*++
Copyright (c) 1988-1999 Microsoft Corporation
Module Name:
ctools3.c
Abstract:
Low level utilities
--*/
#include "cmd.h"
struct envdata CmdEnv ; /* Holds info need to manipulate Cmd's environment */
extern unsigned tywild; /* type is wild flag @@5@J1 */
extern TCHAR CurDrvDir[], PathChar, Delimiters[] ;
extern TCHAR VolSrch[] ; /* M009 */
extern TCHAR BSlash ;
extern unsigned DosErr ;
/*** FullPath - build a full path name
*
* Purpose:
* See below.
*
* int FullPath(TCHAR * buf, TCHAR *fname)
*
* Args:
* buf - buffer to write full pathname into. (M017)
* fname - a file name and/or partial path
*
* Returns: (M017)
* FAILURE if malformed pathname (erroneous '.' or '..')
* SUCCESS otherwise
*
* Notes:
* - '.' and '..' are removed from the translated string (M017)
* - VERY BIG GOTCHA! Note that the 509 change can cause
* this rountine to modify the input filename (fname), because
* it strips quotes and copies it over the input filename.
*
*/
int FullPath(
TCHAR *buf,
TCHAR *fname,
ULONG sizpath
)
{
unsigned rc = SUCCESS; /* prime with good rc */
unsigned buflen; /* buffer length */
TCHAR *filepart;
DWORD rv;
mystrcpy(fname, StripQuotes(fname) );
if (*fname == NULLC) {
GetDir(buf,GD_DEFAULT);
buf += 2; /* Inc past drivespec */
buflen = mystrlen(buf); /* Is curdir root only? */
if (buflen >= MAX_PATH-3) { /* If too big then stop */
DosErr = ERROR_PATH_NOT_FOUND;
rc = FAILURE;
} else if (buflen != 1) { /* if not root then append */
*(buf+buflen++) = PathChar; /* ...a pathchar and... */
*(buf+buflen) = NULLC ; /* ...a null byte... */
} /* */
} else {
if ((mystrlen(fname) == 2) && (*(fname + 1) == COLON)) {
GetDir(buf,*fname); /* Get curdrvdir */
if ((buflen = mystrlen(buf)) > 3) {
*(buf+buflen++) = PathChar; /* ...a pathchar and... */
*(buf+buflen) = NULLC ; /* ...a null byte... */
}
} else {
DWORD dwOldMode;
dwOldMode = SetErrorMode(0);
SetErrorMode(SEM_FAILCRITICALERRORS);
rv = GetFullPathName( fname, sizpath, buf, &filepart );
SetErrorMode(dwOldMode);
if (!rv || rv > sizpath ) {
DosErr = ERROR_FILENAME_EXCED_RANGE;
rc = FAILURE;
}
}
}
return(rc);
}
/*** FileIsDevice - check a handle to see if it references a device
*
* Purpose:
* Return a nonzero value if fh is the file handle for a device.
* Otherwise, return 0.
*
* int FileIsDevice(int fh)
*
* Args:
* fh - the file handle to check
*
* Returns:
* See above.
*
*/
unsigned int flgwd;
int FileIsDevice( CRTHANDLE fh )
{
HANDLE hFile;
DWORD dwMode;
unsigned htype ;
hFile = CRTTONT(fh);
htype = GetFileType( hFile );
htype &= ~FILE_TYPE_REMOTE;
if (htype == FILE_TYPE_CHAR) {
//
// Simulate old behavior of this routine of setting the flgwd
// global variable with either 0, 1 or 2 to indicate if the
// passed handle is NOT a CON handle or is a CON input handle or
// is a CON output handle.
//
switch ( fh ) {
case STDIN:
hFile = GetStdHandle(STD_INPUT_HANDLE);
break;
case STDOUT:
hFile = GetStdHandle(STD_OUTPUT_HANDLE);
break;
case STDERR:
hFile = GetStdHandle(STD_ERROR_HANDLE);
break;
}
if (GetConsoleMode(hFile,&dwMode)) {
if (dwMode & (ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_ECHO_INPUT)) {
flgwd = 1;
} else if (dwMode & (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT)) {
flgwd = 2;
}
} else {
flgwd = 0;
}
return TRUE;
} else {
flgwd = 0;
return FALSE;
}
}
int FileIsPipe( CRTHANDLE fh )
{
unsigned htype ;
htype = GetFileType( CRTTONT(fh) );
htype &= ~FILE_TYPE_REMOTE;
flgwd = 0;
return( htype == FILE_TYPE_PIPE ) ; /* @@4 */
}
int FileIsRemote( LPTSTR FileName )
{
LPTSTR p;
TCHAR Drive[MAX_PATH*2];
DWORD Length;
Length = GetFullPathName( FileName, sizeof(Drive)/sizeof(TCHAR), Drive, &p );
if (Length != 0 && Length < MAX_PATH * 2) {
Drive[3] = 0;
if (GetDriveType( Drive ) == DRIVE_REMOTE) {
return TRUE;
}
}
return FALSE;
}
int FileIsConsole(CRTHANDLE fh)
{
unsigned htype ;
DWORD dwMode;
HANDLE hFile;
hFile = CRTTONT(fh);
if (hFile == INVALID_HANDLE_VALUE) {
return FALSE;
}
htype = GetFileType( hFile );
htype &= ~FILE_TYPE_REMOTE;
if ( htype == FILE_TYPE_CHAR ) {
switch ( fh ) {
case STDIN:
hFile = GetStdHandle(STD_INPUT_HANDLE);
break;
case STDOUT:
hFile = GetStdHandle(STD_OUTPUT_HANDLE);
break;
case STDERR:
hFile = GetStdHandle(STD_ERROR_HANDLE);
break;
}
if (GetConsoleMode(hFile,&dwMode)) {
return TRUE;
}
}
return FALSE;
}
/*** GetDir - get a current directory string
*
* Purpose:
* Get the current directory of the specified drive and put it in str.
*
* int GetDir(TCHAR *str, TCHAR dlet)
*
* Args:
* str - place to store the directory string
* dlet - the drive letter or 0 for the default drive
*
* Returns:
* 0 or 1 depending on the value of the carry flag after the CURRENTDIR
* system call/
*
* Notes:
* - M024 - If dlet is invalid, we leave the buffer as simply the
* null terminated root directory string.
*
*/
int GetDir(TCHAR *str, TCHAR dlet)
{
TCHAR denvname[ 4 ];
TCHAR *denvvalue;
if (dlet == GD_DEFAULT) {
GetCurrentDirectory(MAX_PATH, str);
return( SUCCESS );
}
denvname[ 0 ] = EQ;
denvname[ 1 ] = (TCHAR)_totupper(dlet);
denvname[ 2 ] = COLON;
denvname[ 3 ] = NULLC;
denvvalue = GetEnvVar( denvname );
if (!denvvalue) {
*str++ = (TCHAR)_totupper(dlet);
*str++ = COLON;
*str++ = BSLASH;
*str = NULLC;
return(FAILURE);
} else {
mystrcpy( str, denvvalue );
return(SUCCESS);
}
}
BOOL
FixupPath(
TCHAR *path,
BOOL fShortNames
)
{
TCHAR c, *src, *dst, *s;
int n, n1, length;
WIN32_FIND_DATA FindFileData;
HANDLE hFind;
length = _tcslen( path );
if (length > MAX_PATH) {
return FALSE;
}
src = path + 3; // Skip root directory.
dst = path + 3;
do {
c = *src;
if (!c || c == PathChar) {
*src = NULLC;
hFind = FindFirstFile( path, &FindFileData );
*src = c;
if (hFind != INVALID_HANDLE_VALUE) {
FindClose( hFind );
if (FindFileData.cAlternateFileName[0] &&
(fShortNames ||
(!_tcsnicmp( FindFileData.cAlternateFileName, dst, (UINT)(src - dst)) &&
_tcsicmp( FindFileData.cFileName, FindFileData.cAlternateFileName)
)
)
)
//
// Use short name if requested or
// if input is explicitly using it and short name differs from long name
//
s = FindFileData.cAlternateFileName;
else
s = FindFileData.cFileName;
n = _tcslen( s );
n1 = n - (int)(src - dst);
//
// Make sure we don't overflow name
//
if (length + n1 > MAX_PATH) {
return FALSE;
} else {
length += n1;
}
if (n1 > 0) {
memmove( src+n1, src, _tcslen(src)*sizeof(TCHAR) );
src += n1;
}
_tcsncpy( dst, s, n );
dst += n;
_tcscpy( dst, src );
dst += 1;
src = dst;
} else {
src += 1;
dst = src;
}
}
src += 1;
}
while (c != NULLC);
return TRUE;
}
/*** ChangeDirectory - change a current directory
*
* Purpose:
* Change the current directory on a drive. We do this either
* via changing the associated environment variable, or
* by changing the Win32 drive and directory.
*
* Args:
* newdir - directory (optionally w/drive)
* op - what operation should be performed
* CD_SET_DRIVE_DIRECTORY - set the Win32 current directory and drive
* CD_SET_DIRECTORY - set the Win32 current directory if the same drive
* CD_SET_ENV - set the environment variables for the current directory
* on a drive that's not the default drive.
*
* Returns:
* SUCCESS if the directory was changed.
* FAILURE otherwise.
*/
int ChangeDirectory(
TCHAR *newdir,
CHANGE_OP op
)
{
TCHAR denvname[ 4 ];
TCHAR newpath[ MAX_PATH + MAX_PATH ];
TCHAR denvvalue[ MAX_PATH ];
TCHAR c, *s;
DWORD attr;
DWORD newdirlength,length;
//
// UNC paths are not allowed
//
if (newdir[0] == PathChar && newdir[1] == PathChar)
return MSG_NO_UNC_CURDIR;
//
// truncate trailing spaces on ..
//
if (newdir[0] == DOT && newdir[1] == DOT) {
DWORD i, fNonBlank;
fNonBlank = 0;
newdirlength = mystrlen(newdir);
for (i=2; i<newdirlength; i++) {
if (newdir[i] != SPACE) {
fNonBlank = 1;
break;
}
}
if ( ! fNonBlank) {
newdir[2] = NULLC;
}
}
//
// Get current drive and directory in order to
// set up for forming the environment variable
//
GetCurrentDirectory( MAX_PATH, denvvalue );
c = (TCHAR)_totupper( denvvalue[ 0 ] );
//
// Convention for environment form is:
// Name of variable =d:
// value is full path including drive
//
denvname[ 0 ] = EQ;
if (_istalpha(*newdir) && newdir[1] == COLON) {
denvname[ 1 ] = (TCHAR)_totupper(*newdir);
newdir += 2;
} else {
denvname[ 1 ] = c;
}
denvname[ 2 ] = COLON;
denvname[ 3 ] = NULLC;
newdirlength = mystrlen(newdir);
if (*newdir == PathChar) {
if ((newdirlength+2) > sizeof(newpath)/sizeof( TCHAR )) {
return ERROR_FILENAME_EXCED_RANGE;
}
newpath[ 0 ] = denvname[ 1 ]; // drive
newpath[ 1 ] = denvname[ 2 ]; // colon
mystrcpy( &newpath[ 2 ], newdir );
} else {
if (s = GetEnvVar( denvname )) {
mystrcpy( newpath, s );
} else {
newpath[ 0 ] = denvname[ 1 ]; // drive
newpath[ 1 ] = denvname[ 2 ]; // colon
newpath[ 2 ] = NULLC;
}
//
// Make sure there's exactly one backslash between newpath and newdir
//
s = lastc( newpath );
//
// s points to the last character or point to NUL (if newpath was
// zero length to begin with). A NULL means we drop in a path char
// over the NUL. A non-path char means we append a path char.
//
if (*s == NULLC) {
*s++ = PathChar;
} else if (*s != PathChar) {
s[1] = PathChar;
s += 2;
}
if (newdirlength + (s - newpath) > sizeof( newpath ) / sizeof( TCHAR )) {
return ERROR_FILENAME_EXCED_RANGE;
}
mystrcpy( s, newdir );
}
denvvalue[(sizeof( denvvalue )-1)/sizeof( TCHAR )] = NULLC;
//
// form the full path name
//
if ((length = GetFullPathName( newpath, (sizeof( denvvalue )-1)/sizeof( TCHAR ), denvvalue, &s ))==0) {
return( ERROR_ACCESS_DENIED );
}
//
// Remove any trailing backslash
//
if (s == NULL) {
s = denvvalue + _tcslen( denvvalue );
}
if (*s == NULLC && s > &denvvalue[ 3 ] && s[ -1 ] == PathChar) {
*--s = NULLC;
}
//
// Verify that there won't be (initially) disk errors when we touch
// the directory
//
attr = GetFileAttributes( denvvalue );
if (attr == -1) {
attr = GetLastError( );
if (attr != ERROR_FILE_NOT_FOUND &&
attr != ERROR_PATH_NOT_FOUND &&
attr != ERROR_INVALID_NAME) {
return attr;
}
}
//
// If extensions are enabled, fixup the path to have the same case as
// on disk
//
if (fEnableExtensions) {
if (!FixupPath( denvvalue, FALSE )) {
return ERROR_FILENAME_EXCED_RANGE;
}
}
if (op != CD_SET_ENV) {
attr = GetFileAttributes( denvvalue );
if (attr == (DWORD)-1 ||
!(attr & (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_REPARSE_POINT))
) {
if ( attr == -1 ) {
attr = GetLastError();
if ( attr == ERROR_FILE_NOT_FOUND ) {
attr = ERROR_PATH_NOT_FOUND;
}
return attr;
}
return( ERROR_DIRECTORY );
}
}
//
// If we're always setting the directory or
// if we're setting the directory if the drives are the same and
// the drives ARE the same
//
if (op == CD_SET_DRIVE_DIRECTORY
|| (op == CD_SET_DIRECTORY
&& c == denvname[ 1 ])) {
if (!SetCurrentDirectory( denvvalue )) {
return GetLastError();
}
}
if (SetEnvVar(denvname,denvvalue,&CmdEnv)) {
return( ERROR_NOT_ENOUGH_MEMORY );
}
GetDir(CurDrvDir, GD_DEFAULT) ;
return SUCCESS;
}
/*** ChangeDir2 - change a current directory
*
* Purpose:
* To change to the directory specified in newdir on the drive specified
* in newdir. If no drive is given, the default drive is used. If the
* directory of the current drive is changed, the global variable
* CurDrvDir is updated.
*
* This routine is used by RestoreCurrentDirectories
*
* int ChangeDir2(BYTE *newdir, BOOL )
*
* Args:
* newdir - directory to change to
* BOOL - current drive
*
* Returns:
* SUCCESS if the directory was changed.
* FAILURE otherwise.
*
*/
int ChangeDir2(
TCHAR *newdir,
BOOL CurrentDrive
)
{
return ChangeDirectory( newdir, CurrentDrive ? CD_SET_DRIVE_DIRECTORY : CD_SET_ENV );
}
/*** ChangeDir - change a current directory
*
* Purpose:
* To change to the directory specified in newdir on the drive specified
* in newdir. If no drive is given, the default drive is used. If the
* directory of the current drive is changed, the global variable
* CurDrvDir is updated.
*
* int ChangeDir(TCHAR *newdir)
*
* Args:
* newdir - directory to change to
*
* Returns:
* SUCCESS if the directory was changed.
* FAILURE otherwise.
*
*/
int ChangeDir(
TCHAR *newdir
)
{
return ChangeDirectory( newdir, CD_SET_DIRECTORY );
}
/*** exists - Determine if a given file exists
*
* Purpose:
* To test the existence of a named file.
*
* int exists(TCHAR *filename)
*
* Args:
* filename - the filespec to test
*
* Returns:
* TRUE if file exists
* FALSE if it does not.
*
* Notes:
* M020 - Now uses ffirst to catch devices, directories and wildcards.
*/
exists(filename)
TCHAR *filename;
{
WIN32_FIND_DATA buf ; /* Use for ffirst/fnext */
HANDLE hn ;
int i ; /* tmp */
TCHAR FullPath[ 2 * MAX_PATH ];
TCHAR *p, *p1, SaveChar;
p = StripQuotes(filename);
i = GetFullPathName( p, 2 * MAX_PATH, FullPath, &p1 );
if (i) {
p = FullPath;
if (!_tcsncmp( p, TEXT("\\\\.\\"), 4 )) {
//
// If they gave us a device name, then see if they put something
// in front of it.
//
p += 4;
p1 = p;
if ((p1 = _tcsstr( filename, p )) && p1 > filename) {
//
// Something in front of the device name, so truncate the input
// path at the device name and see if that exists.
//
SaveChar = *p1;
*p1 = NULLC;
i = (int)GetFileAttributes( filename );
*p1 = SaveChar;
if (i != 0xFFFFFFFF) {
return i;
} else {
return 0;
}
} else {
//
// Just a device name given. See if it is valid.
//
i = (int)GetFileAttributes( filename );
if (i != 0xFFFFFFFF) {
return i;
} else {
return 0;
}
}
}
if (p1 == NULL || *p1 == NULLC) {
i = (int)(GetFileAttributes( p ) != 0xFFFFFFFF);
} else {
i = ffirst( p, A_ALL, &buf, &hn );
findclose(hn);
if ( i == 0 ) {
//
// ffirst handles files & directories, but not
// root drives, so do special check for them.
//
if ( *(p+1) == (TCHAR)'\\' ||
(*(p+1) == (TCHAR)':' &&
*(p+2) == (TCHAR)'\\' &&
*(p+3) == (TCHAR)0
)
) {
UINT t;
t = GetDriveType( p );
if ( t > 1 ) {
i = 1;
}
}
}
}
}
return i;
}
/*** exists_ex - Determine if a given executable file exists @@4
*
* Purpose:
* To test the existence of a named executable file.
*
* int exists_ex(TCHAR *filename)
*
* Args:
* filename - the filespec to test
* checkformeta - if TRUE, check for wildcard char
*
* Returns:
* TRUE if file exists
* FALSE if it does not.
*
* Notes:
* @@4 - Now uses ffirst to catch only files .
*/
exists_ex(filename,checkformeta) /*@@4*/
TCHAR *filename; /*@@4*/
BOOL checkformeta;
{ /*@@4*/
WIN32_FIND_DATA buf; /* use for ffirst/fnext */
HANDLE hn;
int i;
TCHAR *ptr;
/* can not execute wild card files, so check for those first */
if (checkformeta && (mystrchr( filename, STAR ) || mystrchr( filename, QMARK ))) {
DosErr = 3;
i = 0;
} else {
/* see if the file exists, do not include Directory, volume, or */
/* hidden files */
i = ((ffirst( filename , A_AEDV, &buf, &hn))) ;
if ( i ) {
findclose(hn) ;
/* if the file exists then copy the file name, to get the case */
ptr = mystrrchr( filename, BSLASH );
if ( ptr == NULL ) {
ptr = filename;
if ( mystrlen( ptr ) > 2 && ptr[1] == COLON ) {
ptr = &filename[2];
}
} else {
ptr++;
}
mystrcpy( ptr, buf.cFileName);
} else if ( DosErr == 18 ) {
DosErr = 2;
}
}
return(i) ; /*@@4*/
} /*@@4*/
/*** FixPChar - Fix up any leading path in a string
*
* Purpose:
* To insure that paths match the current Swit/Pathchar setting
*
* void FixPChar(TCHAR *str, TCHAR PChar)
*
* Args:
* str - the string to fixup
* Pchar - character to replace
*
* Returns:
* Nothing
*
*/
void FixPChar(TCHAR *str, TCHAR PChar)
{
TCHAR *sptr1, /* Index for string */
*sptr2 ; /* Change marker */
sptr1 = str ; /* Init to start of string */
while (sptr2 = mystrchr(sptr1,PChar)) {
*sptr2++ = PathChar ;
sptr1 = sptr2 ;
} ;
}
/*** FlushKB - Remove extra unwanted input from Keyboard
*
* Purpose:
* To perform a keyboard flush up to the next CR/LF.
*
* FlushKB()
*
* Args:
* None
*
* Returns:
* Nothing
*
*/
void FlushKB()
{
DWORD cnt;
TCHAR IgnoreBuffer[128];
while (ReadBufFromInput( GetStdHandle(STD_INPUT_HANDLE), IgnoreBuffer, 128, &cnt )) {
if (mystrchr( IgnoreBuffer, CR ))
return ;
}
}
/*** DriveIsFixed - Determine if drive is removeable media
*
* Purpose: @@4
* To determine if the input drive is a removeable media.
*
* DriveIsFixed(TCHAR *drive_ptr )
*
* Args:
* drive_ptr - pointer to a file name that contains a drive
* specification.
*
* Returns:
* 1 - if error or non removeable media
* 0 - if no error and removeable media
*/
int
DriveIsFixed( TCHAR *drive_ptr )
{
unsigned rc = 0;
TCHAR drive_spec[3];
drive_spec[0] = *drive_ptr;
drive_spec[1] = COLON;
drive_spec[2] = NULLC;
// FIX, FIX - use GetVolumeInfo, disabling hard errors?
if ((*drive_ptr == TEXT('A')) || (*drive_ptr == TEXT('B'))) {
rc = 0;
} else {
rc = 1;
}
return( rc );
}
int
CmdPutChars(
PTCHAR String,
int Length
)
{
int rc = SUCCESS; /* return code */
int bytesread; /* bytes to write count */
int byteswrit; /* bytes written count */
BOOL flag;
if (Length > 0) {
if (FileIsConsole(STDOUT)) {
if (!(flag=WriteConsole(CRTTONT(STDOUT), String, Length, &byteswrit, NULL)))
rc = GetLastError();
} else {
Length *= sizeof(TCHAR);
flag = MyWriteFile( STDOUT, (CHAR *)String, Length, &byteswrit);
}
//
// If the write failed or unable to output all the data
//
if (flag == 0 || byteswrit != Length) {
rc = GetLastError();
//
// No error => DiskFull
//
if (rc == 0) {
rc = ERROR_DISK_FULL;
}
if (FileIsDevice(STDOUT)) {
//
// If the we were writing to a device then the error is a device
// write fault.
//
PutStdErr( ERROR_WRITE_FAULT, NOARGS );
} else if (FileIsPipe(STDOUT)) {
//
// If the we were writing to a pipe, then the error is an invalid
// pipe error.
//
PutStdErr(MSG_CMD_INVAL_PIPE, NOARGS);
return(FAILURE);
} else {
//
// Just output the error we found
//
PrtErr(rc);
return(FAILURE);
}
}
}
return(rc);
}
int
CmdPutString(
PTCHAR String
)
{
return CmdPutChars( String, _tcslen( String ));
}
int
cmd_printf(
TCHAR *fmt,
...
)
{
int Length;
va_list arg_ptr;
va_start(arg_ptr,fmt);
Length = _vsntprintf( MsgBuf, MAXCBMSGBUFFER, fmt, arg_ptr );
va_end(arg_ptr);
return CmdPutChars( MsgBuf, Length );
}