branch update for HEAD-2003050101
[reactos.git] / apps / utils / cabman / main.cpp
1 /*
2  * COPYRIGHT:   See COPYING in the top level directory
3  * PROJECT:     ReactOS cabinet manager
4  * FILE:        apps/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  */
10 #include <stdlib.h>
11 #include <stdarg.h>
12 #include <string.h>
13 #include <stdio.h>
14 #include <conio.h>
15 #include <windows.h>
16 #include <reactos/buildno.h>
17 #include "cabman.h"
18
19
20 #ifdef DBG
21
22 DWORD DebugTraceLevel = MIN_TRACE;
23 //DWORD DebugTraceLevel = MID_TRACE;
24 //DWORD DebugTraceLevel = MAX_TRACE;
25
26 #endif /* DBG */
27
28
29 #define CM_VERSION  KERNEL_VERSION_STR
30
31
32 LPTSTR Pad(LPTSTR Str, CHAR PadChar, UINT Length)
33 /*
34  * FUNCTION: Pads a string with a character to make a given length
35  * ARGUMENTS:
36  *     Str     = Pointer to string to pad
37  *     PadChar = Character to pad with
38  *     Length  = Disired length of string
39  * RETURNS:
40  *     Pointer to string
41  * NOTES:
42  *     Str must be at least Length + 1 bytes
43  */
44 {
45     UINT Len;
46
47     Len = lstrlen(Str);
48
49     if (Len < Length) {
50         memcpy(&Str[Length - Len], Str, Len + 1);
51         memset(Str, PadChar, Length - Len);
52     }
53     return Str;
54 }
55
56
57 LPTSTR Date2Str(LPTSTR Str, WORD Date)
58 /*
59  * FUNCTION: Converts a DOS style date to a string
60  * ARGUMENTS:
61  *     Str  = Pointer to destination string
62  *     Date = DOS style date
63  * RETURNS:
64  *     Pointer to string
65  */
66 {
67     DWORD dw;
68
69     /* Month */
70     Str[0] = (CHAR)('0' + ((Date & 0x01E0) >> 5) / 10);
71     Str[1] = (CHAR)('0' + ((Date & 0x01E0) >> 5) % 10);
72     Str[2] = '-';
73     /* Day */
74     Str[3] = (CHAR)('0' + (Date & 0x001F) / 10);
75     Str[4] = (CHAR)('0' + (Date & 0x001F) % 10);
76     Str[5] = '-';
77     /* Year */
78     dw = 1980 + ((Date & 0xFE00) >> 9);
79     Str[6] = (CHAR)('0' + dw / 1000); dw %= 1000;
80     Str[7] = (CHAR)('0' + dw / 100);  dw %= 100;
81     Str[8] = (CHAR)('0' + dw / 10);   dw %= 10;
82     Str[9] = (CHAR)('0' + dw % 10);
83     Str[10] = '\0';
84     return Str;
85 }
86
87
88 LPTSTR Time2Str(LPTSTR Str, WORD Time)
89 /*
90  * FUNCTION: Converts a DOS style time to a string
91  * ARGUMENTS:
92  *     Str  = Pointer to destination string
93  *     Time = DOS style time
94  * RETURNS:
95  *     Pointer to string
96  */
97 {
98     BOOL PM;
99     DWORD Hour;
100     DWORD dw;
101
102     Hour = ((Time & 0xF800) >> 11);
103     PM = (Hour >= 12);
104     Hour %= 12;
105     if (Hour == 0)
106         Hour = 12;
107
108     if (Hour >= 10)
109         Str[0] = (CHAR)('0' + Hour / 10);
110     else Str[0] = ' ';
111     Str[1] = (CHAR)('0' + Hour % 10);
112     Str[2] = ':';
113     /* Minute */
114     Str[3] = (CHAR)('0' + ((Time & 0x07E0) >> 5) / 10);
115     Str[4] = (CHAR)('0' + ((Time & 0x07E0) >> 5) % 10);
116     Str[5] = ':';
117     /* Second */
118     dw = 2 * (Time & 0x001F);
119     Str[6] = (CHAR)('0' + dw / 10);
120     Str[7] = (CHAR)('0' + dw % 10);
121
122     Str[8] = PM? 'p' : 'a';
123     Str[9] = '\0';
124     return Str;
125 }
126
127
128 LPTSTR Attr2Str(LPTSTR Str, WORD Attr)
129 /*
130  * FUNCTION: Converts attributes to a string
131  * ARGUMENTS:
132  *     Str  = Pointer to destination string
133  *     Attr = Attributes
134  * RETURNS:
135  *     Pointer to string
136  */
137 {
138     /* Archive */
139     if (Attr & CAB_ATTRIB_ARCHIVE)
140         Str[0] = 'A';
141     else
142         Str[0] = '-';
143
144     /* Hidden */
145     if (Attr & CAB_ATTRIB_HIDDEN)
146         Str[1] = 'H';
147     else
148         Str[1] = '-';
149
150     /* Read only */
151     if (Attr & CAB_ATTRIB_READONLY)
152         Str[2] = 'R';
153     else
154         Str[2] = '-';
155
156     /* System */
157     if (Attr & CAB_ATTRIB_SYSTEM)
158         Str[3] = 'S';
159     else
160         Str[3] = '-';
161
162     Str[4] = '\0';
163     return Str;
164 }
165
166
167 /* CCABManager */
168
169 CCABManager::CCABManager()
170 /*
171  * FUNCTION: Default constructor
172  */
173 {
174     ProcessAll        = FALSE;
175     Mode              = CM_MODE_DISPLAY;
176     PromptOnOverwrite = TRUE;
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 - Version %s\n\n", CM_VERSION);
194     printf("CABMAN [/D | /E] [/A] [/L dir] [/Y] cabinet [filename ...]\n");
195     printf("CABMAN /C dirfile\n");
196     printf("  cabinet   Cabinet file.\n");
197     printf("  filename  Name of the file to extract from the cabinet.\n");
198     printf("            Wild cards and multiple filenames\n");
199     printf("            (separated by blanks) may be used.\n\n");
200     printf("  dirfile   Name of the directive file to use.\n\n");
201     
202
203     printf("  /A        Process ALL cabinets. Follows cabinet chain\n");
204     printf("            starting in first cabinet mentioned.\n");
205     printf("  /C        Create cabinet.\n");
206     printf("  /D        Display cabinet directory.\n");
207     printf("  /E        Extract files from cabinet.\n");
208     printf("  /L dir    Location to place extracted files\n");
209     printf("            (default is current directory).\n");
210     printf("  /Y        Do not prompt before overwriting an existing file.\n\n");
211 }
212
213
214 BOOL CCABManager::ParseCmdline(INT argc, PCHAR argv[])
215 /*
216  * FUNCTION: Parse command line arguments
217  * ARGUMENTS:
218  *     argc = Number of arguments on command line
219  *     argv = Pointer to list of command line arguments
220  * RETURNS:
221  *    TRUE if command line arguments was successfully parsed, false if not
222  */
223 {
224     INT i;
225         BOOL ShowUsage;
226     BOOL FoundCabinet = FALSE;
227         
228     ShowUsage = (argc < 2);
229
230         for (i = 1; i < argc; i++) {
231                 if (argv[i][0] == '/') {
232                         switch (argv[i][1]) {
233             case 'a':
234             case 'A': ProcessAll = TRUE; break;
235             case 'c':
236             case 'C': Mode = CM_MODE_CREATE; break;
237             case 'd':
238             case 'D': Mode = CM_MODE_DISPLAY; break;
239             case 'e':
240                         case 'E': Mode = CM_MODE_EXTRACT; break;
241             case 'l':
242                         case 'L':
243                 if (argv[i][2] == ' ') {
244                     i++;
245                     SetDestinationPath((LPTSTR)&argv[i][0]);
246                 } else
247                     SetDestinationPath((LPTSTR)&argv[i][1]);
248                 break;
249             case 'y':
250                         case 'Y': PromptOnOverwrite = FALSE; break;
251                         default:
252                 printf("Bad parameter %s.\n", argv[i]);
253                 return FALSE;
254                         }
255                 } else {
256                         if ((FoundCabinet) || (Mode == CM_MODE_CREATE)) {
257                 /* FIXME: There may be many of these if Mode != CM_MODE_CREATE */
258                 lstrcpy((LPTSTR)FileName, argv[i]);
259             } else {
260                 SetCabinetName(argv[i]);
261                 FoundCabinet = TRUE;
262             }
263                 }
264     }
265
266         if (ShowUsage) {
267                 Usage();
268                 return FALSE;
269         }
270
271     /* FIXME */
272     SelectCodec(CAB_CODEC_MSZIP);
273
274         return TRUE;
275 }
276
277
278 VOID CCABManager::CreateCabinet()
279 /*
280  * FUNCTION: Create cabinet
281  */
282 {
283     ULONG Status;
284
285     Status = Load((LPTSTR)&FileName);
286     if (Status != CAB_STATUS_SUCCESS) {
287         printf("Specified directive file could not be found: %s.\n", (LPTSTR)&FileName);
288         return;
289     }
290
291     Parse();
292 }
293
294
295 VOID CCABManager::DisplayCabinet()
296 /*
297  * FUNCTION: Display cabinet contents
298  */
299 {
300     CAB_SEARCH Search;
301     TCHAR Str[20];
302     DWORD FileCount = 0;
303     DWORD ByteCount = 0;
304
305     if (Open() == CAB_STATUS_SUCCESS) {
306         printf("Cabinet %s\n\n", GetCabinetName());
307
308         if (FindFirst("", &Search) == CAB_STATUS_SUCCESS) {
309             do {
310                 if (Search.File->FileControlID != CAB_FILE_CONTINUED) {
311                     printf("%s ", Date2Str((LPTSTR)&Str, Search.File->FileDate));
312                     printf("%s ", Time2Str((LPTSTR)&Str, Search.File->FileTime));
313                     printf("%s ", Attr2Str((LPTSTR)&Str, Search.File->Attributes));
314                     printf("%s ", Pad(itoa(Search.File->FileSize, (LPTSTR)&Str, 10), ' ', 13));
315                     printf("%s\n", Search.FileName);
316
317                     FileCount++;
318                     ByteCount += Search.File->FileSize;
319                 }
320             } while (FindNext(&Search) == CAB_STATUS_SUCCESS);
321         }
322
323         if (FileCount > 0) {
324             if (FileCount == 1)
325                 printf("                 1 file    ");
326             else
327                 printf("      %s files   ", 
328                     Pad(itoa(FileCount, (LPTSTR)&Str, 10), ' ', 12));
329
330             if (ByteCount == 1)
331                 printf("           1 byte\n");
332             else
333                 printf("%s bytes\n",
334                     Pad(itoa(ByteCount, (LPTSTR)&Str, 10), ' ', 12));
335         } else {
336             /* There should be at least one file in a cabinet */
337             printf("No files in cabinet.");
338         }
339     } else
340         printf("Cannot not open file: %s\n", GetCabinetName());
341 }
342
343
344 VOID CCABManager::ExtractFromCabinet()
345 /*
346  * FUNCTION: Extract file(s) from cabinet
347  */
348 {
349     CAB_SEARCH Search;
350     ULONG Status;
351
352     if (Open() == CAB_STATUS_SUCCESS) {
353         printf("Cabinet %s\n\n", GetCabinetName());
354
355         if (FindFirst("", &Search) == CAB_STATUS_SUCCESS) {
356             do {
357                 switch (Status = ExtractFile(Search.FileName)) {
358                     case CAB_STATUS_SUCCESS:
359                         break;
360                     case CAB_STATUS_INVALID_CAB:
361                         printf("Cabinet contains errors.\n");
362                         return;
363                     case CAB_STATUS_UNSUPPCOMP:
364                         printf("Cabinet uses unsupported compression type.\n");
365                         return;
366                     case CAB_STATUS_CANNOT_WRITE:
367                         printf("You've run out of free space on the destination volume or the volume is damaged.\n");
368                         return;
369                     default:
370                         printf("Unspecified error code (%d).\n", (UINT)Status);
371                         return;
372                 }
373             } while (FindNext(&Search) == CAB_STATUS_SUCCESS);
374         }
375     } else
376         printf("Cannot not open file: %s.\n", GetCabinetName());
377 }
378
379
380 VOID CCABManager::Run()
381 /*
382  * FUNCTION: Process cabinet
383  */
384 {
385     printf("ReactOS Cabinet Manager - Version %s\n\n", CM_VERSION);
386
387     switch (Mode) {
388     case CM_MODE_CREATE:  CreateCabinet(); break;
389     case CM_MODE_DISPLAY: DisplayCabinet(); break;
390     case CM_MODE_EXTRACT: ExtractFromCabinet(); break;
391     default:
392         break;
393     }
394     printf("\n");
395 }
396
397
398 /* Event handlers */
399
400 BOOL CCABManager::OnOverwrite(PCFFILE File,
401                               LPTSTR FileName)
402 /*
403  * FUNCTION: Called when extracting a file and it already exists
404  * ARGUMENTS:
405  *     File     = Pointer to CFFILE for file being extracted
406  *     Filename = Pointer to buffer with name of file (full path)
407  * RETURNS
408  *     TRUE if the file should be overwritten, FALSE if not
409  */
410 {
411     TCHAR ch;
412
413     if (Mode == CM_MODE_CREATE)
414         return TRUE;
415
416     /* Should we prompt on overwrite? */
417     if (!PromptOnOverwrite)
418         return TRUE;
419
420     /* Ask if file should be overwritten */
421     printf("Overwrite %s (Yes/No/All)? ", GetFileName(FileName));
422     
423     for (;;) {
424         ch = _getch();
425         switch (ch) {
426             case 'Y':
427             case 'y': printf("%c\n", ch); return TRUE;
428             case 'N':
429             case 'n': printf("%c\n", ch); return FALSE;
430             case 'A':
431             case 'a': printf("%c\n", ch); PromptOnOverwrite = FALSE; return TRUE;
432         }
433     }
434 }
435
436
437 VOID CCABManager::OnExtract(PCFFILE File,
438                             LPTSTR FileName)
439 /*
440  * FUNCTION: Called just before extracting a file
441  * ARGUMENTS:
442  *     File     = Pointer to CFFILE for file being extracted
443  *     FileName = Pointer to buffer with name of file (full path)
444  */
445 {
446     printf("Extracting %s\n", GetFileName(FileName));
447 }
448
449
450
451 VOID CCABManager::OnDiskChange(LPTSTR CabinetName,
452                                LPTSTR DiskLabel)
453 /*
454  * FUNCTION: Called when a new disk is to be processed
455  * ARGUMENTS:
456  *     CabinetName = Pointer to buffer with name of cabinet
457  *     DiskLabel   = Pointer to buffer with label of disk
458  */
459 {
460     printf("\nChanging to cabinet %s - %s\n\n", CabinetName, DiskLabel);
461 }
462
463
464 VOID CCABManager::OnAdd(PCFFILE File,
465                         LPTSTR FileName)
466 /*
467  * FUNCTION: Called just before adding a file to a cabinet
468  * ARGUMENTS:
469  *     File     = Pointer to CFFILE for file being added
470  *     FileName = Pointer to buffer with name of file (full path)
471  */
472 {
473     printf("Adding %s\n", GetFileName(FileName));
474 }
475
476
477 int main(int argc, char * argv[])
478 /*
479  * FUNCTION: Main entry point
480  * ARGUMENTS:
481  *     argc = Number of arguments on command line
482  *     argv = Pointer to list of command line arguments
483  */
484 {
485     CCABManager CABMgr;
486
487     if (CABMgr.ParseCmdline(argc, argv))
488         CABMgr.Run();
489
490     return 0;
491 }
492
493 /* EOF */