update for HEAD-2003091401
[reactos.git] / tools / cabman / main.cxx
1 /*
2  * COPYRIGHT:   See COPYING in the top level directory
3  * PROJECT:     ReactOS cabinet manager
4  * FILE:        tools/cabman/main.cpp
5  * PURPOSE:     Main program
6  * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7  * REVISIONS:
8  *   CSH 21/03-2001 Created
9  *   CSH 15/08-2003 Made it portable
10  */
11 #include <stdlib.h>
12 #include <stdarg.h>
13 #include <string.h>
14 #include <stdio.h>
15 #include "cabman.h"
16
17
18 #ifdef DBG
19
20 unsigned long DebugTraceLevel = MIN_TRACE;
21 //unsigned long DebugTraceLevel = MID_TRACE;
22 //unsigned long DebugTraceLevel = MAX_TRACE;
23
24 #endif /* DBG */
25
26
27 #define CM_VERSION  "0.9"
28
29
30 char* Pad(char* Str, char PadChar, unsigned int Length)
31 /*
32  * FUNCTION: Pads a string with a character to make a given length
33  * ARGUMENTS:
34  *     Str     = Pointer to string to pad
35  *     PadChar = Character to pad with
36  *     Length  = Disired length of string
37  * RETURNS:
38  *     Pointer to string
39  * NOTES:
40  *     Str must be at least Length + 1 bytes
41  */
42 {
43     unsigned int Len;
44
45     Len = strlen(Str);
46
47     if (Len < Length) {
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, unsigned short 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     unsigned long 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, unsigned short 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     unsigned long Hour;
98     unsigned long 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, unsigned short 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 }
176
177
178 CCABManager::~CCABManager()
179 /*
180  * FUNCTION: Default destructor
181  */
182 {
183 }
184
185
186 void CCABManager::Usage()
187 /*
188  * FUNCTION: Display usage information on screen
189  */
190 {
191     printf("ReactOS Cabinet Manager - Version %s\n\n", CM_VERSION);
192     printf("CABMAN [/D | /E] [/A] [/L dir] cabinet [filename ...]\n");
193     printf("CABMAN /C dirfile [/I] [/RC file]\n");
194     printf("CABMAN /S cabinet filename\n");
195     printf("  cabinet   Cabinet file.\n");
196     printf("  filename  Name of the file to extract from the cabinet.\n");
197     printf("            Wild cards and multiple filenames\n");
198     printf("            (separated by blanks) may be used.\n\n");
199
200     printf("  dirfile   Name of the directive file to use.\n");
201
202     printf("  /A        Process ALL cabinets. Follows cabinet chain\n");
203     printf("            starting in first cabinet mentioned.\n");
204     printf("  /C        Create cabinet.\n");
205     printf("  /D        Display cabinet directory.\n");
206     printf("  /E        Extract files from cabinet.\n");
207     printf("  /I        Don't create the cabinet, only the .inf file.\n");
208     printf("  /L dir    Location to place extracted or generated files\n");
209     printf("            (default is current directory).\n");
210     printf("  /N        Don't create the .inf file, only the cabinet.\n");
211     printf("  /RC       Specify file to put in cabinet reserved area\n");
212     printf("            (size must be less than 64KB).\n");
213     printf("  /S        Create simple cabinet.\n");
214 }
215
216 bool CCABManager::ParseCmdline(int argc, char* argv[])
217 /*
218  * FUNCTION: Parse command line arguments
219  * ARGUMENTS:
220  *     argc = Number of arguments on command line
221  *     argv = Pointer to list of command line arguments
222  * RETURNS:
223  *    true if command line arguments was successfully parsed, false if not
224  */
225 {
226     int i;
227     bool ShowUsage;
228     bool FoundCabinet = false;
229
230     ShowUsage = (argc < 2);
231
232     for (i = 1; i < argc; i++) {
233         if (argv[i][0] == '/') {
234           switch (argv[i][1]) {
235           case 'a':
236           case 'A': ProcessAll = true; break;
237           case 'c':
238           case 'C': Mode = CM_MODE_CREATE; break;
239           case 'd':
240           case 'D': Mode = CM_MODE_DISPLAY; break;
241           case 'e':
242           case 'E': Mode = CM_MODE_EXTRACT; break;
243           case 'i':
244           case 'I': InfFileOnly = true; break;
245           case 'l':
246           case 'L':
247                     if (argv[i][2] == 0) {
248                         i++;
249                         SetDestinationPath((char*)&argv[i][0]);
250                     } else {
251                         SetDestinationPath((char*)&argv[i][1]);
252                     }
253                     break;
254           case 'n':
255           case 'N': DontGenerateInf = true; break;
256           case 'R':
257                     switch (argv[i][2]) {
258                         case 'C': /* File to put in cabinet reserved area */
259                             if (argv[i][3] == 0) {
260                                 i++;
261                                 if (!SetCabinetReservedFile((char*)&argv[i][0])) {
262                                     printf("Cannot open cabinet reserved area file.\n");
263                                     return false;
264                                 }
265                             } else {
266                                 if (!SetCabinetReservedFile((char*)&argv[i][3])) {
267                                     printf("Cannot open cabinet reserved area file.\n");
268                                     return false;
269                                 }
270                             }
271                             break;
272                         default:
273                             printf("Bad parameter %s.\n", argv[i]);
274                             return false;
275                     }
276                     break;
277           case 's':
278           case 'S': Mode = CM_MODE_CREATE_SIMPLE; break;
279           default:
280                     printf("Bad parameter %s.\n", argv[i]);
281                     return false;
282           }
283         } else {
284             if ((FoundCabinet) || (Mode == CM_MODE_CREATE)) {
285                 /* FIXME: There may be many of these if Mode != CM_MODE_CREATE */
286                 strcpy((char*)FileName, argv[i]);
287             } else {
288                 SetCabinetName(argv[i]);
289                 FoundCabinet = true;
290             }
291         }
292     }
293
294     if (ShowUsage) {
295       Usage();
296       return false;
297     }
298
299     /* FIXME */
300     SelectCodec(CAB_CODEC_MSZIP);
301
302     return true;
303 }
304
305
306 bool CCABManager::CreateCabinet()
307 /*
308  * FUNCTION: Create cabinet
309  */
310 {
311     unsigned long Status;
312
313     Status = Load((char*)&FileName);
314     if (Status != CAB_STATUS_SUCCESS) {
315         printf("Specified directive file could not be found: %s.\n", (char*)&FileName);
316         return false;
317     }
318
319     Parse();
320
321     return true;
322 }
323
324
325 bool CCABManager::CreateSimpleCabinet()
326 /*
327  * FUNCTION: Create cabinet
328  */
329 {
330     unsigned long Status;
331
332     Status = NewCabinet();
333     if (Status != CAB_STATUS_SUCCESS) {
334         DPRINT(MIN_TRACE, ("Cannot create cabinet (%d).\n", (unsigned int)Status));
335         return false;
336     }
337
338     Status = AddFile(FileName);
339     if (Status != CAB_STATUS_SUCCESS) {
340         DPRINT(MIN_TRACE, ("Cannot add file to cabinet (%d).\n", (unsigned int)Status));
341         return false;
342     }
343
344     Status = WriteDisk(false);
345     if (Status == CAB_STATUS_SUCCESS)
346         Status = CloseDisk();
347     if (Status != CAB_STATUS_SUCCESS) {
348         DPRINT(MIN_TRACE, ("Cannot write disk (%d).\n", (unsigned int)Status));
349         return false;
350     }
351
352     CloseCabinet();
353
354     return true;
355 }
356
357
358 bool CCABManager::DisplayCabinet()
359 /*
360  * FUNCTION: Display cabinet contents
361  */
362 {
363     CAB_SEARCH Search;
364     char Str[20];
365     unsigned long FileCount = 0;
366     unsigned long ByteCount = 0;
367
368     if (Open() == CAB_STATUS_SUCCESS) {
369         printf("Cabinet %s\n\n", GetCabinetName());
370
371         if (FindFirst("", &Search) == CAB_STATUS_SUCCESS) {
372             do {
373                 if (Search.File->FileControlID != CAB_FILE_CONTINUED) {
374                     printf("%s ", Date2Str((char*)&Str, Search.File->FileDate));
375                     printf("%s ", Time2Str((char*)&Str, Search.File->FileTime));
376                     printf("%s ", Attr2Str((char*)&Str, Search.File->Attributes));
377                     sprintf(Str, "%d", Search.File->FileSize);
378                     printf("%s ", Pad(Str, ' ', 13));
379                     printf("%s\n", Search.FileName);
380
381                     FileCount++;
382                     ByteCount += Search.File->FileSize;
383                 }
384             } while (FindNext(&Search) == CAB_STATUS_SUCCESS);
385         }
386
387         if (FileCount > 0) {
388             if (FileCount == 1)
389                 printf("                 1 file    ");
390             else {
391                 sprintf(Str, "%d", FileCount);
392                 printf("      %s files   ", Pad(Str, ' ', 12));
393             }
394
395             if (ByteCount == 1)
396                 printf("           1 byte\n");
397             else {
398                 sprintf(Str, "%d", ByteCount);
399                 printf("%s bytes\n", Pad(Str, ' ', 12));
400             }
401         } else {
402             /* There should be at least one file in a cabinet */
403             printf("No files in cabinet.");
404         }
405         return true;
406     } else
407         printf("Cannot not open file: %s\n", GetCabinetName());
408
409     return false;
410 }
411
412
413 bool CCABManager::ExtractFromCabinet()
414 /*
415  * FUNCTION: Extract file(s) from cabinet
416  */
417 {
418     CAB_SEARCH Search;
419     unsigned long Status;
420
421     if (Open() == CAB_STATUS_SUCCESS) {
422         printf("Cabinet %s\n\n", GetCabinetName());
423
424         if (FindFirst("", &Search) == CAB_STATUS_SUCCESS) {
425             do {
426                 switch (Status = ExtractFile(Search.FileName)) {
427                     case CAB_STATUS_SUCCESS:
428                         break;
429                     case CAB_STATUS_INVALID_CAB:
430                         printf("Cabinet contains errors.\n");
431                         return false;
432                     case CAB_STATUS_UNSUPPCOMP:
433                         printf("Cabinet uses unsupported compression type.\n");
434                         return false;
435                     case CAB_STATUS_CANNOT_WRITE:
436                         printf("You've run out of free space on the destination volume or the volume is damaged.\n");
437                         return false;
438                     default:
439                         printf("Unspecified error code (%d).\n", (unsigned int)Status);
440                         return false;
441                 }
442             } while (FindNext(&Search) == CAB_STATUS_SUCCESS);
443         }
444         return true;
445     } else
446         printf("Cannot not open file: %s.\n", GetCabinetName());
447
448     return false;
449 }
450
451
452 bool CCABManager::Run()
453 /*
454  * FUNCTION: Process cabinet
455  */
456 {
457     printf("ReactOS Cabinet Manager - Version %s\n\n", CM_VERSION);
458
459     switch (Mode) {
460     case CM_MODE_CREATE:  return CreateCabinet(); break;
461     case CM_MODE_DISPLAY: return DisplayCabinet(); break;
462     case CM_MODE_EXTRACT: return ExtractFromCabinet(); break;
463     case CM_MODE_CREATE_SIMPLE:  return CreateSimpleCabinet(); break;
464     default:
465         break;
466     }
467     return false;
468 }
469
470
471 /* Event handlers */
472
473 bool CCABManager::OnOverwrite(PCFFILE File,
474                               char* FileName)
475 /*
476  * FUNCTION: Called when extracting a file and it already exists
477  * ARGUMENTS:
478  *     File     = Pointer to CFFILE for file being extracted
479  *     Filename = Pointer to buffer with name of file (full path)
480  * RETURNS
481  *     true if the file should be overwritten, false if not
482  */
483 {
484     char ch;
485
486     if (Mode == CM_MODE_CREATE)
487         return true;
488
489     /* Always overwrite */
490     return true;
491 }
492
493
494 void CCABManager::OnExtract(PCFFILE File,
495                             char* FileName)
496 /*
497  * FUNCTION: Called just before extracting a file
498  * ARGUMENTS:
499  *     File     = Pointer to CFFILE for file being extracted
500  *     FileName = Pointer to buffer with name of file (full path)
501  */
502 {
503     printf("Extracting %s\n", GetFileName(FileName));
504 }
505
506
507
508 void CCABManager::OnDiskChange(char* CabinetName,
509                                char* DiskLabel)
510 /*
511  * FUNCTION: Called when a new disk is to be processed
512  * ARGUMENTS:
513  *     CabinetName = Pointer to buffer with name of cabinet
514  *     DiskLabel   = Pointer to buffer with label of disk
515  */
516 {
517     printf("\nChanging to cabinet %s - %s\n\n", CabinetName, DiskLabel);
518 }
519
520
521 void CCABManager::OnAdd(PCFFILE File,
522                         char* FileName)
523 /*
524  * FUNCTION: Called just before adding a file to a cabinet
525  * ARGUMENTS:
526  *     File     = Pointer to CFFILE for file being added
527  *     FileName = Pointer to buffer with name of file (full path)
528  */
529 {
530     printf("Adding %s\n", GetFileName(FileName));
531 }
532
533
534 int main(int argc, char * argv[])
535 /*
536  * FUNCTION: Main entry point
537  * ARGUMENTS:
538  *     argc = Number of arguments on command line
539  *     argv = Pointer to list of command line arguments
540  */
541 {
542     CCABManager CABMgr;
543     bool status = false;
544
545     if (CABMgr.ParseCmdline(argc, argv)) {
546         status = CABMgr.Run();
547     }
548
549     if (status) {
550       return 0;
551     } else {
552       return 1;
553     }
554 }
555
556 /* EOF */