/*++ 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 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 ); }