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
Pad(char * Str,char PadChar,ULONG Length)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
Date2Str(char * Str,USHORT Date)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
Time2Str(char * Str,USHORT Time)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
Attr2Str(char * Str,USHORT Attr)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
CCABManager()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
~CCABManager()180 CCABManager::~CCABManager()
181 /*
182 * FUNCTION: Default destructor
183 */
184 {
185 }
186
187
Usage()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
ParseCmdline(int argc,char * argv[])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
CreateCabinet()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
DisplayCabinet()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
ExtractFromCabinet()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
Run()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
OnOverwrite(PCFFILE File,const char * FileName)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
OnExtract(PCFFILE File,const char * FileName)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
OnDiskChange(const char * CabinetName,const char * DiskLabel)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
OnAdd(PCFFILE File,const char * FileName)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
OnVerboseMessage(const char * Message)673 void CCABManager::OnVerboseMessage(const char* Message)
674 {
675 if (Verbose)
676 {
677 printf("%s", Message);
678 }
679 }
680
main(int argc,char * argv[])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