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 /* help 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 exacly 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 /* The use of both of these together will correct the case of a path 188 where as one alone or GetFullPath will not. Exameple: 189 c:\windows\SYSTEM32 => C:\WINDOWS\system32 */ 190 if (GetFullPathName(InPath, MAX_PATH, OutPathTemp, NULL)) 191 { 192 GetPathCase(OutPathTemp, OutPath); 193 194 /* Use _tchdir, since unlike SetCurrentDirectory it updates 195 * the current-directory-on-drive environment variables. */ 196 if (_tchdir(OutPath) != 0) 197 { 198 ConErrFormatMessage(GetLastError()); 199 nErrorLevel = 1; 200 return FALSE; 201 } 202 203 /* Keep original drive in ordinary CD/CHDIR (without /D switch). */ 204 if (oldpath != NULL && _tcsncicmp(OutPath, oldpath, 2) != 0) 205 SetCurrentDirectory(oldpath); 206 } 207 208 return TRUE; 209 } 210 211 212 /* 213 * CD / CHDIR 214 * 215 */ 216 INT cmd_chdir (LPTSTR param) 217 { 218 TCHAR szCurrent[MAX_PATH]; 219 BOOL bChangeDrive = FALSE; 220 221 /* Filter out special cases first */ 222 223 /* Print Help */ 224 if (!_tcsncmp(param, _T("/?"), 2)) 225 { 226 ConOutResPaging(TRUE,STRING_CD_HELP); 227 return 0; 228 } 229 230 /* Remove " */ 231 StripQuotes(param); 232 233 /* Set Error Level to Success */ 234 nErrorLevel = 0; 235 236 /* Print Current Directory on a disk */ 237 if (_tcslen(param) == 2 && param[1] == _T(':')) 238 { 239 if (GetRootPath(param, szCurrent, MAX_PATH)) 240 { 241 error_invalid_drive(); 242 return 1; 243 } 244 ConOutPuts(szCurrent); 245 return 0; 246 } 247 248 /* Get Current Directory */ 249 GetCurrentDirectory(MAX_PATH, szCurrent); 250 if (param[0] == _T('\0')) 251 { 252 ConOutPuts(szCurrent); 253 return 0; 254 } 255 256 /* Input String Contains /D Switch */ 257 if (!_tcsncicmp(param, _T("/D"), 2)) 258 { 259 bChangeDrive = TRUE; 260 param += 2; 261 while (_istspace(*param)) 262 param++; 263 } 264 265 if (!SetRootPath(bChangeDrive ? NULL : szCurrent, param)) 266 return 1; 267 268 return 0; 269 } 270 271 #endif 272 273 274 275 #ifdef INCLUDE_CMD_MKDIR 276 277 /* Helper funtion for mkdir to make directories in a path. 278 Dont use the api to decrease depence on libs */ 279 BOOL 280 MakeFullPath(TCHAR * DirPath) 281 { 282 TCHAR path[MAX_PATH]; 283 TCHAR *p = DirPath; 284 INT n; 285 286 if (CreateDirectory(DirPath, NULL)) 287 return TRUE; 288 else if (GetLastError() != ERROR_PATH_NOT_FOUND) 289 return FALSE; 290 291 /* got ERROR_PATH_NOT_FOUND, so try building it up one component at a time */ 292 if (p[0] && p[1] == _T(':')) 293 p += 2; 294 while (*p == _T('\\')) 295 p++; /* skip drive root */ 296 do 297 { 298 p = _tcschr(p, _T('\\')); 299 n = p ? p++ - DirPath : _tcslen(DirPath); 300 _tcsncpy(path, DirPath, n); 301 path[n] = _T('\0'); 302 if( !CreateDirectory(path, NULL) && 303 (GetLastError() != ERROR_ALREADY_EXISTS)) 304 return FALSE; 305 } while (p != NULL); 306 307 return TRUE; 308 } 309 310 /* 311 * MD / MKDIR 312 * 313 */ 314 INT cmd_mkdir (LPTSTR param) 315 { 316 LPTSTR *p; 317 INT argc, i; 318 if (!_tcsncmp (param, _T("/?"), 2)) 319 { 320 ConOutResPaging(TRUE,STRING_MKDIR_HELP); 321 return 0; 322 } 323 324 p = split (param, &argc, FALSE); 325 if (argc == 0) 326 { 327 ConErrResPuts(STRING_ERROR_REQ_PARAM_MISSING); 328 nErrorLevel = 1; 329 freep(p); 330 return 1; 331 } 332 333 nErrorLevel = 0; 334 for (i = 0; i < argc; i++) 335 { 336 if (!MakeFullPath(p[i])) 337 { 338 if(GetLastError() == ERROR_PATH_NOT_FOUND) 339 { 340 ConErrResPuts(STRING_MD_ERROR2); 341 } 342 else 343 { 344 ErrorMessage (GetLastError(), _T("MD")); 345 } 346 nErrorLevel = 1; 347 } 348 } 349 350 freep (p); 351 return nErrorLevel; 352 } 353 #endif 354 355 356 #ifdef INCLUDE_CMD_RMDIR 357 /* 358 * RD / RMDIR 359 * 360 */ 361 BOOL DeleteFolder(LPTSTR FileName) 362 { 363 TCHAR Base[MAX_PATH]; 364 TCHAR TempFileName[MAX_PATH]; 365 HANDLE hFile; 366 WIN32_FIND_DATA f; 367 _tcscpy(Base,FileName); 368 _tcscat(Base,_T("\\*")); 369 hFile = FindFirstFile(Base, &f); 370 Base[_tcslen(Base) - 1] = _T('\0'); 371 if (hFile != INVALID_HANDLE_VALUE) 372 { 373 do 374 { 375 if (!_tcscmp(f.cFileName, _T(".")) || 376 !_tcscmp(f.cFileName, _T(".."))) 377 continue; 378 _tcscpy(TempFileName,Base); 379 _tcscat(TempFileName,f.cFileName); 380 381 if(f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 382 DeleteFolder(TempFileName); 383 else 384 { 385 SetFileAttributes(TempFileName,FILE_ATTRIBUTE_NORMAL); 386 if(!DeleteFile(TempFileName)) 387 return 0; 388 } 389 390 }while (FindNextFile (hFile, &f)); 391 FindClose (hFile); 392 } 393 return RemoveDirectory(FileName); 394 } 395 INT cmd_rmdir (LPTSTR param) 396 { 397 TCHAR ch; 398 INT args; 399 INT dirCount; 400 LPTSTR *arg; 401 INT i; 402 BOOL RD_SUB = FALSE; 403 BOOL RD_QUIET = FALSE; 404 INT res; 405 INT nError = 0; 406 TCHAR szFullPath[MAX_PATH]; 407 408 if (!_tcsncmp (param, _T("/?"), 2)) 409 { 410 ConOutResPaging(TRUE,STRING_RMDIR_HELP); 411 return 0; 412 } 413 414 arg = split (param, &args, FALSE); 415 dirCount = 0; 416 417 /* check for options anywhere in command line */ 418 for (i = 0; i < args; i++) 419 { 420 if (*arg[i] == _T('/')) 421 { 422 /*found a command, but check to make sure it has something after it*/ 423 if (_tcslen (arg[i]) == 2) 424 { 425 ch = _totupper (arg[i][1]); 426 427 if (ch == _T('S')) 428 { 429 RD_SUB = TRUE; 430 } 431 else if (ch == _T('Q')) 432 { 433 RD_QUIET = TRUE; 434 } 435 } 436 } 437 else 438 { 439 dirCount++; 440 } 441 } 442 443 if (dirCount == 0) 444 { 445 /* No folder to remove */ 446 error_req_param_missing(); 447 freep(arg); 448 return 1; 449 } 450 451 for (i = 0; i < args; i++) 452 { 453 if (*arg[i] == _T('/')) 454 continue; 455 456 if (RD_SUB) 457 { 458 /* ask if they want to delete evrything in the folder */ 459 if (!RD_QUIET) 460 { 461 res = FilePromptYNA (STRING_DEL_HELP2); 462 if (res == PROMPT_NO || res == PROMPT_BREAK) 463 { 464 nError = 1; 465 continue; 466 } 467 if (res == PROMPT_ALL) 468 RD_QUIET = TRUE; 469 } 470 /* get the folder name */ 471 GetFullPathName(arg[i],MAX_PATH,szFullPath,NULL); 472 473 /* remove trailing \ if any, but ONLY if dir is not the root dir */ 474 if (_tcslen (szFullPath) >= 2 && szFullPath[_tcslen (szFullPath) - 1] == _T('\\')) 475 szFullPath[_tcslen(szFullPath) - 1] = _T('\0'); 476 477 res = DeleteFolder(szFullPath); 478 } 479 else 480 { 481 res = RemoveDirectory(arg[i]); 482 } 483 484 if (!res) 485 { 486 /* Couldn't delete the folder, print out the error */ 487 nError = GetLastError(); 488 ErrorMessage(nError, _T("RD")); 489 } 490 } 491 492 freep (arg); 493 return nError; 494 } 495 #endif 496 497 498 /* 499 * set the exitflag to true 500 * 501 */ 502 INT CommandExit (LPTSTR param) 503 { 504 if (!_tcsncmp (param, _T("/?"), 2)) 505 { 506 ConOutResPaging(TRUE,STRING_EXIT_HELP); 507 /* Just make sure */ 508 bExit = FALSE; 509 /* Dont exit */ 510 return 0; 511 } 512 513 if (bc != NULL && _tcsnicmp(param,_T("/b"),2) == 0) 514 { 515 param += 2; 516 while (_istspace (*param)) 517 param++; 518 if (_istdigit(*param)) 519 nErrorLevel = _ttoi(param); 520 ExitBatch(); 521 } 522 523 else 524 bExit = TRUE; 525 526 527 return 0; 528 529 } 530 531 #ifdef INCLUDE_CMD_REM 532 /* 533 * does nothing 534 * 535 */ 536 INT CommandRem (LPTSTR param) 537 { 538 if (!_tcsncmp (param, _T("/?"), 2)) 539 { 540 ConOutResPaging(TRUE,STRING_REM_HELP); 541 } 542 543 return 0; 544 } 545 #endif /* INCLUDE_CMD_REM */ 546 547 548 INT CommandShowCommands (LPTSTR param) 549 { 550 PrintCommandList (); 551 return 0; 552 } 553 554 INT CommandShowCommandsDetail (LPTSTR param) 555 { 556 /* If a param was send, display help of correspondent command */ 557 if (_tcslen(param)) 558 { 559 DoCommand(param, _T("/?"), NULL); 560 } 561 /* Else, display detailed commands list */ 562 else 563 { 564 PrintCommandListDetail (); 565 } 566 return 0; 567 } 568 569 /* EOF */ 570