663 lines
18 KiB
C
663 lines
18 KiB
C
/*++
|
|
|
|
Copyright (c) 1988-1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
cpath.c
|
|
|
|
Abstract:
|
|
|
|
Path-related commands
|
|
|
|
--*/
|
|
|
|
#include "cmd.h"
|
|
|
|
extern TCHAR SwitChar, PathChar;
|
|
|
|
extern TCHAR Fmt17[] ;
|
|
|
|
extern TCHAR CurDrvDir[] ;
|
|
|
|
extern int LastRetCode ; /* @@ */
|
|
extern TCHAR TmpBuf[] ;
|
|
|
|
|
|
/**************** START OF SPECIFICATIONS ***********************/
|
|
/* */
|
|
/* SUBROUTINE NAME: eMkDir */
|
|
/* */
|
|
/* DESCRIPTIVE NAME: Begin execution of the MKDIR command */
|
|
/* */
|
|
/* FUNCTION: This routine will make any number of directories, */
|
|
/* and will continue if it encounters a bad argument. */
|
|
/* eMkDir will be called if the user enters MD or */
|
|
/* MKDIR on the command line. */
|
|
/* */
|
|
/* NOTES: */
|
|
/* */
|
|
/* ENTRY POINT: eMkDir */
|
|
/* LINKAGE: Near */
|
|
/* */
|
|
/* INPUT: n - parse tree node containing the MKDIR command */
|
|
/* */
|
|
/* EXIT-NORMAL: returns SUCCESS if all directories were */
|
|
/* successfully created. */
|
|
/* */
|
|
/* EXIT-ERROR: returns FAILURE otherwise */
|
|
/* */
|
|
/* EFFECTS: None. */
|
|
/* */
|
|
/* INTERNAL REFERENCES: */
|
|
/* ROUTINES: */
|
|
/* LoopThroughArgs - break up command line, call MdWork */
|
|
/* */
|
|
/* EXTERNAL REFERENCES: */
|
|
/* ROUTINES: */
|
|
/* */
|
|
/**************** END OF SPECIFICATIONS *************************/
|
|
|
|
|
|
int eMkdir(n)
|
|
struct cmdnode *n ;
|
|
{
|
|
|
|
DEBUG((PCGRP, MDLVL, "MKDIR: Entered.")) ;
|
|
return(LastRetCode = LoopThroughArgs(n->argptr, MdWork, LTA_CONT)) ;
|
|
}
|
|
|
|
|
|
|
|
/**************** START OF SPECIFICATIONS ***********************/
|
|
/* */
|
|
/* SUBROUTINE NAME: MdWork */
|
|
/* */
|
|
/* DESCRIPTIVE NAME: Make a directory */
|
|
/* */
|
|
/* FUNCTION: MdWork creates a new directory. */
|
|
/* */
|
|
/* NOTES: */
|
|
/* */
|
|
/* ENTRY POINT: MdWork */
|
|
/* LINKAGE: Near */
|
|
/* */
|
|
/* INPUT: arg - a pointer to a NULL terminated string of the */
|
|
/* new directory to create. */
|
|
/* */
|
|
/* EXIT-NORMAL: returns SUCCESS if the directory is made */
|
|
/* successfully */
|
|
/* */
|
|
/* EXIT-ERROR: returns FAILURE otherwise */
|
|
/* */
|
|
/* EFFECTS: None. */
|
|
/* */
|
|
/* INTERNAL REFERENCES: */
|
|
/* ROUTINES: */
|
|
/* PutStdErr - Writes to standard error */
|
|
/* */
|
|
/* EXTERNAL REFERENCES: */
|
|
/* ROUTINES: */
|
|
/* DOSMKDIR */
|
|
/* */
|
|
/**************** END OF SPECIFICATIONS *************************/
|
|
|
|
|
|
int MdWork(arg)
|
|
TCHAR *arg ;
|
|
{
|
|
ULONG Status;
|
|
TCHAR *lpw;
|
|
TCHAR TempBuffer[MAX_PATH];
|
|
DWORD Length;
|
|
|
|
/* Check if drive is valid because Dosmkdir does not
|
|
return invalid drive @@5 */
|
|
|
|
if ((arg[1] == COLON) && !IsValidDrv(*arg)) {
|
|
|
|
PutStdErr(ERROR_INVALID_DRIVE, NOARGS);
|
|
return(FAILURE) ;
|
|
}
|
|
|
|
|
|
Length = GetFullPathName(arg, MAX_PATH, TempBuffer, &lpw);
|
|
if (Length == 0) {
|
|
PutStdErr( GetLastError(), NOARGS);
|
|
return FAILURE;
|
|
}
|
|
|
|
if (Length >= MAX_PATH) {
|
|
PutStdErr( MSG_FULL_PATH_TOO_LONG, ONEARG, arg );
|
|
return FAILURE;
|
|
}
|
|
|
|
if (CreateDirectory( arg, NULL )) {
|
|
return SUCCESS;
|
|
}
|
|
|
|
Status = GetLastError();
|
|
|
|
if (Status == ERROR_ALREADY_EXISTS) {
|
|
|
|
PutStdErr( MSG_DIR_EXISTS, ONEARG, arg );
|
|
return FAILURE;
|
|
|
|
} else if (Status != ERROR_PATH_NOT_FOUND) {
|
|
PutStdErr( Status, NOARGS);
|
|
return FAILURE;
|
|
}
|
|
|
|
//
|
|
// If no extensions, then simply fail.
|
|
//
|
|
|
|
if (!fEnableExtensions) {
|
|
PutStdErr(ERROR_CANNOT_MAKE, NOARGS);
|
|
return FAILURE;
|
|
}
|
|
|
|
//
|
|
// loop over input path and create any needed intermediary directories.
|
|
//
|
|
// Find the point in the string to begin the creation. Note, for UNC
|
|
// names, we must skip the machine and the share
|
|
//
|
|
|
|
if (TempBuffer[1] == COLON) {
|
|
|
|
//
|
|
// Skip D:\
|
|
//
|
|
|
|
lpw = TempBuffer+3;
|
|
} else if (TempBuffer[0] == BSLASH && TempBuffer[1] == BSLASH) {
|
|
|
|
//
|
|
// Skip \\server\share\
|
|
//
|
|
|
|
lpw = TempBuffer+2;
|
|
while (*lpw && *lpw != BSLASH) {
|
|
lpw++;
|
|
}
|
|
if (*lpw) {
|
|
lpw++;
|
|
}
|
|
|
|
while (*lpw && *lpw != BSLASH) {
|
|
lpw++;
|
|
}
|
|
if (*lpw) {
|
|
lpw++;
|
|
}
|
|
} else {
|
|
//
|
|
// For some reason, GetFullPath has given us something we can't understand
|
|
//
|
|
|
|
PutStdErr(ERROR_CANNOT_MAKE, NOARGS);
|
|
return FAILURE;
|
|
}
|
|
|
|
//
|
|
// Walk through the components creating them
|
|
//
|
|
|
|
|
|
while (*lpw) {
|
|
|
|
//
|
|
// Move forward until the next path separator
|
|
//
|
|
|
|
while (*lpw && *lpw != BSLASH) {
|
|
lpw++;
|
|
}
|
|
|
|
//
|
|
// If we've encountered a path character, then attempt to
|
|
// make the given path.
|
|
//
|
|
|
|
if (*lpw == BSLASH) {
|
|
*lpw = NULLC;
|
|
if (!CreateDirectory( TempBuffer, NULL )) {
|
|
Status = GetLastError();
|
|
if (Status != ERROR_ALREADY_EXISTS) {
|
|
PutStdErr( ERROR_CANNOT_MAKE, NOARGS );
|
|
return FAILURE;
|
|
}
|
|
}
|
|
*lpw++ = BSLASH;
|
|
}
|
|
}
|
|
|
|
if (!CreateDirectory( TempBuffer, NULL )) {
|
|
Status = GetLastError( );
|
|
if (Status != ERROR_ALREADY_EXISTS) {
|
|
PutStdErr( Status, NOARGS);
|
|
return FAILURE;
|
|
}
|
|
}
|
|
|
|
return(SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*** eChdir - execute the Chdir command
|
|
*
|
|
* Purpose:
|
|
* If the command is "cd", display the current directory of the current
|
|
* drive.
|
|
*
|
|
* If the command is "cd d:", display the current directory of drive d.
|
|
*
|
|
* If the command is "cd str", change the current directory to str.
|
|
*
|
|
* int eChdir(struct cmdnode *n)
|
|
*
|
|
* Args:
|
|
* n - the parse tree node containing the chdir command
|
|
*
|
|
* Returns:
|
|
* SUCCESS if the requested task was accomplished.
|
|
* FAILURE if it was not.
|
|
*
|
|
*/
|
|
|
|
int eChdir(n)
|
|
struct cmdnode *n ;
|
|
{
|
|
TCHAR *tas, *src, *dst; /* Tokenized arg string */
|
|
TCHAR dirstr[MAX_PATH] ;/* Holds current dir of specified drive */
|
|
|
|
//
|
|
// If extensions are enabled, dont treat spaces as delimeters so it is
|
|
// easier to CHDIR to directory names with embedded spaces without having
|
|
// to quote the directory name
|
|
//
|
|
tas = TokStr(n->argptr, TEXT( "" ), fEnableExtensions ? TS_WSPACE|TS_SDTOKENS : TS_SDTOKENS) ;
|
|
|
|
if (fEnableExtensions) {
|
|
//
|
|
// If extensions were enabled we could have some trailing spaces
|
|
// that need to be nuked since there weren't treated as delimeters
|
|
// by TokStr call above.
|
|
//
|
|
// We compress the extra spaces out since we rely on the tokenized
|
|
// format later inside ChdirWork
|
|
//
|
|
|
|
src = tas;
|
|
dst = tas;
|
|
while (*src) {
|
|
while (*dst = *src++)
|
|
dst += 1;
|
|
|
|
while (_istspace(dst[-1]))
|
|
dst -= 1;
|
|
*dst++ = NULLC;
|
|
}
|
|
|
|
*dst = NULLC;
|
|
}
|
|
|
|
DEBUG((PCGRP, CDLVL, "CHDIR: tas = `%ws'", tas)) ;
|
|
|
|
mystrcpy( tas, StripQuotes( tas ) );
|
|
|
|
//
|
|
// No arguments means display current drive and directory
|
|
//
|
|
|
|
if (*tas == NULLC) {
|
|
GetDir(CurDrvDir, GD_DEFAULT) ;
|
|
cmd_printf(Fmt17, CurDrvDir) ;
|
|
} else
|
|
|
|
|
|
//
|
|
// single drive letter means display current dirctory on that drive
|
|
//
|
|
|
|
if (mystrlen(tas) == 2 && *(tas+1) == COLON && _istalpha(*tas)) {
|
|
GetDir(dirstr, *tas) ;
|
|
cmd_printf(Fmt17, dirstr) ;
|
|
} else
|
|
|
|
//
|
|
// We need to change current directory (and possibly drive)
|
|
//
|
|
|
|
{
|
|
return( LastRetCode = ChdirWork(tas) );
|
|
}
|
|
|
|
return( LastRetCode = SUCCESS );
|
|
}
|
|
|
|
int ChdirWork( TCHAR *tas )
|
|
{
|
|
unsigned i = MSG_BAD_SYNTAX;
|
|
|
|
//
|
|
// If there's no leading "/D", just chdir
|
|
// to the input path
|
|
//
|
|
if (_tcsnicmp( tas, TEXT( "/D" ), 2)) {
|
|
i = ChangeDir((TCHAR *)tas);
|
|
} else {
|
|
//
|
|
// Advance over the "/D" and intervening whitespace
|
|
//
|
|
|
|
tas = SkipWhiteSpace( tas + 2 );
|
|
|
|
//
|
|
// if there's no other switch char, strip any quotes and do
|
|
// the chdir
|
|
//
|
|
|
|
if (*tas != SwitChar) {
|
|
_tcscpy( tas, StripQuotes( tas ));
|
|
i = ChangeDir2(tas, TRUE);
|
|
}
|
|
}
|
|
|
|
if (i != SUCCESS) {
|
|
PutStdErr( i, ONEARG, tas);
|
|
return (FAILURE) ;
|
|
}
|
|
return (SUCCESS) ;
|
|
}
|
|
|
|
#define SIZEOFSTACK 25
|
|
typedef struct {
|
|
PTCHAR SavedDirectory;
|
|
TCHAR NetDriveCreated;
|
|
} *PSAVEDDIRECTORY;
|
|
|
|
PSAVEDDIRECTORY SavedDirectoryStack = NULL;
|
|
|
|
int StrStackDepth = 0;
|
|
int MaxStackDepth = 0;
|
|
|
|
|
|
int GetDirStackDepth(void)
|
|
{
|
|
return StrStackDepth;
|
|
}
|
|
|
|
int
|
|
PushStr ( PTCHAR pszString )
|
|
{
|
|
//
|
|
// If we're full, grow the stack by an increment
|
|
//
|
|
|
|
if (StrStackDepth == MaxStackDepth) {
|
|
PSAVEDDIRECTORY Tmp =
|
|
realloc( SavedDirectoryStack,
|
|
sizeof( *SavedDirectoryStack ) * (MaxStackDepth + SIZEOFSTACK));
|
|
if (Tmp == NULL) {
|
|
return FALSE;
|
|
}
|
|
SavedDirectoryStack = Tmp;
|
|
MaxStackDepth += SIZEOFSTACK;
|
|
}
|
|
|
|
SavedDirectoryStack[ StrStackDepth ].SavedDirectory = pszString;
|
|
SavedDirectoryStack[ StrStackDepth ].NetDriveCreated = NULLC;
|
|
StrStackDepth += 1;
|
|
return TRUE;
|
|
}
|
|
|
|
PTCHAR
|
|
PopStr ()
|
|
{
|
|
|
|
PTCHAR pszString;
|
|
|
|
if (StrStackDepth == 0) {
|
|
return ( NULL );
|
|
}
|
|
StrStackDepth -= 1;
|
|
pszString = SavedDirectoryStack[StrStackDepth].SavedDirectory;
|
|
if (SavedDirectoryStack[StrStackDepth].NetDriveCreated != NULLC) {
|
|
TCHAR szLocalName[4];
|
|
|
|
szLocalName[0] = SavedDirectoryStack[StrStackDepth].NetDriveCreated;
|
|
szLocalName[1] = COLON;
|
|
szLocalName[2] = NULLC;
|
|
SavedDirectoryStack[StrStackDepth].NetDriveCreated = NULLC;
|
|
try {
|
|
WNetCancelConnection2(szLocalName, 0, TRUE);
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
}
|
|
}
|
|
SavedDirectoryStack[StrStackDepth].SavedDirectory = NULL;
|
|
|
|
//
|
|
// If we can eliminate an increment from the stack, go do so
|
|
//
|
|
|
|
if (StrStackDepth > 0 && StrStackDepth + 2 * SIZEOFSTACK <= MaxStackDepth) {
|
|
PSAVEDDIRECTORY Tmp =
|
|
realloc( SavedDirectoryStack,
|
|
sizeof( *SavedDirectoryStack ) * (StrStackDepth + SIZEOFSTACK));
|
|
if (Tmp != NULL) {
|
|
SavedDirectoryStack = Tmp;
|
|
MaxStackDepth = StrStackDepth + SIZEOFSTACK;
|
|
}
|
|
}
|
|
|
|
return ( pszString );
|
|
}
|
|
|
|
VOID
|
|
DumpStrStack() {
|
|
|
|
int i;
|
|
|
|
for (i=StrStackDepth-1; i>=0; i--) {
|
|
cmd_printf( Fmt17, SavedDirectoryStack[i].SavedDirectory );
|
|
}
|
|
return;
|
|
}
|
|
|
|
BOOLEAN
|
|
PushCurDir()
|
|
{
|
|
|
|
PTCHAR pszCurDir;
|
|
|
|
GetDir( CurDrvDir, GD_DEFAULT ) ;
|
|
if ((pszCurDir=HeapAlloc( GetProcessHeap( ), 0, (mystrlen( CurDrvDir )+1)*sizeof( TCHAR ))) != NULL) {
|
|
mystrcpy( pszCurDir, CurDrvDir) ;
|
|
if (PushStr( pszCurDir ))
|
|
return ( TRUE );
|
|
HeapFree( GetProcessHeap( ), 0, pszCurDir );
|
|
}
|
|
return ( FALSE );
|
|
|
|
}
|
|
|
|
int ePushDir(n)
|
|
struct cmdnode *n ;
|
|
{
|
|
TCHAR *tas ; /* Tokenized arg string */
|
|
PTCHAR pszTmp, s;
|
|
|
|
//
|
|
// If extensions are enabled, dont treat spaces as delimeters so it is
|
|
// easier to CHDIR to directory names with embedded spaces without having
|
|
// to quote the directory name
|
|
//
|
|
tas = TokStr(n->argptr, NULL, fEnableExtensions ? TS_WSPACE|TS_NOFLAGS : TS_NOFLAGS) ;
|
|
if (fEnableExtensions) {
|
|
//
|
|
// If extensions were enabled we could have some trailing spaces
|
|
// that need to be nuked since there weren't treated as delimeters
|
|
// by TokStr call above.
|
|
//
|
|
s = lastc(tas);
|
|
while (s > tas) {
|
|
if (_istspace(*s))
|
|
*s-- = NULLC;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
mystrcpy(tas, StripQuotes(tas) );
|
|
|
|
LastRetCode = SUCCESS;
|
|
if (*tas == NULLC) {
|
|
|
|
//
|
|
// Print out entire stack
|
|
//
|
|
DumpStrStack();
|
|
|
|
} else if (PushCurDir()) {
|
|
|
|
//
|
|
// If extensions are enabled and a UNC name was given, then do
|
|
// a temporary NET USE to define a drive letter that we can
|
|
// use to change drive/directory to. The matching POPD will
|
|
// delete the temporary drive letter.
|
|
//
|
|
|
|
if (fEnableExtensions && tas[0] == BSLASH && tas[1] == BSLASH) {
|
|
NETRESOURCE netResource;
|
|
TCHAR szLocalName[4];
|
|
PTCHAR s;
|
|
|
|
//
|
|
// If there is a directory specified after the \\server\share
|
|
// then test to see if that directory exists before doing any
|
|
// network connections
|
|
//
|
|
|
|
if ((s = _tcschr(&tas[2], BSLASH)) != NULL
|
|
&& (s = _tcschr(s+1, BSLASH)) != NULL) {
|
|
|
|
if (GetFileAttributes( tas ) == -1) {
|
|
LastRetCode = GetLastError( );
|
|
if (LastRetCode == ERROR_FILE_NOT_FOUND) {
|
|
LastRetCode = ERROR_PATH_NOT_FOUND;
|
|
}
|
|
} else {
|
|
*s++ = NULLC;
|
|
}
|
|
}
|
|
|
|
szLocalName[0] = TEXT('Z');
|
|
szLocalName[1] = COLON;
|
|
szLocalName[2] = NULLC;
|
|
netResource.dwType = RESOURCETYPE_DISK;
|
|
netResource.lpLocalName = szLocalName;
|
|
netResource.lpRemoteName = tas;
|
|
netResource.lpProvider = NULL;
|
|
|
|
while (LastRetCode == NO_ERROR && szLocalName[0] != TEXT('A')) {
|
|
|
|
try {
|
|
LastRetCode = WNetAddConnection2( &netResource, NULL, NULL, 0);
|
|
} except (LastRetCode = GetExceptionCode( ), EXCEPTION_EXECUTE_HANDLER) {
|
|
}
|
|
|
|
switch (LastRetCode) {
|
|
case NO_ERROR:
|
|
SavedDirectoryStack[StrStackDepth-1].NetDriveCreated = szLocalName[0];
|
|
tas[0] = szLocalName[0];
|
|
tas[1] = szLocalName[1];
|
|
tas[2] = BSLASH;
|
|
if (s != NULL)
|
|
_tcscpy(&tas[3], s);
|
|
else
|
|
tas[3] = NULLC;
|
|
goto godrive;
|
|
case ERROR_ALREADY_ASSIGNED:
|
|
case ERROR_DEVICE_ALREADY_REMEMBERED:
|
|
szLocalName[0] = (TCHAR)((UCHAR)szLocalName[0] - 1);
|
|
LastRetCode = NO_ERROR;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
godrive: ;
|
|
}
|
|
|
|
//
|
|
// The NET USE succeeded, now attempt to change the directory
|
|
// as well.
|
|
//
|
|
|
|
if (LastRetCode == NO_ERROR
|
|
&& (LastRetCode = ChangeDir2( tas, TRUE )) == SUCCESS) {
|
|
if (tas[1] == ':') {
|
|
GetDir(CurDrvDir,tas[0]);
|
|
}
|
|
}
|
|
|
|
if (LastRetCode != SUCCESS) {
|
|
pszTmp = PopStr();
|
|
HeapFree(GetProcessHeap(), 0, pszTmp);
|
|
PutStdErr( LastRetCode, NOARGS );
|
|
LastRetCode = FAILURE;
|
|
}
|
|
} else {
|
|
PutStdErr( MSG_ERROR_PUSHD_DEPTH_EXCEEDED, NOARGS );
|
|
LastRetCode = FAILURE;
|
|
}
|
|
|
|
return ( LastRetCode );
|
|
}
|
|
|
|
int ePopDir(n)
|
|
struct cmdnode *n ;
|
|
{
|
|
|
|
PTCHAR pszCurDir;
|
|
|
|
UNREFERENCED_PARAMETER( n );
|
|
if (pszCurDir = PopStr()) {
|
|
if (ChangeDir2( pszCurDir,TRUE ) == SUCCESS) {
|
|
HeapFree(GetProcessHeap(), 0, pszCurDir);
|
|
return( SUCCESS );
|
|
}
|
|
HeapFree(GetProcessHeap(), 0, pszCurDir);
|
|
}
|
|
return( FAILURE );
|
|
}
|
|
|
|
|
|
/*** eRmdir - begin the execution of the Rmdir command
|
|
*
|
|
* Purpose:
|
|
* To remove an arbitrary number of directories.
|
|
*
|
|
* int eRmdir(struct cmdnode *n)
|
|
*
|
|
* Args:
|
|
* n - the parse tree node containing the rmdir command
|
|
*
|
|
* Returns:
|
|
* SUCCESS if all directories were removed.
|
|
* FAILURE if they weren't.
|
|
*
|
|
*/
|
|
|
|
int eRmdir(n)
|
|
struct cmdnode *n ;
|
|
{
|
|
DEBUG((PCGRP, RDLVL, "RMDIR: Entered.")) ;
|
|
return(RdWork(n->argptr)); // in del.c
|
|
}
|