[MKHIVE] Minor improvements.
[reactos.git] / sdk / tools / cabman / main.cxx
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS cabinet manager
4 * FILE: tools/cabman/main.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 [...]\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(" -I Don't create the cabinet, only the .inf file.\n");
210 printf(" -L dir Location to place extracted or generated files\n");
211 printf(" (default is current directory).\n");
212 printf(" -M mode Specify the compression method to use:\n");
213 printf(" raw - No compression\n");
214 printf(" mszip - MsZip compression (default)\n");
215 printf(" -N Don't create the .inf file, only the cabinet.\n");
216 printf(" -RC Specify file to put in cabinet reserved area\n");
217 printf(" (size must be less than 64KB).\n");
218 printf(" -S Create simple cabinet.\n");
219 printf(" -P dir Files in the .dff are relative to this directory.\n");
220 printf(" -V Verbose mode (prints more messages).\n");
221 }
222
223 bool CCABManager::ParseCmdline(int argc, char* argv[])
224 /*
225 * FUNCTION: Parse command line arguments
226 * ARGUMENTS:
227 * argc = Number of arguments on command line
228 * argv = Pointer to list of command line arguments
229 * RETURNS:
230 * true if command line arguments was successfully parsed, false if not
231 */
232 {
233 int i;
234 bool ShowUsage;
235 bool FoundCabinet = false;
236
237 ShowUsage = (argc < 2);
238
239 for (i = 1; i < argc; i++)
240 {
241 if (argv[i][0] == '-')
242 {
243 switch (argv[i][1])
244 {
245 case 'a':
246 case 'A':
247 ProcessAll = true;
248 break;
249
250 case 'c':
251 case 'C':
252 Mode = CM_MODE_CREATE;
253 break;
254
255 case 'd':
256 case 'D':
257 Mode = CM_MODE_DISPLAY;
258 break;
259
260 case 'e':
261 case 'E':
262 Mode = CM_MODE_EXTRACT;
263 break;
264
265 case 'i':
266 case 'I':
267 InfFileOnly = true;
268 break;
269
270 case 'l':
271 case 'L':
272 if (argv[i][2] == 0)
273 {
274 i++;
275 SetDestinationPath(&argv[i][0]);
276 }
277 else
278 SetDestinationPath(&argv[i][2]);
279
280 break;
281
282 case 'm':
283 case 'M':
284 // Set the compression codec (only affects compression, not decompression)
285 if(argv[i][2] == 0)
286 {
287 i++;
288
289 if( !SetCompressionCodec(&argv[i][0]) )
290 return false;
291 }
292 else
293 {
294 if( !SetCompressionCodec(&argv[i][2]) )
295 return false;
296 }
297
298 break;
299
300 case 'n':
301 case 'N':
302 DontGenerateInf = true;
303 break;
304
305 case 'R':
306 switch (argv[i][2])
307 {
308 case 'C': /* File to put in cabinet reserved area */
309 if (argv[i][3] == 0)
310 {
311 i++;
312 if (!SetCabinetReservedFile(&argv[i][0]))
313 {
314 printf("ERROR: Cannot open cabinet reserved area file.\n");
315 return false;
316 }
317 }
318 else
319 {
320 if (!SetCabinetReservedFile(&argv[i][3]))
321 {
322 printf("ERROR: Cannot open cabinet reserved area file.\n");
323 return false;
324 }
325 }
326 break;
327
328 default:
329 printf("ERROR: Bad parameter %s.\n", argv[i]);
330 return false;
331 }
332 break;
333
334 case 's':
335 case 'S':
336 Mode = CM_MODE_CREATE_SIMPLE;
337 break;
338
339 case 'P':
340 if (argv[i][2] == 0)
341 {
342 i++;
343 SetFileRelativePath(&argv[i][0]);
344 }
345 else
346 SetFileRelativePath(&argv[i][2]);
347
348 break;
349
350 case 'V':
351 Verbose = true;
352 break;
353
354 default:
355 printf("ERROR: Bad parameter %s.\n", argv[i]);
356 return false;
357 }
358 }
359 else
360 {
361 if(Mode == CM_MODE_CREATE)
362 {
363 if(FileName[0])
364 {
365 printf("ERROR: You may only specify one directive file!\n");
366 return false;
367 }
368 else
369 {
370 // For creating cabinets, this argument is the path to the directive file
371 strcpy(FileName, argv[i]);
372 }
373 }
374 else if(FoundCabinet)
375 {
376 // For creating simple cabinets, displaying or extracting them, add the argument as a search criteria
377 AddSearchCriteria(argv[i]);
378 }
379 else
380 {
381 SetCabinetName(argv[i]);
382 FoundCabinet = true;
383 }
384 }
385 }
386
387 if (ShowUsage)
388 {
389 Usage();
390 return false;
391 }
392
393 // Select MsZip by default for creating cabinets
394 if( (Mode == CM_MODE_CREATE || Mode == CM_MODE_CREATE_SIMPLE) && !IsCodecSelected() )
395 SelectCodec(CAB_CODEC_MSZIP);
396
397 // Search criteria (= the filename argument) is necessary for creating a simple cabinet
398 if( Mode == CM_MODE_CREATE_SIMPLE && !HasSearchCriteria())
399 {
400 printf("ERROR: You have to enter input file names!\n");
401 return false;
402 }
403
404 return true;
405 }
406
407
408 bool CCABManager::CreateCabinet()
409 /*
410 * FUNCTION: Create cabinet
411 */
412 {
413 ULONG Status;
414
415 Status = Load(FileName);
416 if (Status != CAB_STATUS_SUCCESS)
417 {
418 printf("ERROR: Specified directive file could not be found: %s.\n", FileName);
419 return false;
420 }
421
422 Status = Parse();
423
424 return (Status == CAB_STATUS_SUCCESS ? true : false);
425 }
426
427 bool CCABManager::DisplayCabinet()
428 /*
429 * FUNCTION: Display cabinet contents
430 */
431 {
432 CAB_SEARCH Search;
433 char Str[20];
434 ULONG FileCount = 0;
435 ULONG ByteCount = 0;
436
437 if (Open() == CAB_STATUS_SUCCESS)
438 {
439 if (Verbose)
440 {
441 printf("Cabinet %s\n\n", GetCabinetName());
442 }
443
444 if (FindFirst(&Search) == CAB_STATUS_SUCCESS)
445 {
446 do
447 {
448 if (Search.File->FileControlID != CAB_FILE_CONTINUED)
449 {
450 printf("%s ", Date2Str(Str, Search.File->FileDate));
451 printf("%s ", Time2Str(Str, Search.File->FileTime));
452 printf("%s ", Attr2Str(Str, Search.File->Attributes));
453 sprintf(Str, "%u", (UINT)Search.File->FileSize);
454 printf("%s ", Pad(Str, ' ', 13));
455 printf("%s\n", Search.FileName);
456
457 FileCount++;
458 ByteCount += Search.File->FileSize;
459 }
460 } while (FindNext(&Search) == CAB_STATUS_SUCCESS);
461 }
462
463 DestroySearchCriteria();
464
465 if (FileCount > 0) {
466 if (FileCount == 1)
467 printf(" 1 file ");
468 else
469 {
470 sprintf(Str, "%u", (UINT)FileCount);
471 printf(" %s files ", Pad(Str, ' ', 12));
472 }
473
474 if (ByteCount == 1)
475 printf(" 1 byte\n");
476 else
477 {
478 sprintf(Str, "%u", (UINT)ByteCount);
479 printf("%s bytes\n", Pad(Str, ' ', 12));
480 }
481 }
482 else
483 {
484 /* There should be at least one file in a cabinet */
485 printf("WARNING: No files in cabinet.");
486 }
487 return true;
488 }
489 else
490 printf("ERROR: Cannot open file: %s\n", GetCabinetName());
491
492 return false;
493 }
494
495
496 bool CCABManager::ExtractFromCabinet()
497 /*
498 * FUNCTION: Extract file(s) from cabinet
499 */
500 {
501 bool bRet = true;
502 CAB_SEARCH Search;
503 ULONG Status;
504
505 if (Open() == CAB_STATUS_SUCCESS)
506 {
507 if (Verbose)
508 {
509 printf("Cabinet %s\n\n", GetCabinetName());
510 }
511
512 if (FindFirst(&Search) == CAB_STATUS_SUCCESS)
513 {
514 do
515 {
516 switch (Status = ExtractFile(Search.FileName))
517 {
518 case CAB_STATUS_SUCCESS:
519 break;
520
521 case CAB_STATUS_INVALID_CAB:
522 printf("ERROR: Cabinet contains errors.\n");
523 bRet = false;
524 break;
525
526 case CAB_STATUS_UNSUPPCOMP:
527 printf("ERROR: Cabinet uses unsupported compression type.\n");
528 bRet = false;
529 break;
530
531 case CAB_STATUS_CANNOT_WRITE:
532 printf("ERROR: You've run out of free space on the destination volume or the volume is damaged.\n");
533 bRet = false;
534 break;
535
536 default:
537 printf("ERROR: Unspecified error code (%u).\n", (UINT)Status);
538 bRet = false;
539 break;
540 }
541
542 if(!bRet)
543 break;
544 } while (FindNext(&Search) == CAB_STATUS_SUCCESS);
545
546 DestroySearchCriteria();
547 }
548
549 return bRet;
550 }
551 else
552 printf("ERROR: Cannot open file: %s.\n", GetCabinetName());
553
554 return false;
555 }
556
557
558 bool CCABManager::Run()
559 /*
560 * FUNCTION: Process cabinet
561 */
562 {
563 if (Verbose)
564 {
565 printf("ReactOS Cabinet Manager\n\n");
566 }
567
568 switch (Mode)
569 {
570 case CM_MODE_CREATE:
571 return CreateCabinet();
572
573 case CM_MODE_DISPLAY:
574 return DisplayCabinet();
575
576 case CM_MODE_EXTRACT:
577 return ExtractFromCabinet();
578
579 case CM_MODE_CREATE_SIMPLE:
580 return CreateSimpleCabinet();
581
582 default:
583 break;
584 }
585 return false;
586 }
587
588
589 /* Event handlers */
590
591 bool CCABManager::OnOverwrite(PCFFILE File,
592 char* FileName)
593 /*
594 * FUNCTION: Called when extracting a file and it already exists
595 * ARGUMENTS:
596 * File = Pointer to CFFILE for file being extracted
597 * Filename = Pointer to buffer with name of file (full path)
598 * RETURNS
599 * true if the file should be overwritten, false if not
600 */
601 {
602 if (Mode == CM_MODE_CREATE)
603 return true;
604
605 /* Always overwrite */
606 return true;
607 }
608
609
610 void CCABManager::OnExtract(PCFFILE File,
611 char* FileName)
612 /*
613 * FUNCTION: Called just before extracting a file
614 * ARGUMENTS:
615 * File = Pointer to CFFILE for file being extracted
616 * FileName = Pointer to buffer with name of file (full path)
617 */
618 {
619 if (Verbose)
620 {
621 printf("Extracting %s\n", GetFileName(FileName));
622 }
623 }
624
625
626
627 void CCABManager::OnDiskChange(char* CabinetName,
628 char* DiskLabel)
629 /*
630 * FUNCTION: Called when a new disk is to be processed
631 * ARGUMENTS:
632 * CabinetName = Pointer to buffer with name of cabinet
633 * DiskLabel = Pointer to buffer with label of disk
634 */
635 {
636 if (Verbose)
637 {
638 printf("\nChanging to cabinet %s - %s\n\n", CabinetName, DiskLabel);
639 }
640 }
641
642
643 void CCABManager::OnAdd(PCFFILE File,
644 char* FileName)
645 /*
646 * FUNCTION: Called just before adding a file to a cabinet
647 * ARGUMENTS:
648 * File = Pointer to CFFILE for file being added
649 * FileName = Pointer to buffer with name of file (full path)
650 */
651 {
652 if (Verbose)
653 {
654 printf("Adding %s\n", GetFileName(FileName));
655 }
656 }
657
658 CCABManager CABMgr;
659
660 int main(int argc, char * argv[])
661 /*
662 * FUNCTION: Main entry point
663 * ARGUMENTS:
664 * argc = Number of arguments on command line
665 * argv = Pointer to list of command line arguments
666 */
667 {
668 bool status = false;
669
670 if (CABMgr.ParseCmdline(argc, argv)) status = CABMgr.Run();
671
672 return (status ? 0 : 1);
673 }
674
675 /* EOF */