1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS cabinet manager 4 * FILE: tools/cabman/cabman.cxx 5 * PURPOSE: Main program 6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) 7 * Colin Finck <mail@colinfinck.de> 8 * REVISIONS: 9 * CSH 21/03-2001 Created 10 * CSH 15/08-2003 Made it portable 11 * CF 04/05-2007 Made it compatible with 64-bit operating systems 12 */ 13 #include <stdlib.h> 14 #include <stdarg.h> 15 #include <string.h> 16 #include <stdio.h> 17 #include "cabman.h" 18 19 20 #if DBG 21 22 ULONG DebugTraceLevel = MIN_TRACE; 23 //ULONG DebugTraceLevel = MID_TRACE; 24 //ULONG DebugTraceLevel = MAX_TRACE; 25 26 #endif /* DBG */ 27 28 29 char* Pad(char* Str, char PadChar, ULONG Length) 30 /* 31 * FUNCTION: Pads a string with a character to make a given length 32 * ARGUMENTS: 33 * Str = Pointer to string to pad 34 * PadChar = Character to pad with 35 * Length = Disired length of string 36 * RETURNS: 37 * Pointer to string 38 * NOTES: 39 * Str must be at least Length + 1 bytes 40 */ 41 { 42 ULONG Len; 43 44 Len = (ULONG)strlen(Str); 45 46 if (Len < Length) 47 { 48 memcpy(&Str[Length - Len], Str, Len + 1); 49 memset(Str, PadChar, Length - Len); 50 } 51 return Str; 52 } 53 54 55 char* Date2Str(char* Str, USHORT Date) 56 /* 57 * FUNCTION: Converts a DOS style date to a string 58 * ARGUMENTS: 59 * Str = Pointer to destination string 60 * Date = DOS style date 61 * RETURNS: 62 * Pointer to string 63 */ 64 { 65 ULONG dw; 66 67 /* Month */ 68 Str[0] = (char)('0' + ((Date & 0x01E0) >> 5) / 10); 69 Str[1] = (char)('0' + ((Date & 0x01E0) >> 5) % 10); 70 Str[2] = '-'; 71 /* Day */ 72 Str[3] = (char)('0' + (Date & 0x001F) / 10); 73 Str[4] = (char)('0' + (Date & 0x001F) % 10); 74 Str[5] = '-'; 75 /* Year */ 76 dw = 1980 + ((Date & 0xFE00) >> 9); 77 Str[6] = (char)('0' + dw / 1000); dw %= 1000; 78 Str[7] = (char)('0' + dw / 100); dw %= 100; 79 Str[8] = (char)('0' + dw / 10); dw %= 10; 80 Str[9] = (char)('0' + dw % 10); 81 Str[10] = '\0'; 82 return Str; 83 } 84 85 86 char* Time2Str(char* Str, USHORT Time) 87 /* 88 * FUNCTION: Converts a DOS style time to a string 89 * ARGUMENTS: 90 * Str = Pointer to destination string 91 * Time = DOS style time 92 * RETURNS: 93 * Pointer to string 94 */ 95 { 96 bool PM; 97 ULONG Hour; 98 ULONG dw; 99 100 Hour = ((Time & 0xF800) >> 11); 101 PM = (Hour >= 12); 102 Hour %= 12; 103 if (Hour == 0) 104 Hour = 12; 105 106 if (Hour >= 10) 107 Str[0] = (char)('0' + Hour / 10); 108 else Str[0] = ' '; 109 Str[1] = (char)('0' + Hour % 10); 110 Str[2] = ':'; 111 /* Minute */ 112 Str[3] = (char)('0' + ((Time & 0x07E0) >> 5) / 10); 113 Str[4] = (char)('0' + ((Time & 0x07E0) >> 5) % 10); 114 Str[5] = ':'; 115 /* Second */ 116 dw = 2 * (Time & 0x001F); 117 Str[6] = (char)('0' + dw / 10); 118 Str[7] = (char)('0' + dw % 10); 119 120 Str[8] = PM? 'p' : 'a'; 121 Str[9] = '\0'; 122 return Str; 123 } 124 125 126 char* Attr2Str(char* Str, USHORT Attr) 127 /* 128 * FUNCTION: Converts attributes to a string 129 * ARGUMENTS: 130 * Str = Pointer to destination string 131 * Attr = Attributes 132 * RETURNS: 133 * Pointer to string 134 */ 135 { 136 /* Archive */ 137 if (Attr & CAB_ATTRIB_ARCHIVE) 138 Str[0] = 'A'; 139 else 140 Str[0] = '-'; 141 142 /* Hidden */ 143 if (Attr & CAB_ATTRIB_HIDDEN) 144 Str[1] = 'H'; 145 else 146 Str[1] = '-'; 147 148 /* Read only */ 149 if (Attr & CAB_ATTRIB_READONLY) 150 Str[2] = 'R'; 151 else 152 Str[2] = '-'; 153 154 /* System */ 155 if (Attr & CAB_ATTRIB_SYSTEM) 156 Str[3] = 'S'; 157 else 158 Str[3] = '-'; 159 160 Str[4] = '\0'; 161 return Str; 162 } 163 164 165 /* CCABManager */ 166 167 CCABManager::CCABManager() 168 /* 169 * FUNCTION: Default constructor 170 */ 171 { 172 ProcessAll = false; 173 InfFileOnly = false; 174 Mode = CM_MODE_DISPLAY; 175 FileName[0] = 0; 176 Verbose = false; 177 } 178 179 180 CCABManager::~CCABManager() 181 /* 182 * FUNCTION: Default destructor 183 */ 184 { 185 } 186 187 188 void CCABManager::Usage() 189 /* 190 * FUNCTION: Display usage information on screen 191 */ 192 { 193 printf("ReactOS Cabinet Manager\n\n"); 194 printf("CABMAN [-D | -E] [-A] [-L dir] cabinet [filename ...]\n"); 195 printf("CABMAN [-M mode] -C dirfile [-I] [-RC file] [-P dir]\n"); 196 printf("CABMAN [-M mode] -S cabinet filename [-F folder] [filename] [...]\n"); 197 printf(" cabinet Cabinet file.\n"); 198 printf(" filename Name of the file to add to or extract from the cabinet.\n"); 199 printf(" Wild cards and multiple filenames\n"); 200 printf(" (separated by blanks) may be used.\n\n"); 201 202 printf(" dirfile Name of the directive file to use.\n"); 203 204 printf(" -A Process ALL cabinets. Follows cabinet chain\n"); 205 printf(" starting in first cabinet mentioned.\n"); 206 printf(" -C Create cabinet.\n"); 207 printf(" -D Display cabinet directory.\n"); 208 printf(" -E Extract files from cabinet.\n"); 209 printf(" -F Put the files from the next 'filename' filter in the cab in folder\filename.\n"); 210 printf(" -I Don't create the cabinet, only the .inf file.\n"); 211 printf(" -L dir Location to place extracted or generated files\n"); 212 printf(" (default is current directory).\n"); 213 printf(" -M mode Specify the compression method to use:\n"); 214 printf(" raw - No compression\n"); 215 printf(" mszip - MsZip compression (default)\n"); 216 printf(" -N Don't create the .inf file, only the cabinet.\n"); 217 printf(" -RC Specify file to put in cabinet reserved area\n"); 218 printf(" (size must be less than 64KB).\n"); 219 printf(" -S Create simple cabinet.\n"); 220 printf(" -P dir Files in the .dff are relative to this directory.\n"); 221 printf(" -V Verbose mode (prints more messages).\n"); 222 } 223 224 bool CCABManager::ParseCmdline(int argc, char* argv[]) 225 /* 226 * FUNCTION: Parse command line arguments 227 * ARGUMENTS: 228 * argc = Number of arguments on command line 229 * argv = Pointer to list of command line arguments 230 * RETURNS: 231 * true if command line arguments was successfully parsed, false if not 232 */ 233 { 234 int i; 235 bool ShowUsage; 236 bool FoundCabinet = false; 237 std::string NextFolder; 238 ShowUsage = (argc < 2); 239 240 for (i = 1; i < argc; i++) 241 { 242 if (argv[i][0] == '-') 243 { 244 switch (argv[i][1]) 245 { 246 case 'a': 247 case 'A': 248 ProcessAll = true; 249 break; 250 251 case 'c': 252 case 'C': 253 Mode = CM_MODE_CREATE; 254 break; 255 256 case 'd': 257 case 'D': 258 Mode = CM_MODE_DISPLAY; 259 break; 260 261 case 'e': 262 case 'E': 263 Mode = CM_MODE_EXTRACT; 264 break; 265 266 case 'f': 267 case 'F': 268 if (argv[i][2] == 0) 269 { 270 i++; 271 NextFolder = argv[i]; 272 } 273 else 274 { 275 NextFolder = argv[i] + 2; 276 } 277 break; 278 279 case 'i': 280 case 'I': 281 InfFileOnly = true; 282 break; 283 284 case 'l': 285 case 'L': 286 if (argv[i][2] == 0) 287 { 288 i++; 289 SetDestinationPath(&argv[i][0]); 290 } 291 else 292 SetDestinationPath(&argv[i][2]); 293 294 break; 295 296 case 'm': 297 case 'M': 298 // Set the compression codec (only affects compression, not decompression) 299 if(argv[i][2] == 0) 300 { 301 i++; 302 303 if( !SetCompressionCodec(&argv[i][0]) ) 304 return false; 305 } 306 else 307 { 308 if( !SetCompressionCodec(&argv[i][2]) ) 309 return false; 310 } 311 312 break; 313 314 case 'n': 315 case 'N': 316 DontGenerateInf = true; 317 break; 318 319 case 'R': 320 switch (argv[i][2]) 321 { 322 case 'C': /* File to put in cabinet reserved area */ 323 if (argv[i][3] == 0) 324 { 325 i++; 326 if (!SetCabinetReservedFile(&argv[i][0])) 327 { 328 printf("ERROR: Cannot open cabinet reserved area file.\n"); 329 return false; 330 } 331 } 332 else 333 { 334 if (!SetCabinetReservedFile(&argv[i][3])) 335 { 336 printf("ERROR: Cannot open cabinet reserved area file.\n"); 337 return false; 338 } 339 } 340 break; 341 342 default: 343 printf("ERROR: Bad parameter %s.\n", argv[i]); 344 return false; 345 } 346 break; 347 348 case 's': 349 case 'S': 350 Mode = CM_MODE_CREATE_SIMPLE; 351 break; 352 353 case 'P': 354 if (argv[i][2] == 0) 355 { 356 i++; 357 SetFileRelativePath(&argv[i][0]); 358 } 359 else 360 SetFileRelativePath(&argv[i][2]); 361 362 break; 363 364 case 'V': 365 Verbose = true; 366 break; 367 368 default: 369 printf("ERROR: Bad parameter %s.\n", argv[i]); 370 return false; 371 } 372 } 373 else 374 { 375 if(Mode == CM_MODE_CREATE) 376 { 377 if(FileName[0]) 378 { 379 printf("ERROR: You may only specify one directive file!\n"); 380 return false; 381 } 382 else 383 { 384 // For creating cabinets, this argument is the path to the directive file 385 strcpy(FileName, argv[i]); 386 } 387 } 388 else if(FoundCabinet) 389 { 390 // For creating simple cabinets, displaying or extracting them, add the argument as a search criteria 391 AddSearchCriteria(argv[i], NextFolder); 392 NextFolder.clear(); 393 } 394 else 395 { 396 SetCabinetName(argv[i]); 397 FoundCabinet = true; 398 } 399 } 400 } 401 402 if (ShowUsage) 403 { 404 Usage(); 405 return false; 406 } 407 408 // Select MsZip by default for creating cabinets 409 if( (Mode == CM_MODE_CREATE || Mode == CM_MODE_CREATE_SIMPLE) && !IsCodecSelected() ) 410 SelectCodec(CAB_CODEC_MSZIP); 411 412 // Search criteria (= the filename argument) is necessary for creating a simple cabinet 413 if( Mode == CM_MODE_CREATE_SIMPLE && !HasSearchCriteria()) 414 { 415 printf("ERROR: You have to enter input file names!\n"); 416 return false; 417 } 418 419 return true; 420 } 421 422 423 bool CCABManager::CreateCabinet() 424 /* 425 * FUNCTION: Create cabinet 426 */ 427 { 428 ULONG Status; 429 430 Status = Load(FileName); 431 if (Status != CAB_STATUS_SUCCESS) 432 { 433 printf("ERROR: Specified directive file could not be found: %s.\n", FileName); 434 return false; 435 } 436 437 Status = Parse(); 438 439 return (Status == CAB_STATUS_SUCCESS ? true : false); 440 } 441 442 bool CCABManager::DisplayCabinet() 443 /* 444 * FUNCTION: Display cabinet contents 445 */ 446 { 447 CAB_SEARCH Search; 448 char Str[20]; 449 ULONG FileCount = 0; 450 ULONG ByteCount = 0; 451 452 if (Open() == CAB_STATUS_SUCCESS) 453 { 454 if (Verbose) 455 { 456 printf("Cabinet %s\n\n", GetCabinetName()); 457 } 458 459 if (FindFirst(&Search) == CAB_STATUS_SUCCESS) 460 { 461 do 462 { 463 if (Search.File->FileControlID != CAB_FILE_CONTINUED) 464 { 465 printf("%s ", Date2Str(Str, Search.File->FileDate)); 466 printf("%s ", Time2Str(Str, Search.File->FileTime)); 467 printf("%s ", Attr2Str(Str, Search.File->Attributes)); 468 sprintf(Str, "%u", (UINT)Search.File->FileSize); 469 printf("%s ", Pad(Str, ' ', 13)); 470 printf("%s\n", Search.FileName.c_str()); 471 472 FileCount++; 473 ByteCount += Search.File->FileSize; 474 } 475 } while (FindNext(&Search) == CAB_STATUS_SUCCESS); 476 } 477 478 DestroySearchCriteria(); 479 480 if (FileCount > 0) { 481 if (FileCount == 1) 482 printf(" 1 file "); 483 else 484 { 485 sprintf(Str, "%u", (UINT)FileCount); 486 printf(" %s files ", Pad(Str, ' ', 12)); 487 } 488 489 if (ByteCount == 1) 490 printf(" 1 byte\n"); 491 else 492 { 493 sprintf(Str, "%u", (UINT)ByteCount); 494 printf("%s bytes\n", Pad(Str, ' ', 12)); 495 } 496 } 497 else 498 { 499 /* There should be at least one file in a cabinet */ 500 printf("WARNING: No files in cabinet."); 501 } 502 return true; 503 } 504 else 505 printf("ERROR: Cannot open file: %s\n", GetCabinetName()); 506 507 return false; 508 } 509 510 511 bool CCABManager::ExtractFromCabinet() 512 /* 513 * FUNCTION: Extract file(s) from cabinet 514 */ 515 { 516 bool bRet = true; 517 CAB_SEARCH Search; 518 ULONG Status; 519 520 if (Open() == CAB_STATUS_SUCCESS) 521 { 522 if (Verbose) 523 { 524 printf("Cabinet %s\n\n", GetCabinetName()); 525 } 526 527 if (FindFirst(&Search) == CAB_STATUS_SUCCESS) 528 { 529 do 530 { 531 switch (Status = ExtractFile(Search.FileName.c_str())) 532 { 533 case CAB_STATUS_SUCCESS: 534 break; 535 536 case CAB_STATUS_INVALID_CAB: 537 printf("ERROR: Cabinet contains errors.\n"); 538 bRet = false; 539 break; 540 541 case CAB_STATUS_UNSUPPCOMP: 542 printf("ERROR: Cabinet uses unsupported compression type.\n"); 543 bRet = false; 544 break; 545 546 case CAB_STATUS_CANNOT_WRITE: 547 printf("ERROR: You've run out of free space on the destination volume or the volume is damaged.\n"); 548 bRet = false; 549 break; 550 551 default: 552 printf("ERROR: Unspecified error code (%u).\n", (UINT)Status); 553 bRet = false; 554 break; 555 } 556 557 if(!bRet) 558 break; 559 } while (FindNext(&Search) == CAB_STATUS_SUCCESS); 560 561 DestroySearchCriteria(); 562 } 563 564 return bRet; 565 } 566 else 567 printf("ERROR: Cannot open file: %s.\n", GetCabinetName()); 568 569 return false; 570 } 571 572 573 bool CCABManager::Run() 574 /* 575 * FUNCTION: Process cabinet 576 */ 577 { 578 if (Verbose) 579 { 580 printf("ReactOS Cabinet Manager\n\n"); 581 } 582 583 switch (Mode) 584 { 585 case CM_MODE_CREATE: 586 return CreateCabinet(); 587 588 case CM_MODE_DISPLAY: 589 return DisplayCabinet(); 590 591 case CM_MODE_EXTRACT: 592 return ExtractFromCabinet(); 593 594 case CM_MODE_CREATE_SIMPLE: 595 return CreateSimpleCabinet(); 596 597 default: 598 break; 599 } 600 return false; 601 } 602 603 604 /* Event handlers */ 605 606 bool CCABManager::OnOverwrite(PCFFILE File, 607 const char* FileName) 608 /* 609 * FUNCTION: Called when extracting a file and it already exists 610 * ARGUMENTS: 611 * File = Pointer to CFFILE for file being extracted 612 * Filename = Pointer to buffer with name of file (full path) 613 * RETURNS 614 * true if the file should be overwritten, false if not 615 */ 616 { 617 if (Mode == CM_MODE_CREATE) 618 return true; 619 620 /* Always overwrite */ 621 return true; 622 } 623 624 625 void CCABManager::OnExtract(PCFFILE File, 626 const char* FileName) 627 /* 628 * FUNCTION: Called just before extracting a file 629 * ARGUMENTS: 630 * File = Pointer to CFFILE for file being extracted 631 * FileName = Pointer to buffer with name of file (full path) 632 */ 633 { 634 if (Verbose) 635 { 636 printf("Extracting %s\n", GetFileName(FileName).c_str()); 637 } 638 } 639 640 641 642 void CCABManager::OnDiskChange(const char* CabinetName, 643 const char* DiskLabel) 644 /* 645 * FUNCTION: Called when a new disk is to be processed 646 * ARGUMENTS: 647 * CabinetName = Pointer to buffer with name of cabinet 648 * DiskLabel = Pointer to buffer with label of disk 649 */ 650 { 651 if (Verbose) 652 { 653 printf("\nChanging to cabinet %s - %s\n\n", CabinetName, DiskLabel); 654 } 655 } 656 657 658 void CCABManager::OnAdd(PCFFILE File, 659 const char* FileName) 660 /* 661 * FUNCTION: Called just before adding a file to a cabinet 662 * ARGUMENTS: 663 * File = Pointer to CFFILE for file being added 664 * FileName = Pointer to buffer with name of file (full path) 665 */ 666 { 667 if (Verbose) 668 { 669 printf("Adding %s\n", GetFileName(FileName).c_str()); 670 } 671 } 672 673 void CCABManager::OnVerboseMessage(const char* Message) 674 { 675 if (Verbose) 676 { 677 printf("%s", Message); 678 } 679 } 680 681 int main(int argc, char * argv[]) 682 /* 683 * FUNCTION: Main entry point 684 * ARGUMENTS: 685 * argc = Number of arguments on command line 686 * argv = Pointer to list of command line arguments 687 */ 688 { 689 CCABManager CABMgr; 690 691 if (!CABMgr.ParseCmdline(argc, argv)) 692 return 2; 693 694 return CABMgr.Run() ? 0 : 1; 695 } 696 697 /* EOF */ 698