1 /* 2 * INTERNAL.C - command.com internal commands. 3 * 4 * 5 * History: 6 * 7 * 17/08/94 (Tim Norman) 8 * started. 9 * 10 * 08/08/95 (Matt Rains) 11 * i have cleaned up the source code. changes now bring this source into 12 * guidelines for recommended programming practice. 13 * 14 * cd() 15 * started. 16 * 17 * dir() 18 * i have added support for file attributes to the DIR() function. the 19 * routine adds "d" (directory) and "r" (read only) output. files with the 20 * system attribute have the filename converted to lowercase. files with 21 * the hidden attribute are not displayed. 22 * 23 * i have added support for directorys. now if the directory attribute is 24 * detected the file size if replaced with the string "<dir>". 25 * 26 * ver() 27 * started. 28 * 29 * md() 30 * started. 31 * 32 * rd() 33 * started. 34 * 35 * del() 36 * started. 37 * 38 * does not support wildcard selection. 39 * 40 * todo: add delete directory support. 41 * add recursive directory delete support. 42 * 43 * ren() 44 * started. 45 * 46 * does not support wildcard selection. 47 * 48 * todo: add rename directory support. 49 * 50 * a general structure has been used for the cd, rd and md commands. this 51 * will be better in the long run. it is too hard to maintain such diverse 52 * functions when you are involved in a group project like this. 53 * 54 * 12/14/95 (Tim Norman) 55 * fixed DIR so that it will stick \*.* if a directory is specified and 56 * that it will stick on .* if a file with no extension is specified or 57 * *.* if it ends in a \ 58 * 59 * 1/6/96 (Tim Norman) 60 * added an isatty call to DIR so it won't prompt for keypresses unless 61 * stdin and stdout are the console. 62 * 63 * changed parameters to be mutually consistent to make calling the 64 * functions easier 65 * 66 * rem() 67 * started. 68 * 69 * doskey() 70 * started. 71 * 72 * 01/22/96 (Oliver Mueller) 73 * error messages are now handled by perror. 74 * 75 * 02/05/96 (Tim Norman) 76 * converted all functions to accept first/rest parameters 77 * 78 * 07/26/96 (Tim Norman) 79 * changed return values to int instead of void 80 * 81 * path() started. 82 * 83 * 12/23/96 (Aaron Kaufman) 84 * rewrote dir() to mimic MS-DOS's dir 85 * 86 * 01/28/97 (Tim Norman) 87 * cleaned up Aaron's DIR code 88 * 89 * 06/13/97 (Tim Norman) 90 * moved DIR code to dir.c 91 * re-implemented Aaron's DIR code 92 * 93 * 06/14/97 (Steffan Kaiser) 94 * ctrl-break handling 95 * bug fixes 96 * 97 * 27-Jul-1998 (John P Price <linux-guru@gcfl.net>) 98 * added config.h include 99 * 100 * 03-Dec-1998 (Eric Kohl) 101 * Replaced DOS calls by Win32 calls. 102 * 103 * 08-Dec-1998 (Eric Kohl) 104 * Added help texts ("/?"). 105 * 106 * 18-Dec-1998 (Eric Kohl) 107 * Added support for quoted arguments (cd "program files"). 108 * 109 * 07-Jan-1999 (Eric Kohl) 110 * Clean up. 111 * 112 * 26-Jan-1999 (Eric Kohl) 113 * Replaced remaining CRT io functions by Win32 io functions. 114 * Unicode safe! 115 * 116 * 30-Jan-1999 (Eric Kohl) 117 * Added "cd -" feature. Changes to the previous directory. 118 * 119 * 15-Mar-1999 (Eric Kohl) 120 * Fixed bug in "cd -" feature. If the previous directory was a root 121 * directory, it was ignored. 122 * 123 * 23-Feb-2001 (Carl Nettelblad <cnettel@hem.passagen.se>) 124 * Improved chdir/cd command. 125 * 126 * 02-Apr-2004 (Magnus Olsen <magnus@greatlord.com>) 127 * Remove all hard code string so they can be 128 * translate to other langues. 129 * 130 * 19-Jul-2005 (Brandon Turner <turnerb7@msu.edu>) 131 * Rewrite the CD, it working as Windows 2000 CMD 132 * 133 * 19-Jul-2005 (Magnus Olsen <magnus@greatlord.com>) 134 * Add SetRootPath and GetRootPath 135 * 136 * 14-Jul-2007 (Pierre Schweitzer <heis_spiter@hotmail.com>) 137 * Added commands help display to help command (ex. : "help cmd") 138 */ 139 140 #include "precomp.h" 141 142 #ifdef INCLUDE_CMD_CHDIR 143 144 /* helper functions for getting current path from drive 145 without changing drive. Return code 0 = ok, 1 = fail. 146 INT GetRootPath("C:",outbuffer,chater size of outbuffer); 147 the first param can have any size, if the the two frist 148 letter are not a drive with : it will get Currentpath on 149 current drive exactly as GetCurrentDirectory does. 150 */ 151 152 INT GetRootPath(TCHAR *InPath,TCHAR *OutPath,INT size) 153 { 154 if (InPath[0] && InPath[1] == _T(':')) 155 { 156 INT t=0; 157 158 if ((InPath[0] >= _T('0')) && (InPath[0] <= _T('9'))) 159 { 160 t = (InPath[0] - _T('0')) +28; 161 } 162 163 if ((InPath[0] >= _T('a')) && (InPath[0] <= _T('z'))) 164 { 165 t = (InPath[0] - _T('a')) +1; 166 InPath[0] = t + _T('A') - 1; 167 } 168 169 if ((InPath[0] >= _T('A')) && (InPath[0] <= _T('Z'))) 170 { 171 t = (InPath[0] - _T('A')) +1; 172 } 173 174 return _tgetdcwd(t,OutPath,size) == NULL; 175 } 176 177 /* Get current directory */ 178 return !GetCurrentDirectory(size,OutPath); 179 } 180 181 182 BOOL SetRootPath(TCHAR *oldpath, TCHAR *InPath) 183 { 184 TCHAR OutPath[MAX_PATH]; 185 TCHAR OutPathTemp[MAX_PATH]; 186 187 StripQuotes(InPath); 188 189 /* Retrieve the full path name from the (possibly relative) InPath */ 190 if (GetFullPathName(InPath, MAX_PATH, OutPathTemp, NULL) == 0) 191 goto Fail; 192 193 /* Convert the full path to its correct case. 194 * Example: c:\windows\SYSTEM32 => C:\WINDOWS\System32 */ 195 GetPathCase(OutPathTemp, OutPath); 196 197 /* Use _tchdir, since unlike SetCurrentDirectory it updates 198 * the current-directory-on-drive environment variables. */ 199 if (_tchdir(OutPath) != 0) 200 goto Fail; 201 202 /* Keep original drive in ordinary CD/CHDIR (without /D switch). */ 203 if (oldpath != NULL && _tcsncicmp(OutPath, oldpath, 2) != 0) 204 SetCurrentDirectory(oldpath); 205 206 return TRUE; 207 208 Fail: 209 ConErrFormatMessage(GetLastError()); 210 nErrorLevel = 1; 211 return FALSE; 212 } 213 214 215 /* 216 * CD / CHDIR 217 * 218 */ 219 INT cmd_chdir (LPTSTR param) 220 { 221 BOOL bChangeDrive = FALSE; 222 LPTSTR tmp; 223 TCHAR szCurrent[MAX_PATH]; 224 225 /* Filter out special cases first */ 226 227 /* Print Help */ 228 if (!_tcsncmp(param, _T("/?"), 2)) 229 { 230 ConOutResPaging(TRUE,STRING_CD_HELP); 231 return 0; 232 } 233 234 /* Remove extra quotes and strip trailing whitespace */ 235 StripQuotes(param); 236 tmp = param + _tcslen(param) - 1; 237 while (tmp > param && _istspace(*tmp)) 238 tmp--; 239 *(tmp + 1) = _T('\0'); 240 241 /* Set Error Level to Success */ 242 nErrorLevel = 0; 243 244 /* Print Current Directory on a disk */ 245 if (_tcslen(param) == 2 && param[1] == _T(':')) 246 { 247 if (GetRootPath(param, szCurrent, MAX_PATH)) 248 { 249 error_invalid_drive(); 250 return 1; 251 } 252 ConOutPrintf(_T("%s\n"), szCurrent); 253 return 0; 254 } 255 256 /* Get Current Directory */ 257 GetCurrentDirectory(MAX_PATH, szCurrent); 258 if (param[0] == _T('\0')) 259 { 260 ConOutPrintf(_T("%s\n"), szCurrent); 261 return 0; 262 } 263 264 /* Input String Contains /D Switch */ 265 if (!_tcsncicmp(param, _T("/D"), 2)) 266 { 267 bChangeDrive = TRUE; 268 param += 2; 269 while (_istspace(*param)) 270 param++; 271 } 272 273 if (!SetRootPath(bChangeDrive ? NULL : szCurrent, param)) 274 { 275 nErrorLevel = 1; 276 return 1; 277 } 278 279 return 0; 280 } 281 282 #endif 283 284 #ifdef INCLUDE_CMD_MKDIR 285 286 /* Helper function for mkdir to make directories in a path. 287 Don't use the api to decrease dependence on libs */ 288 BOOL 289 MakeFullPath(TCHAR * DirPath) 290 { 291 TCHAR path[MAX_PATH]; 292 TCHAR *p = DirPath; 293 INT_PTR n; 294 295 if (CreateDirectory(DirPath, NULL)) 296 return TRUE; 297 else if (GetLastError() != ERROR_PATH_NOT_FOUND) 298 return FALSE; 299 300 /* got ERROR_PATH_NOT_FOUND, so try building it up one component at a time */ 301 if (p[0] && p[1] == _T(':')) 302 p += 2; 303 while (*p == _T('\\')) 304 p++; /* skip drive root */ 305 do 306 { 307 p = _tcschr(p, _T('\\')); 308 n = p ? p++ - DirPath : _tcslen(DirPath); 309 _tcsncpy(path, DirPath, n); 310 path[n] = _T('\0'); 311 if ( !CreateDirectory(path, NULL) && 312 (GetLastError() != ERROR_ALREADY_EXISTS)) 313 { 314 return FALSE; 315 } 316 } while (p != NULL); 317 318 return TRUE; 319 } 320 321 /* 322 * MD / MKDIR 323 */ 324 INT cmd_mkdir (LPTSTR param) 325 { 326 LPTSTR *p; 327 INT argc, i; 328 if (!_tcsncmp (param, _T("/?"), 2)) 329 { 330 ConOutResPaging(TRUE,STRING_MKDIR_HELP); 331 return 0; 332 } 333 334 p = split (param, &argc, FALSE, FALSE); 335 if (argc == 0) 336 { 337 ConErrResPuts(STRING_ERROR_REQ_PARAM_MISSING); 338 freep(p); 339 nErrorLevel = 1; 340 return 1; 341 } 342 343 nErrorLevel = 0; 344 for (i = 0; i < argc; i++) 345 { 346 if (!MakeFullPath(p[i])) 347 { 348 if (GetLastError() == ERROR_PATH_NOT_FOUND) 349 { 350 ConErrResPuts(STRING_MD_ERROR2); 351 } 352 else 353 { 354 ErrorMessage (GetLastError(), _T("MD")); 355 } 356 nErrorLevel = 1; 357 } 358 } 359 360 freep (p); 361 return nErrorLevel; 362 } 363 #endif 364 365 366 #ifdef INCLUDE_CMD_RMDIR 367 /* 368 * RD / RMDIR 369 */ 370 BOOL DeleteFolder(LPTSTR FileName) 371 { 372 TCHAR Base[MAX_PATH]; 373 TCHAR TempFileName[MAX_PATH]; 374 HANDLE hFile; 375 WIN32_FIND_DATA f; 376 _tcscpy(Base,FileName); 377 _tcscat(Base,_T("\\*")); 378 hFile = FindFirstFile(Base, &f); 379 Base[_tcslen(Base) - 1] = _T('\0'); 380 if (hFile != INVALID_HANDLE_VALUE) 381 { 382 do 383 { 384 if (!_tcscmp(f.cFileName, _T(".")) || 385 !_tcscmp(f.cFileName, _T(".."))) 386 continue; 387 _tcscpy(TempFileName,Base); 388 _tcscat(TempFileName,f.cFileName); 389 390 if (f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 391 DeleteFolder(TempFileName); 392 else 393 { 394 SetFileAttributes(TempFileName,FILE_ATTRIBUTE_NORMAL); 395 if (!DeleteFile(TempFileName)) 396 { 397 FindClose (hFile); 398 return 0; 399 } 400 } 401 402 }while (FindNextFile (hFile, &f)); 403 FindClose (hFile); 404 } 405 return RemoveDirectory(FileName); 406 } 407 408 INT cmd_rmdir (LPTSTR param) 409 { 410 TCHAR ch; 411 INT args; 412 INT dirCount; 413 LPTSTR *arg; 414 INT i; 415 BOOL RD_SUB = FALSE; 416 BOOL RD_QUIET = FALSE; 417 INT res; 418 INT nError = 0; 419 TCHAR szFullPath[MAX_PATH]; 420 421 if (!_tcsncmp (param, _T("/?"), 2)) 422 { 423 ConOutResPaging(TRUE,STRING_RMDIR_HELP); 424 return 0; 425 } 426 427 arg = split (param, &args, FALSE, FALSE); 428 dirCount = 0; 429 430 /* check for options anywhere in command line */ 431 for (i = 0; i < args; i++) 432 { 433 if (*arg[i] == _T('/')) 434 { 435 /*found a command, but check to make sure it has something after it*/ 436 if (_tcslen (arg[i]) == 2) 437 { 438 ch = _totupper (arg[i][1]); 439 440 if (ch == _T('S')) 441 { 442 RD_SUB = TRUE; 443 } 444 else if (ch == _T('Q')) 445 { 446 RD_QUIET = TRUE; 447 } 448 } 449 } 450 else 451 { 452 dirCount++; 453 } 454 } 455 456 if (dirCount == 0) 457 { 458 /* No folder to remove */ 459 error_req_param_missing(); 460 freep(arg); 461 return 1; 462 } 463 464 for (i = 0; i < args; i++) 465 { 466 if (*arg[i] == _T('/')) 467 continue; 468 469 if (RD_SUB) 470 { 471 /* ask if they want to delete everything in the folder */ 472 if (!RD_QUIET) 473 { 474 res = FilePromptYNA (STRING_DEL_HELP2); 475 if (res == PROMPT_NO || res == PROMPT_BREAK) 476 { 477 nError = 1; 478 continue; 479 } 480 if (res == PROMPT_ALL) 481 RD_QUIET = TRUE; 482 } 483 /* get the folder name */ 484 GetFullPathName(arg[i],MAX_PATH,szFullPath,NULL); 485 486 /* remove trailing \ if any, but ONLY if dir is not the root dir */ 487 if (_tcslen (szFullPath) >= 2 && szFullPath[_tcslen (szFullPath) - 1] == _T('\\')) 488 szFullPath[_tcslen(szFullPath) - 1] = _T('\0'); 489 490 res = DeleteFolder(szFullPath); 491 } 492 else 493 { 494 res = RemoveDirectory(arg[i]); 495 } 496 497 if (!res) 498 { 499 /* Couldn't delete the folder, print out the error */ 500 nError = GetLastError(); 501 ErrorMessage(nError, _T("RD")); 502 } 503 } 504 505 freep (arg); 506 return nError; 507 } 508 #endif 509 510 511 /* 512 * Either exits the command interpreter, or quits the current batch context. 513 */ 514 515 /* Enable this define for supporting EXIT /B even when extensions are disabled */ 516 // #define SUPPORT_EXIT_B_NO_EXTENSIONS 517 518 INT CommandExit(LPTSTR param) 519 { 520 if (!_tcsncmp(param, _T("/?"), 2)) 521 { 522 ConOutResPaging(TRUE, STRING_EXIT_HELP); 523 524 /* Just make sure we don't exit */ 525 bExit = FALSE; 526 return 0; 527 } 528 529 if (_tcsnicmp(param, _T("/B"), 2) == 0) 530 { 531 param += 2; 532 533 /* 534 * If a current batch file is running, exit it, 535 * otherwise exit this command interpreter instance. 536 */ 537 if (bc) 538 { 539 /* Windows' CMD compatibility: Use GOTO :EOF */ 540 TCHAR EofLabel[] = _T(":EOF"); 541 542 #ifdef SUPPORT_EXIT_B_NO_EXTENSIONS 543 /* 544 * Temporarily enable extensions so as to support :EOF. 545 * 546 * Our GOTO implementation ensures that, when extensions are 547 * enabled and the label is ':EOF', no immediate change of batch 548 * context (done e.g. via ExitBatch() calls) is performed. 549 * This will therefore ensure that we do not spoil the extensions 550 * state when we restore it below. 551 */ 552 BOOL bOldEnableExtensions = bEnableExtensions; 553 bEnableExtensions = TRUE; 554 #endif 555 556 cmd_goto(EofLabel); 557 558 #ifdef SUPPORT_EXIT_B_NO_EXTENSIONS 559 /* Restore the original state of the extensions */ 560 bEnableExtensions = bOldEnableExtensions; 561 #endif 562 } 563 else 564 { 565 bExit = TRUE; 566 } 567 } 568 else 569 { 570 /* Exit this command interpreter instance */ 571 bExit = TRUE; 572 } 573 574 /* Search for an optional exit code */ 575 while (_istspace(*param)) 576 ++param; 577 578 /* Set the errorlevel to the exit code */ 579 if (_istdigit(*param)) 580 { 581 nErrorLevel = _ttoi(param); 582 // if (fSingleCommand == 1) return nErrorLevel; 583 } 584 585 return (bExit ? nErrorLevel : 0); 586 } 587 588 #ifdef INCLUDE_CMD_REM 589 /* 590 * does nothing 591 */ 592 INT CommandRem (LPTSTR param) 593 { 594 if (!_tcsncmp (param, _T("/?"), 2)) 595 { 596 ConOutResPaging(TRUE,STRING_REM_HELP); 597 } 598 599 return 0; 600 } 601 #endif /* INCLUDE_CMD_REM */ 602 603 604 INT CommandShowCommands (LPTSTR param) 605 { 606 PrintCommandList(); 607 return 0; 608 } 609 610 /* EOF */ 611