per-module clean rules, make cabman more *nix/msys friendly
[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]\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, "%lu", 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, "%lu", FileCount);
392 printf(" %s files ", Pad(Str, ' ', 12));
393 }
394
395 if (ByteCount == 1)
396 printf(" 1 byte\n");
397 else {
398 sprintf(Str, "%lu", 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 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 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 if (Mode == CM_MODE_CREATE)
485 return true;
486
487 /* Always overwrite */
488 return true;
489 }
490
491
492 void CCABManager::OnExtract(PCFFILE File,
493 char* FileName)
494 /*
495 * FUNCTION: Called just before extracting a file
496 * ARGUMENTS:
497 * File = Pointer to CFFILE for file being extracted
498 * FileName = Pointer to buffer with name of file (full path)
499 */
500 {
501 printf("Extracting %s\n", GetFileName(FileName));
502 }
503
504
505
506 void CCABManager::OnDiskChange(char* CabinetName,
507 char* DiskLabel)
508 /*
509 * FUNCTION: Called when a new disk is to be processed
510 * ARGUMENTS:
511 * CabinetName = Pointer to buffer with name of cabinet
512 * DiskLabel = Pointer to buffer with label of disk
513 */
514 {
515 printf("\nChanging to cabinet %s - %s\n\n", CabinetName, DiskLabel);
516 }
517
518
519 void CCABManager::OnAdd(PCFFILE File,
520 char* FileName)
521 /*
522 * FUNCTION: Called just before adding a file to a cabinet
523 * ARGUMENTS:
524 * File = Pointer to CFFILE for file being added
525 * FileName = Pointer to buffer with name of file (full path)
526 */
527 {
528 printf("Adding %s\n", GetFileName(FileName));
529 }
530
531
532 int main(int argc, char * argv[])
533 /*
534 * FUNCTION: Main entry point
535 * ARGUMENTS:
536 * argc = Number of arguments on command line
537 * argv = Pointer to list of command line arguments
538 */
539 {
540 CCABManager CABMgr;
541 bool status = false;
542
543 if (CABMgr.ParseCmdline(argc, argv)) {
544 status = CABMgr.Run();
545 }
546
547 if (status) {
548 return 0;
549 } else {
550 return 1;
551 }
552 }
553
554 /* EOF */