Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

662 lines
18 KiB

  1. /*++
  2. Copyright (c) 1988-1999 Microsoft Corporation
  3. Module Name:
  4. cpath.c
  5. Abstract:
  6. Path-related commands
  7. --*/
  8. #include "cmd.h"
  9. extern TCHAR SwitChar, PathChar;
  10. extern TCHAR Fmt17[] ;
  11. extern TCHAR CurDrvDir[] ;
  12. extern int LastRetCode ; /* @@ */
  13. extern TCHAR TmpBuf[] ;
  14. /**************** START OF SPECIFICATIONS ***********************/
  15. /* */
  16. /* SUBROUTINE NAME: eMkDir */
  17. /* */
  18. /* DESCRIPTIVE NAME: Begin execution of the MKDIR command */
  19. /* */
  20. /* FUNCTION: This routine will make any number of directories, */
  21. /* and will continue if it encounters a bad argument. */
  22. /* eMkDir will be called if the user enters MD or */
  23. /* MKDIR on the command line. */
  24. /* */
  25. /* NOTES: */
  26. /* */
  27. /* ENTRY POINT: eMkDir */
  28. /* LINKAGE: Near */
  29. /* */
  30. /* INPUT: n - parse tree node containing the MKDIR command */
  31. /* */
  32. /* EXIT-NORMAL: returns SUCCESS if all directories were */
  33. /* successfully created. */
  34. /* */
  35. /* EXIT-ERROR: returns FAILURE otherwise */
  36. /* */
  37. /* EFFECTS: None. */
  38. /* */
  39. /* INTERNAL REFERENCES: */
  40. /* ROUTINES: */
  41. /* LoopThroughArgs - break up command line, call MdWork */
  42. /* */
  43. /* EXTERNAL REFERENCES: */
  44. /* ROUTINES: */
  45. /* */
  46. /**************** END OF SPECIFICATIONS *************************/
  47. int eMkdir(n)
  48. struct cmdnode *n ;
  49. {
  50. DEBUG((PCGRP, MDLVL, "MKDIR: Entered.")) ;
  51. return(LastRetCode = LoopThroughArgs(n->argptr, MdWork, LTA_CONT)) ;
  52. }
  53. /**************** START OF SPECIFICATIONS ***********************/
  54. /* */
  55. /* SUBROUTINE NAME: MdWork */
  56. /* */
  57. /* DESCRIPTIVE NAME: Make a directory */
  58. /* */
  59. /* FUNCTION: MdWork creates a new directory. */
  60. /* */
  61. /* NOTES: */
  62. /* */
  63. /* ENTRY POINT: MdWork */
  64. /* LINKAGE: Near */
  65. /* */
  66. /* INPUT: arg - a pointer to a NULL terminated string of the */
  67. /* new directory to create. */
  68. /* */
  69. /* EXIT-NORMAL: returns SUCCESS if the directory is made */
  70. /* successfully */
  71. /* */
  72. /* EXIT-ERROR: returns FAILURE otherwise */
  73. /* */
  74. /* EFFECTS: None. */
  75. /* */
  76. /* INTERNAL REFERENCES: */
  77. /* ROUTINES: */
  78. /* PutStdErr - Writes to standard error */
  79. /* */
  80. /* EXTERNAL REFERENCES: */
  81. /* ROUTINES: */
  82. /* DOSMKDIR */
  83. /* */
  84. /**************** END OF SPECIFICATIONS *************************/
  85. int MdWork(arg)
  86. TCHAR *arg ;
  87. {
  88. ULONG Status;
  89. TCHAR *lpw;
  90. TCHAR TempBuffer[MAX_PATH];
  91. DWORD Length;
  92. /* Check if drive is valid because Dosmkdir does not
  93. return invalid drive @@5 */
  94. if ((arg[1] == COLON) && !IsValidDrv(*arg)) {
  95. PutStdErr(ERROR_INVALID_DRIVE, NOARGS);
  96. return(FAILURE) ;
  97. }
  98. Length = GetFullPathName(arg, MAX_PATH, TempBuffer, &lpw);
  99. if (Length == 0) {
  100. PutStdErr( GetLastError(), NOARGS);
  101. return FAILURE;
  102. }
  103. if (Length >= MAX_PATH) {
  104. PutStdErr( MSG_FULL_PATH_TOO_LONG, ONEARG, arg );
  105. return FAILURE;
  106. }
  107. if (CreateDirectory( arg, NULL )) {
  108. return SUCCESS;
  109. }
  110. Status = GetLastError();
  111. if (Status == ERROR_ALREADY_EXISTS) {
  112. PutStdErr( MSG_DIR_EXISTS, ONEARG, arg );
  113. return FAILURE;
  114. } else if (Status != ERROR_PATH_NOT_FOUND) {
  115. PutStdErr( Status, NOARGS);
  116. return FAILURE;
  117. }
  118. //
  119. // If no extensions, then simply fail.
  120. //
  121. if (!fEnableExtensions) {
  122. PutStdErr(ERROR_CANNOT_MAKE, NOARGS);
  123. return FAILURE;
  124. }
  125. //
  126. // loop over input path and create any needed intermediary directories.
  127. //
  128. // Find the point in the string to begin the creation. Note, for UNC
  129. // names, we must skip the machine and the share
  130. //
  131. if (TempBuffer[1] == COLON) {
  132. //
  133. // Skip D:\
  134. //
  135. lpw = TempBuffer+3;
  136. } else if (TempBuffer[0] == BSLASH && TempBuffer[1] == BSLASH) {
  137. //
  138. // Skip \\server\share\
  139. //
  140. lpw = TempBuffer+2;
  141. while (*lpw && *lpw != BSLASH) {
  142. lpw++;
  143. }
  144. if (*lpw) {
  145. lpw++;
  146. }
  147. while (*lpw && *lpw != BSLASH) {
  148. lpw++;
  149. }
  150. if (*lpw) {
  151. lpw++;
  152. }
  153. } else {
  154. //
  155. // For some reason, GetFullPath has given us something we can't understand
  156. //
  157. PutStdErr(ERROR_CANNOT_MAKE, NOARGS);
  158. return FAILURE;
  159. }
  160. //
  161. // Walk through the components creating them
  162. //
  163. while (*lpw) {
  164. //
  165. // Move forward until the next path separator
  166. //
  167. while (*lpw && *lpw != BSLASH) {
  168. lpw++;
  169. }
  170. //
  171. // If we've encountered a path character, then attempt to
  172. // make the given path.
  173. //
  174. if (*lpw == BSLASH) {
  175. *lpw = NULLC;
  176. if (!CreateDirectory( TempBuffer, NULL )) {
  177. Status = GetLastError();
  178. if (Status != ERROR_ALREADY_EXISTS) {
  179. PutStdErr( ERROR_CANNOT_MAKE, NOARGS );
  180. return FAILURE;
  181. }
  182. }
  183. *lpw++ = BSLASH;
  184. }
  185. }
  186. if (!CreateDirectory( TempBuffer, NULL )) {
  187. Status = GetLastError( );
  188. if (Status != ERROR_ALREADY_EXISTS) {
  189. PutStdErr( Status, NOARGS);
  190. return FAILURE;
  191. }
  192. }
  193. return(SUCCESS);
  194. }
  195. /*** eChdir - execute the Chdir command
  196. *
  197. * Purpose:
  198. * If the command is "cd", display the current directory of the current
  199. * drive.
  200. *
  201. * If the command is "cd d:", display the current directory of drive d.
  202. *
  203. * If the command is "cd str", change the current directory to str.
  204. *
  205. * int eChdir(struct cmdnode *n)
  206. *
  207. * Args:
  208. * n - the parse tree node containing the chdir command
  209. *
  210. * Returns:
  211. * SUCCESS if the requested task was accomplished.
  212. * FAILURE if it was not.
  213. *
  214. */
  215. int eChdir(n)
  216. struct cmdnode *n ;
  217. {
  218. TCHAR *tas, *src, *dst; /* Tokenized arg string */
  219. TCHAR dirstr[MAX_PATH] ;/* Holds current dir of specified drive */
  220. //
  221. // If extensions are enabled, dont treat spaces as delimeters so it is
  222. // easier to CHDIR to directory names with embedded spaces without having
  223. // to quote the directory name
  224. //
  225. tas = TokStr(n->argptr, TEXT( "" ), fEnableExtensions ? TS_WSPACE|TS_SDTOKENS : TS_SDTOKENS) ;
  226. if (fEnableExtensions) {
  227. //
  228. // If extensions were enabled we could have some trailing spaces
  229. // that need to be nuked since there weren't treated as delimeters
  230. // by TokStr call above.
  231. //
  232. // We compress the extra spaces out since we rely on the tokenized
  233. // format later inside ChdirWork
  234. //
  235. src = tas;
  236. dst = tas;
  237. while (*src) {
  238. while (*dst = *src++)
  239. dst += 1;
  240. while (_istspace(dst[-1]))
  241. dst -= 1;
  242. *dst++ = NULLC;
  243. }
  244. *dst = NULLC;
  245. }
  246. DEBUG((PCGRP, CDLVL, "CHDIR: tas = `%ws'", tas)) ;
  247. mystrcpy( tas, StripQuotes( tas ) );
  248. //
  249. // No arguments means display current drive and directory
  250. //
  251. if (*tas == NULLC) {
  252. GetDir(CurDrvDir, GD_DEFAULT) ;
  253. cmd_printf(Fmt17, CurDrvDir) ;
  254. } else
  255. //
  256. // single drive letter means display current dirctory on that drive
  257. //
  258. if (mystrlen(tas) == 2 && *(tas+1) == COLON && _istalpha(*tas)) {
  259. GetDir(dirstr, *tas) ;
  260. cmd_printf(Fmt17, dirstr) ;
  261. } else
  262. //
  263. // We need to change current directory (and possibly drive)
  264. //
  265. {
  266. return( LastRetCode = ChdirWork(tas) );
  267. }
  268. return( LastRetCode = SUCCESS );
  269. }
  270. int ChdirWork( TCHAR *tas )
  271. {
  272. unsigned i = MSG_BAD_SYNTAX;
  273. //
  274. // If there's no leading "/D", just chdir
  275. // to the input path
  276. //
  277. if (_tcsnicmp( tas, TEXT( "/D" ), 2)) {
  278. i = ChangeDir((TCHAR *)tas);
  279. } else {
  280. //
  281. // Advance over the "/D" and intervening whitespace
  282. //
  283. tas = SkipWhiteSpace( tas + 2 );
  284. //
  285. // if there's no other switch char, strip any quotes and do
  286. // the chdir
  287. //
  288. if (*tas != SwitChar) {
  289. _tcscpy( tas, StripQuotes( tas ));
  290. i = ChangeDir2(tas, TRUE);
  291. }
  292. }
  293. if (i != SUCCESS) {
  294. PutStdErr( i, ONEARG, tas);
  295. return (FAILURE) ;
  296. }
  297. return (SUCCESS) ;
  298. }
  299. #define SIZEOFSTACK 25
  300. typedef struct {
  301. PTCHAR SavedDirectory;
  302. TCHAR NetDriveCreated;
  303. } *PSAVEDDIRECTORY;
  304. PSAVEDDIRECTORY SavedDirectoryStack = NULL;
  305. int StrStackDepth = 0;
  306. int MaxStackDepth = 0;
  307. int GetDirStackDepth(void)
  308. {
  309. return StrStackDepth;
  310. }
  311. int
  312. PushStr ( PTCHAR pszString )
  313. {
  314. //
  315. // If we're full, grow the stack by an increment
  316. //
  317. if (StrStackDepth == MaxStackDepth) {
  318. PSAVEDDIRECTORY Tmp =
  319. realloc( SavedDirectoryStack,
  320. sizeof( *SavedDirectoryStack ) * (MaxStackDepth + SIZEOFSTACK));
  321. if (Tmp == NULL) {
  322. return FALSE;
  323. }
  324. SavedDirectoryStack = Tmp;
  325. MaxStackDepth += SIZEOFSTACK;
  326. }
  327. SavedDirectoryStack[ StrStackDepth ].SavedDirectory = pszString;
  328. SavedDirectoryStack[ StrStackDepth ].NetDriveCreated = NULLC;
  329. StrStackDepth += 1;
  330. return TRUE;
  331. }
  332. PTCHAR
  333. PopStr ()
  334. {
  335. PTCHAR pszString;
  336. if (StrStackDepth == 0) {
  337. return ( NULL );
  338. }
  339. StrStackDepth -= 1;
  340. pszString = SavedDirectoryStack[StrStackDepth].SavedDirectory;
  341. if (SavedDirectoryStack[StrStackDepth].NetDriveCreated != NULLC) {
  342. TCHAR szLocalName[4];
  343. szLocalName[0] = SavedDirectoryStack[StrStackDepth].NetDriveCreated;
  344. szLocalName[1] = COLON;
  345. szLocalName[2] = NULLC;
  346. SavedDirectoryStack[StrStackDepth].NetDriveCreated = NULLC;
  347. try {
  348. WNetCancelConnection2(szLocalName, 0, TRUE);
  349. } except (EXCEPTION_EXECUTE_HANDLER) {
  350. }
  351. }
  352. SavedDirectoryStack[StrStackDepth].SavedDirectory = NULL;
  353. //
  354. // If we can eliminate an increment from the stack, go do so
  355. //
  356. if (StrStackDepth > 0 && StrStackDepth + 2 * SIZEOFSTACK <= MaxStackDepth) {
  357. PSAVEDDIRECTORY Tmp =
  358. realloc( SavedDirectoryStack,
  359. sizeof( *SavedDirectoryStack ) * (StrStackDepth + SIZEOFSTACK));
  360. if (Tmp != NULL) {
  361. SavedDirectoryStack = Tmp;
  362. MaxStackDepth = StrStackDepth + SIZEOFSTACK;
  363. }
  364. }
  365. return ( pszString );
  366. }
  367. VOID
  368. DumpStrStack() {
  369. int i;
  370. for (i=StrStackDepth-1; i>=0; i--) {
  371. cmd_printf( Fmt17, SavedDirectoryStack[i].SavedDirectory );
  372. }
  373. return;
  374. }
  375. BOOLEAN
  376. PushCurDir()
  377. {
  378. PTCHAR pszCurDir;
  379. GetDir( CurDrvDir, GD_DEFAULT ) ;
  380. if ((pszCurDir=HeapAlloc( GetProcessHeap( ), 0, (mystrlen( CurDrvDir )+1)*sizeof( TCHAR ))) != NULL) {
  381. mystrcpy( pszCurDir, CurDrvDir) ;
  382. if (PushStr( pszCurDir ))
  383. return ( TRUE );
  384. HeapFree( GetProcessHeap( ), 0, pszCurDir );
  385. }
  386. return ( FALSE );
  387. }
  388. int ePushDir(n)
  389. struct cmdnode *n ;
  390. {
  391. TCHAR *tas ; /* Tokenized arg string */
  392. PTCHAR pszTmp, s;
  393. //
  394. // If extensions are enabled, dont treat spaces as delimeters so it is
  395. // easier to CHDIR to directory names with embedded spaces without having
  396. // to quote the directory name
  397. //
  398. tas = TokStr(n->argptr, NULL, fEnableExtensions ? TS_WSPACE|TS_NOFLAGS : TS_NOFLAGS) ;
  399. if (fEnableExtensions) {
  400. //
  401. // If extensions were enabled we could have some trailing spaces
  402. // that need to be nuked since there weren't treated as delimeters
  403. // by TokStr call above.
  404. //
  405. s = lastc(tas);
  406. while (s > tas) {
  407. if (_istspace(*s))
  408. *s-- = NULLC;
  409. else
  410. break;
  411. }
  412. }
  413. mystrcpy(tas, StripQuotes(tas) );
  414. LastRetCode = SUCCESS;
  415. if (*tas == NULLC) {
  416. //
  417. // Print out entire stack
  418. //
  419. DumpStrStack();
  420. } else if (PushCurDir()) {
  421. //
  422. // If extensions are enabled and a UNC name was given, then do
  423. // a temporary NET USE to define a drive letter that we can
  424. // use to change drive/directory to. The matching POPD will
  425. // delete the temporary drive letter.
  426. //
  427. if (fEnableExtensions && tas[0] == BSLASH && tas[1] == BSLASH) {
  428. NETRESOURCE netResource;
  429. TCHAR szLocalName[4];
  430. PTCHAR s;
  431. //
  432. // If there is a directory specified after the \\server\share
  433. // then test to see if that directory exists before doing any
  434. // network connections
  435. //
  436. if ((s = _tcschr(&tas[2], BSLASH)) != NULL
  437. && (s = _tcschr(s+1, BSLASH)) != NULL) {
  438. if (GetFileAttributes( tas ) == -1) {
  439. LastRetCode = GetLastError( );
  440. if (LastRetCode == ERROR_FILE_NOT_FOUND) {
  441. LastRetCode = ERROR_PATH_NOT_FOUND;
  442. }
  443. } else {
  444. *s++ = NULLC;
  445. }
  446. }
  447. szLocalName[0] = TEXT('Z');
  448. szLocalName[1] = COLON;
  449. szLocalName[2] = NULLC;
  450. netResource.dwType = RESOURCETYPE_DISK;
  451. netResource.lpLocalName = szLocalName;
  452. netResource.lpRemoteName = tas;
  453. netResource.lpProvider = NULL;
  454. while (LastRetCode == NO_ERROR && szLocalName[0] != TEXT('A')) {
  455. try {
  456. LastRetCode = WNetAddConnection2( &netResource, NULL, NULL, 0);
  457. } except (LastRetCode = GetExceptionCode( ), EXCEPTION_EXECUTE_HANDLER) {
  458. }
  459. switch (LastRetCode) {
  460. case NO_ERROR:
  461. SavedDirectoryStack[StrStackDepth-1].NetDriveCreated = szLocalName[0];
  462. tas[0] = szLocalName[0];
  463. tas[1] = szLocalName[1];
  464. tas[2] = BSLASH;
  465. if (s != NULL)
  466. _tcscpy(&tas[3], s);
  467. else
  468. tas[3] = NULLC;
  469. goto godrive;
  470. case ERROR_ALREADY_ASSIGNED:
  471. case ERROR_DEVICE_ALREADY_REMEMBERED:
  472. szLocalName[0] = (TCHAR)((UCHAR)szLocalName[0] - 1);
  473. LastRetCode = NO_ERROR;
  474. break;
  475. default:
  476. break;
  477. }
  478. }
  479. godrive: ;
  480. }
  481. //
  482. // The NET USE succeeded, now attempt to change the directory
  483. // as well.
  484. //
  485. if (LastRetCode == NO_ERROR
  486. && (LastRetCode = ChangeDir2( tas, TRUE )) == SUCCESS) {
  487. if (tas[1] == ':') {
  488. GetDir(CurDrvDir,tas[0]);
  489. }
  490. }
  491. if (LastRetCode != SUCCESS) {
  492. pszTmp = PopStr();
  493. HeapFree(GetProcessHeap(), 0, pszTmp);
  494. PutStdErr( LastRetCode, NOARGS );
  495. LastRetCode = FAILURE;
  496. }
  497. } else {
  498. PutStdErr( MSG_ERROR_PUSHD_DEPTH_EXCEEDED, NOARGS );
  499. LastRetCode = FAILURE;
  500. }
  501. return ( LastRetCode );
  502. }
  503. int ePopDir(n)
  504. struct cmdnode *n ;
  505. {
  506. PTCHAR pszCurDir;
  507. UNREFERENCED_PARAMETER( n );
  508. if (pszCurDir = PopStr()) {
  509. if (ChangeDir2( pszCurDir,TRUE ) == SUCCESS) {
  510. HeapFree(GetProcessHeap(), 0, pszCurDir);
  511. return( SUCCESS );
  512. }
  513. HeapFree(GetProcessHeap(), 0, pszCurDir);
  514. }
  515. return( FAILURE );
  516. }
  517. /*** eRmdir - begin the execution of the Rmdir command
  518. *
  519. * Purpose:
  520. * To remove an arbitrary number of directories.
  521. *
  522. * int eRmdir(struct cmdnode *n)
  523. *
  524. * Args:
  525. * n - the parse tree node containing the rmdir command
  526. *
  527. * Returns:
  528. * SUCCESS if all directories were removed.
  529. * FAILURE if they weren't.
  530. *
  531. */
  532. int eRmdir(n)
  533. struct cmdnode *n ;
  534. {
  535. DEBUG((PCGRP, RDLVL, "RMDIR: Entered.")) ;
  536. return(RdWork(n->argptr)); // in del.c
  537. }