6ceba018dcbad4ac41cea6438a397e8cfbde1183
[reactos.git] / reactos / 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] [-P dir]\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 printf(" -P dir Files in the .dff are relative to this directory\n");
215 }
216
217 bool CCABManager::ParseCmdline(int argc, char* argv[])
218 /*
219 * FUNCTION: Parse command line arguments
220 * ARGUMENTS:
221 * argc = Number of arguments on command line
222 * argv = Pointer to list of command line arguments
223 * RETURNS:
224 * true if command line arguments was successfully parsed, false if not
225 */
226 {
227 int i;
228 bool ShowUsage;
229 bool FoundCabinet = false;
230
231 ShowUsage = (argc < 2);
232
233 for (i = 1; i < argc; i++) {
234 if (argv[i][0] == '-') {
235 switch (argv[i][1]) {
236 case 'a':
237 case 'A': ProcessAll = true; break;
238 case 'c':
239 case 'C': Mode = CM_MODE_CREATE; break;
240 case 'd':
241 case 'D': Mode = CM_MODE_DISPLAY; break;
242 case 'e':
243 case 'E': Mode = CM_MODE_EXTRACT; break;
244 case 'i':
245 case 'I': InfFileOnly = true; break;
246 case 'l':
247 case 'L':
248 if (argv[i][2] == 0) {
249 i++;
250 SetDestinationPath((char*)&argv[i][0]);
251 } else {
252 SetDestinationPath((char*)&argv[i][1]);
253 }
254 break;
255 case 'n':
256 case 'N': DontGenerateInf = true; break;
257 case 'R':
258 switch (argv[i][2]) {
259 case 'C': /* File to put in cabinet reserved area */
260 if (argv[i][3] == 0) {
261 i++;
262 if (!SetCabinetReservedFile((char*)&argv[i][0])) {
263 printf("Cannot open cabinet reserved area file.\n");
264 return false;
265 }
266 } else {
267 if (!SetCabinetReservedFile((char*)&argv[i][3])) {
268 printf("Cannot open cabinet reserved area file.\n");
269 return false;
270 }
271 }
272 break;
273 default:
274 printf("Bad parameter %s.\n", argv[i]);
275 return false;
276 }
277 break;
278 case 's':
279 case 'S': Mode = CM_MODE_CREATE_SIMPLE; break;
280 case 'P':
281 if (argv[i][2] == 0) {
282 i++;
283 SetFileRelativePath((char*)&argv[i][0]);
284 } else {
285 SetFileRelativePath((char*)&argv[i][1]);
286 }
287 break;
288 default:
289 printf("Bad parameter %s.\n", argv[i]);
290 return false;
291 }
292 } else {
293 if ((FoundCabinet) || (Mode == CM_MODE_CREATE)) {
294 /* FIXME: There may be many of these if Mode != CM_MODE_CREATE */
295 strcpy((char*)FileName, argv[i]);
296 } else {
297 SetCabinetName(argv[i]);
298 FoundCabinet = true;
299 }
300 }
301 }
302
303 if (ShowUsage) {
304 Usage();
305 return false;
306 }
307
308 /* FIXME */
309 SelectCodec(CAB_CODEC_MSZIP);
310
311 return true;
312 }
313
314
315 bool CCABManager::CreateCabinet()
316 /*
317 * FUNCTION: Create cabinet
318 */
319 {
320 unsigned long Status;
321
322 Status = Load((char*)&FileName);
323 if (Status != CAB_STATUS_SUCCESS) {
324 printf("Specified directive file could not be found: %s.\n", (char*)&FileName);
325 return false;
326 }
327
328 Status = Parse();
329
330 return (Status == CAB_STATUS_SUCCESS ? true : false);
331 }
332
333
334 bool CCABManager::CreateSimpleCabinet()
335 /*
336 * FUNCTION: Create cabinet
337 */
338 {
339 unsigned long Status;
340
341 Status = NewCabinet();
342 if (Status != CAB_STATUS_SUCCESS) {
343 DPRINT(MIN_TRACE, ("Cannot create cabinet (%d).\n", (unsigned int)Status));
344 return false;
345 }
346
347 Status = AddFile(FileName);
348 if (Status != CAB_STATUS_SUCCESS) {
349 DPRINT(MIN_TRACE, ("Cannot add file to cabinet (%d).\n", (unsigned int)Status));
350 return false;
351 }
352
353 Status = WriteDisk(false);
354 if (Status == CAB_STATUS_SUCCESS)
355 Status = CloseDisk();
356 if (Status != CAB_STATUS_SUCCESS) {
357 DPRINT(MIN_TRACE, ("Cannot write disk (%d).\n", (unsigned int)Status));
358 return false;
359 }
360
361 CloseCabinet();
362
363 return true;
364 }
365
366
367 bool CCABManager::DisplayCabinet()
368 /*
369 * FUNCTION: Display cabinet contents
370 */
371 {
372 CAB_SEARCH Search;
373 char Str[20];
374 unsigned long FileCount = 0;
375 unsigned long ByteCount = 0;
376
377 if (Open() == CAB_STATUS_SUCCESS) {
378 printf("Cabinet %s\n\n", GetCabinetName());
379
380 if (FindFirst("", &Search) == CAB_STATUS_SUCCESS) {
381 do {
382 if (Search.File->FileControlID != CAB_FILE_CONTINUED) {
383 printf("%s ", Date2Str((char*)&Str, Search.File->FileDate));
384 printf("%s ", Time2Str((char*)&Str, Search.File->FileTime));
385 printf("%s ", Attr2Str((char*)&Str, Search.File->Attributes));
386 sprintf(Str, "%lu", Search.File->FileSize);
387 printf("%s ", Pad(Str, ' ', 13));
388 printf("%s\n", Search.FileName);
389
390 FileCount++;
391 ByteCount += Search.File->FileSize;
392 }
393 } while (FindNext(&Search) == CAB_STATUS_SUCCESS);
394 }
395
396 if (FileCount > 0) {
397 if (FileCount == 1)
398 printf(" 1 file ");
399 else {
400 sprintf(Str, "%lu", FileCount);
401 printf(" %s files ", Pad(Str, ' ', 12));
402 }
403
404 if (ByteCount == 1)
405 printf(" 1 byte\n");
406 else {
407 sprintf(Str, "%lu", ByteCount);
408 printf("%s bytes\n", Pad(Str, ' ', 12));
409 }
410 } else {
411 /* There should be at least one file in a cabinet */
412 printf("No files in cabinet.");
413 }
414 return true;
415 } else
416 printf("Cannot open file: %s\n", GetCabinetName());
417
418 return false;
419 }
420
421
422 bool CCABManager::ExtractFromCabinet()
423 /*
424 * FUNCTION: Extract file(s) from cabinet
425 */
426 {
427 CAB_SEARCH Search;
428 unsigned long Status;
429
430 if (Open() == CAB_STATUS_SUCCESS) {
431 printf("Cabinet %s\n\n", GetCabinetName());
432
433 if (FindFirst("", &Search) == CAB_STATUS_SUCCESS) {
434 do {
435 switch (Status = ExtractFile(Search.FileName)) {
436 case CAB_STATUS_SUCCESS:
437 break;
438 case CAB_STATUS_INVALID_CAB:
439 printf("Cabinet contains errors.\n");
440 return false;
441 case CAB_STATUS_UNSUPPCOMP:
442 printf("Cabinet uses unsupported compression type.\n");
443 return false;
444 case CAB_STATUS_CANNOT_WRITE:
445 printf("You've run out of free space on the destination volume or the volume is damaged.\n");
446 return false;
447 default:
448 printf("Unspecified error code (%d).\n", (unsigned int)Status);
449 return false;
450 }
451 } while (FindNext(&Search) == CAB_STATUS_SUCCESS);
452 }
453 return true;
454 } else
455 printf("Cannot open file: %s.\n", GetCabinetName());
456
457 return false;
458 }
459
460
461 bool CCABManager::Run()
462 /*
463 * FUNCTION: Process cabinet
464 */
465 {
466 printf("ReactOS Cabinet Manager - Version %s\n\n", CM_VERSION);
467
468 switch (Mode) {
469 case CM_MODE_CREATE: return CreateCabinet(); break;
470 case CM_MODE_DISPLAY: return DisplayCabinet(); break;
471 case CM_MODE_EXTRACT: return ExtractFromCabinet(); break;
472 case CM_MODE_CREATE_SIMPLE: return CreateSimpleCabinet(); break;
473 default:
474 break;
475 }
476 return false;
477 }
478
479
480 /* Event handlers */
481
482 bool CCABManager::OnOverwrite(PCFFILE File,
483 char* FileName)
484 /*
485 * FUNCTION: Called when extracting a file and it already exists
486 * ARGUMENTS:
487 * File = Pointer to CFFILE for file being extracted
488 * Filename = Pointer to buffer with name of file (full path)
489 * RETURNS
490 * true if the file should be overwritten, false if not
491 */
492 {
493 if (Mode == CM_MODE_CREATE)
494 return true;
495
496 /* Always overwrite */
497 return true;
498 }
499
500
501 void CCABManager::OnExtract(PCFFILE File,
502 char* FileName)
503 /*
504 * FUNCTION: Called just before extracting a file
505 * ARGUMENTS:
506 * File = Pointer to CFFILE for file being extracted
507 * FileName = Pointer to buffer with name of file (full path)
508 */
509 {
510 printf("Extracting %s\n", GetFileName(FileName));
511 }
512
513
514
515 void CCABManager::OnDiskChange(char* CabinetName,
516 char* DiskLabel)
517 /*
518 * FUNCTION: Called when a new disk is to be processed
519 * ARGUMENTS:
520 * CabinetName = Pointer to buffer with name of cabinet
521 * DiskLabel = Pointer to buffer with label of disk
522 */
523 {
524 printf("\nChanging to cabinet %s - %s\n\n", CabinetName, DiskLabel);
525 }
526
527
528 void CCABManager::OnAdd(PCFFILE File,
529 char* FileName)
530 /*
531 * FUNCTION: Called just before adding a file to a cabinet
532 * ARGUMENTS:
533 * File = Pointer to CFFILE for file being added
534 * FileName = Pointer to buffer with name of file (full path)
535 */
536 {
537 printf("Adding %s\n", GetFileName(FileName));
538 }
539
540
541 int main(int argc, char * argv[])
542 /*
543 * FUNCTION: Main entry point
544 * ARGUMENTS:
545 * argc = Number of arguments on command line
546 * argv = Pointer to list of command line arguments
547 */
548 {
549 CCABManager CABMgr;
550 bool status = false;
551
552 if (CABMgr.ParseCmdline(argc, argv)) {
553 status = CABMgr.Run();
554 }
555
556 if (status) {
557 return 0;
558 } else {
559 return 1;
560 }
561 }
562
563 /* EOF */