xref: /reactos/sdk/tools/cabman/cabman.cxx (revision bbccad0e)
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