Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/programs/cabarc/cabarc.c
4389 views
1
/*
2
* Tool to manipulate cabinet files
3
*
4
* Copyright 2011 Alexandre Julliard
5
*
6
* This library is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU Lesser General Public
8
* License as published by the Free Software Foundation; either
9
* version 2.1 of the License, or (at your option) any later version.
10
*
11
* This library is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
* Lesser General Public License for more details.
15
*
16
* You should have received a copy of the GNU Lesser General Public
17
* License along with this library; if not, write to the Free Software
18
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19
*/
20
21
#include <stdio.h>
22
#include <stdlib.h>
23
#include <fcntl.h>
24
#include <io.h>
25
#include <share.h>
26
27
#define WIN32_LEAN_AND_MEAN
28
#include "windows.h"
29
#include "fci.h"
30
#include "fdi.h"
31
32
#include "wine/debug.h"
33
34
WINE_DEFAULT_DEBUG_CHANNEL(cabarc);
35
36
/* command-line options */
37
static int opt_cabinet_size = CB_MAX_DISK;
38
static int opt_cabinet_id;
39
static int opt_compression = tcompTYPE_MSZIP;
40
static BOOL opt_recurse;
41
static BOOL opt_preserve_paths;
42
static int opt_reserve_space;
43
static int opt_verbose;
44
static char *opt_cab_file;
45
static WCHAR *opt_dest_dir;
46
static WCHAR **opt_files;
47
48
static void * CDECL cab_alloc( ULONG size )
49
{
50
return malloc( size );
51
}
52
53
static void CDECL cab_free( void *ptr )
54
{
55
free( ptr );
56
}
57
58
static WCHAR *strdupAtoW( UINT cp, const char *str )
59
{
60
WCHAR *ret = NULL;
61
if (str)
62
{
63
DWORD len = MultiByteToWideChar( cp, 0, str, -1, NULL, 0 );
64
if ((ret = malloc( len * sizeof(WCHAR) )))
65
MultiByteToWideChar( cp, 0, str, -1, ret, len );
66
}
67
return ret;
68
}
69
70
static char *strdupWtoA( UINT cp, const WCHAR *str )
71
{
72
char *ret = NULL;
73
if (str)
74
{
75
DWORD len = WideCharToMultiByte( cp, 0, str, -1, NULL, 0, NULL, NULL );
76
if ((ret = malloc( len )))
77
WideCharToMultiByte( cp, 0, str, -1, ret, len, NULL, NULL );
78
}
79
return ret;
80
}
81
82
/* format a cabinet name by replacing the '*' wildcard by the cabinet id */
83
static BOOL format_cab_name( char *dest, int id, const char *name )
84
{
85
const char *num = strchr( name, '*' );
86
int len;
87
88
if (!num)
89
{
90
if (id == 1)
91
{
92
strcpy( dest, name );
93
return TRUE;
94
}
95
WINE_MESSAGE( "cabarc: Cabinet name must contain a '*' character\n" );
96
return FALSE;
97
}
98
len = num - name;
99
memcpy( dest, name, len );
100
len += sprintf( dest + len, "%u", id );
101
lstrcpynA( dest + len, num + 1, CB_MAX_CABINET_NAME - len );
102
return TRUE;
103
}
104
105
static int CDECL fci_file_placed( CCAB *cab, char *file, LONG size, BOOL continuation, void *ptr )
106
{
107
if (!continuation && opt_verbose) printf( "adding %s\n", file );
108
return 0;
109
}
110
111
static INT_PTR CDECL fci_open( char *file, int oflag, int pmode, int *err, void *ptr )
112
{
113
DWORD creation = 0, sharing = 0;
114
int ioflag = 0;
115
HANDLE handle;
116
117
switch (oflag & _O_ACCMODE)
118
{
119
case _O_RDONLY: ioflag |= GENERIC_READ; break;
120
case _O_WRONLY: ioflag |= GENERIC_WRITE; break;
121
case _O_RDWR: ioflag |= GENERIC_READ | GENERIC_WRITE; break;
122
}
123
124
if (oflag & _O_CREAT)
125
{
126
if (oflag & _O_EXCL) creation = CREATE_NEW;
127
else if (oflag & _O_TRUNC) creation = CREATE_ALWAYS;
128
else creation = OPEN_ALWAYS;
129
}
130
else
131
{
132
if (oflag & _O_TRUNC) creation = TRUNCATE_EXISTING;
133
else creation = OPEN_EXISTING;
134
}
135
136
switch (pmode & 0x70)
137
{
138
case _SH_DENYRW: sharing = 0; break;
139
case _SH_DENYWR: sharing = FILE_SHARE_READ; break;
140
case _SH_DENYRD: sharing = FILE_SHARE_WRITE; break;
141
default: sharing = FILE_SHARE_READ | FILE_SHARE_WRITE; break;
142
}
143
144
handle = CreateFileA( file, ioflag, sharing, NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL );
145
if (handle == INVALID_HANDLE_VALUE) *err = GetLastError();
146
return (INT_PTR)handle;
147
}
148
149
static UINT CDECL fci_read( INT_PTR hf, void *pv, UINT cb, int *err, void *ptr )
150
{
151
DWORD num_read;
152
153
if (!ReadFile( (HANDLE)hf, pv, cb, &num_read, NULL ))
154
{
155
*err = GetLastError();
156
return -1;
157
}
158
return num_read;
159
}
160
161
static UINT CDECL fci_write( INT_PTR hf, void *pv, UINT cb, int *err, void *ptr )
162
{
163
DWORD written;
164
165
if (!WriteFile( (HANDLE) hf, pv, cb, &written, NULL ))
166
{
167
*err = GetLastError();
168
return -1;
169
}
170
return written;
171
}
172
173
static int CDECL fci_close( INT_PTR hf, int *err, void *ptr )
174
{
175
if (!CloseHandle( (HANDLE)hf ))
176
{
177
*err = GetLastError();
178
return -1;
179
}
180
return 0;
181
}
182
183
static LONG CDECL fci_lseek( INT_PTR hf, LONG dist, int seektype, int *err, void *ptr )
184
{
185
DWORD ret;
186
187
ret = SetFilePointer( (HANDLE)hf, dist, NULL, seektype );
188
if (ret == INVALID_SET_FILE_POINTER && GetLastError())
189
{
190
*err = GetLastError();
191
return -1;
192
}
193
return ret;
194
}
195
196
static int CDECL fci_delete( char *file, int *err, void *ptr )
197
{
198
if (!DeleteFileA( file ))
199
{
200
*err = GetLastError();
201
return -1;
202
}
203
return 0;
204
}
205
206
static BOOL CDECL fci_get_temp( char *name, int size, void *ptr )
207
{
208
char path[MAX_PATH];
209
210
if (!GetTempPathA( MAX_PATH, path )) return FALSE;
211
if (!GetTempFileNameA( path, "cab", 0, name )) return FALSE;
212
DeleteFileA( name );
213
return TRUE;
214
}
215
216
static BOOL CDECL fci_get_next_cab( CCAB *cab, ULONG prev_size, void *ptr )
217
{
218
return format_cab_name( cab->szCab, cab->iCab + 1, opt_cab_file );
219
}
220
221
static LONG CDECL fci_status( UINT type, ULONG cb1, ULONG cb2, void *ptr )
222
{
223
return 0;
224
}
225
226
static INT_PTR CDECL fci_get_open_info( char *name, USHORT *date, USHORT *time,
227
USHORT *attribs, int *err, void *ptr )
228
{
229
HANDLE handle;
230
BY_HANDLE_FILE_INFORMATION info;
231
WCHAR *p, *nameW = strdupAtoW( CP_UTF8, name );
232
233
handle = CreateFileW( nameW, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
234
NULL, OPEN_EXISTING, 0, NULL );
235
if (handle == INVALID_HANDLE_VALUE)
236
{
237
*err = GetLastError();
238
WINE_ERR( "failed to open %s: error %u\n", wine_dbgstr_w(nameW), *err );
239
free( nameW );
240
return -1;
241
}
242
if (!GetFileInformationByHandle( handle, &info ))
243
{
244
*err = GetLastError();
245
CloseHandle( handle );
246
free( nameW );
247
return -1;
248
}
249
FileTimeToDosDateTime( &info.ftLastWriteTime, date, time );
250
*attribs = info.dwFileAttributes & (_A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_ARCH);
251
for (p = nameW; *p; p++) if (*p >= 0x80) break;
252
if (*p) *attribs |= _A_NAME_IS_UTF;
253
free( nameW );
254
return (INT_PTR)handle;
255
}
256
257
static INT_PTR CDECL fdi_open( char *file, int oflag, int pmode )
258
{
259
int err;
260
return fci_open( file, oflag, pmode, &err, NULL );
261
}
262
263
static UINT CDECL fdi_read( INT_PTR hf, void *pv, UINT cb )
264
{
265
int err;
266
return fci_read( hf, pv, cb, &err, NULL );
267
}
268
269
static UINT CDECL fdi_write( INT_PTR hf, void *pv, UINT cb )
270
{
271
int err;
272
return fci_write( hf, pv, cb, &err, NULL );
273
}
274
275
static int CDECL fdi_close( INT_PTR hf )
276
{
277
int err;
278
return fci_close( hf, &err, NULL );
279
}
280
281
static LONG CDECL fdi_lseek( INT_PTR hf, LONG dist, int whence )
282
{
283
int err;
284
return fci_lseek( hf, dist, whence, &err, NULL );
285
}
286
287
288
/* create directories leading to a given file */
289
static void create_directories( const WCHAR *name )
290
{
291
WCHAR *path, *p;
292
293
/* create the directory/directories */
294
path = wcsdup( name );
295
296
p = wcschr(path, '\\');
297
while (p != NULL)
298
{
299
*p = 0;
300
if (!CreateDirectoryW( path, NULL ))
301
WINE_TRACE("Couldn't create directory %s - error: %ld\n", wine_dbgstr_w(path), GetLastError());
302
*p = '\\';
303
p = wcschr(p+1, '\\');
304
}
305
free( path );
306
}
307
308
/* check if file name matches against one of the files specification */
309
static BOOL match_files( const WCHAR *name )
310
{
311
int i;
312
313
if (!*opt_files) return TRUE;
314
for (i = 0; opt_files[i]; i++)
315
{
316
unsigned int len = lstrlenW( opt_files[i] );
317
/* FIXME: do smarter matching, and wildcards */
318
if (!len) continue;
319
if (wcsnicmp( name, opt_files[i], len )) continue;
320
if (opt_files[i][len - 1] == '\\' || !name[len] || name[len] == '\\') return TRUE;
321
}
322
return FALSE;
323
}
324
325
static INT_PTR CDECL list_notify( FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin )
326
{
327
WCHAR *nameW;
328
329
switch (fdint)
330
{
331
case fdintCABINET_INFO:
332
return 0;
333
case fdintCOPY_FILE:
334
nameW = strdupAtoW( (pfdin->attribs & _A_NAME_IS_UTF) ? CP_UTF8 : CP_ACP, pfdin->psz1 );
335
if (match_files( nameW ))
336
{
337
if (opt_verbose)
338
{
339
WCHAR attrs[] = L"rxash";
340
if (!(pfdin->attribs & _A_RDONLY)) attrs[0] = '-';
341
if (!(pfdin->attribs & _A_EXEC)) attrs[1] = '-';
342
if (!(pfdin->attribs & _A_ARCH)) attrs[2] = '-';
343
if (!(pfdin->attribs & _A_SYSTEM)) attrs[3] = '-';
344
if (!(pfdin->attribs & _A_HIDDEN)) attrs[4] = '-';
345
wprintf( L" %s %9u %04u/%02u/%02u %02u:%02u:%02u ", attrs, pfdin->cb,
346
(pfdin->date >> 9) + 1980, (pfdin->date >> 5) & 0x0f, pfdin->date & 0x1f,
347
pfdin->time >> 11, (pfdin->time >> 5) & 0x3f, (pfdin->time & 0x1f) * 2 );
348
}
349
wprintf( L"%s\n", nameW );
350
}
351
free( nameW );
352
return 0;
353
default:
354
WINE_FIXME( "Unexpected notification type %d.\n", fdint );
355
return 0;
356
}
357
}
358
359
static int list_cabinet( char *cab_dir )
360
{
361
ERF erf;
362
int ret = 0;
363
HFDI fdi = FDICreate( cab_alloc, cab_free, fdi_open, fdi_read,
364
fdi_write, fdi_close, fdi_lseek, cpuUNKNOWN, &erf );
365
366
if (!FDICopy( fdi, opt_cab_file, cab_dir, 0, list_notify, NULL, NULL )) ret = GetLastError();
367
FDIDestroy( fdi );
368
return ret;
369
}
370
371
static INT_PTR CDECL extract_notify( FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin )
372
{
373
WCHAR *file, *nameW, *path = NULL;
374
INT_PTR ret;
375
376
switch (fdint)
377
{
378
case fdintCABINET_INFO:
379
return 0;
380
381
case fdintCOPY_FILE:
382
nameW = strdupAtoW( (pfdin->attribs & _A_NAME_IS_UTF) ? CP_UTF8 : CP_ACP, pfdin->psz1 );
383
if (opt_preserve_paths)
384
{
385
file = nameW;
386
while (*file == '\\') file++; /* remove leading backslashes */
387
}
388
else
389
{
390
if ((file = wcsrchr( nameW, '\\' ))) file++;
391
else file = nameW;
392
}
393
394
if (opt_dest_dir)
395
{
396
path = malloc( (wcslen(opt_dest_dir) + wcslen(file) + 1) * sizeof(WCHAR) );
397
lstrcpyW( path, opt_dest_dir );
398
lstrcatW( path, file );
399
}
400
else path = file;
401
402
if (match_files( file ))
403
{
404
if (opt_verbose) wprintf( L"extracting %s\n", path );
405
create_directories( path );
406
/* FIXME: check for existing file and overwrite mode */
407
ret = (INT_PTR)CreateFileW( path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
408
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
409
}
410
else ret = 0;
411
412
free( nameW );
413
if (path != file) free( path );
414
return ret;
415
416
case fdintCLOSE_FILE_INFO:
417
CloseHandle( (HANDLE)pfdin->hf );
418
return 0;
419
420
case fdintNEXT_CABINET:
421
WINE_TRACE("Next cab: status %u, path '%s', file '%s'\n", pfdin->fdie, pfdin->psz3, pfdin->psz1);
422
return pfdin->fdie == FDIERROR_NONE ? 0 : -1;
423
424
case fdintENUMERATE:
425
return 0;
426
427
default:
428
WINE_FIXME( "Unexpected notification type %d.\n", fdint );
429
return 0;
430
}
431
}
432
433
static int extract_cabinet( char *cab_dir )
434
{
435
ERF erf;
436
int ret = 0;
437
HFDI fdi = FDICreate( cab_alloc, cab_free, fdi_open, fdi_read,
438
fdi_write, fdi_close, fdi_lseek, cpuUNKNOWN, &erf );
439
440
if (!FDICopy( fdi, opt_cab_file, cab_dir, 0, extract_notify, NULL, NULL ))
441
{
442
ret = GetLastError();
443
WINE_WARN("FDICopy() failed: code %u\n", ret);
444
}
445
FDIDestroy( fdi );
446
return ret;
447
}
448
449
static BOOL add_file( HFCI fci, WCHAR *name )
450
{
451
BOOL ret;
452
char *filename, *path = strdupWtoA( CP_UTF8, name );
453
454
if (!opt_preserve_paths)
455
{
456
if ((filename = strrchr( path, '\\' ))) filename++;
457
else filename = path;
458
}
459
else
460
{
461
filename = path;
462
while (*filename == '\\') filename++; /* remove leading backslashes */
463
}
464
ret = FCIAddFile( fci, path, filename, FALSE,
465
fci_get_next_cab, fci_status, fci_get_open_info, opt_compression );
466
free( path );
467
return ret;
468
}
469
470
static BOOL add_directory( HFCI fci, WCHAR *dir )
471
{
472
WCHAR *p, *buffer;
473
HANDLE handle;
474
WIN32_FIND_DATAW data;
475
BOOL ret = TRUE;
476
477
if (!(buffer = malloc( (wcslen(dir) + MAX_PATH + 2) * sizeof(WCHAR) ))) return FALSE;
478
lstrcpyW( buffer, dir );
479
p = buffer + lstrlenW( buffer );
480
if (p > buffer && p[-1] != '\\') *p++ = '\\';
481
lstrcpyW( p, L"*" );
482
483
if ((handle = FindFirstFileW( buffer, &data )) != INVALID_HANDLE_VALUE)
484
{
485
do
486
{
487
if (data.cFileName[0] == '.' && !data.cFileName[1]) continue;
488
if (data.cFileName[0] == '.' && data.cFileName[1] == '.' && !data.cFileName[2]) continue;
489
if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) continue;
490
491
lstrcpyW( p, data.cFileName );
492
if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
493
ret = add_directory( fci, buffer );
494
else
495
ret = add_file( fci, buffer );
496
if (!ret) break;
497
} while (FindNextFileW( handle, &data ));
498
FindClose( handle );
499
}
500
free( buffer );
501
return TRUE;
502
}
503
504
static BOOL add_file_or_directory( HFCI fci, WCHAR *name )
505
{
506
DWORD attr = GetFileAttributesW( name );
507
508
if (attr == INVALID_FILE_ATTRIBUTES)
509
{
510
WINE_MESSAGE( "cannot open %s\n", wine_dbgstr_w(name) );
511
return FALSE;
512
}
513
if (attr & FILE_ATTRIBUTE_DIRECTORY)
514
{
515
if (opt_recurse) return add_directory( fci, name );
516
WINE_MESSAGE( "cabarc: Cannot add %s, it's a directory (use -r for recursive add)\n",
517
wine_dbgstr_w(name) );
518
return FALSE;
519
}
520
return add_file( fci, name );
521
}
522
523
static int new_cabinet( char *cab_dir )
524
{
525
WCHAR **file;
526
ERF erf;
527
BOOL ret = FALSE;
528
HFCI fci;
529
CCAB cab;
530
531
cab.cb = opt_cabinet_size;
532
cab.cbFolderThresh = CB_MAX_DISK;
533
cab.cbReserveCFHeader = opt_reserve_space;
534
cab.cbReserveCFFolder = 0;
535
cab.cbReserveCFData = 0;
536
cab.iCab = 0;
537
cab.iDisk = 0;
538
cab.setID = opt_cabinet_id;
539
cab.szDisk[0] = 0;
540
541
strcpy( cab.szCabPath, cab_dir );
542
strcat( cab.szCabPath, "\\" );
543
format_cab_name( cab.szCab, 1, opt_cab_file );
544
545
fci = FCICreate( &erf, fci_file_placed, cab_alloc, cab_free,fci_open, fci_read,
546
fci_write, fci_close, fci_lseek, fci_delete, fci_get_temp, &cab, NULL );
547
548
for (file = opt_files; *file; file++)
549
{
550
if (!lstrcmpW( *file, L"+" ))
551
FCIFlushFolder( fci, fci_get_next_cab, fci_status );
552
else
553
if (!(ret = add_file_or_directory( fci, *file ))) break;
554
}
555
556
if (ret)
557
{
558
if (!(ret = FCIFlushCabinet( fci, FALSE, fci_get_next_cab, fci_status )))
559
WINE_MESSAGE( "cabarc: Failed to create cabinet %s\n", wine_dbgstr_a(opt_cab_file) );
560
}
561
FCIDestroy( fci );
562
return !ret;
563
}
564
565
static void usage( void )
566
{
567
WINE_MESSAGE(
568
"Usage: cabarc [options] command file.cab [files...] [dest_dir\\]\n"
569
"\nCommands:\n"
570
" L List the contents of the cabinet\n"
571
" N Create a new cabinet\n"
572
" X Extract files from the cabinet into dest_dir\n"
573
"\nOptions:\n"
574
" -d size Set maximum disk size\n"
575
" -h Display this help\n"
576
" -i id Set cabinet id\n"
577
" -m type Set compression type (mszip|none)\n"
578
" -p Preserve directory names\n"
579
" -r Recurse into directories\n"
580
" -s size Reserve space in the cabinet header\n"
581
" -v More verbose output\n" );
582
}
583
584
int __cdecl wmain( int argc, WCHAR *argv[] )
585
{
586
WCHAR *p, *command;
587
char buffer[MAX_PATH];
588
char filename[MAX_PATH];
589
char *cab_file, *file_part;
590
int i;
591
592
while (argv[1] && argv[1][0] == '-')
593
{
594
switch (argv[1][1])
595
{
596
case 'd':
597
argv++; argc--;
598
opt_cabinet_size = wcstol( argv[1], NULL, 10 );
599
if (opt_cabinet_size < 50000)
600
{
601
WINE_MESSAGE( "cabarc: Cabinet size must be at least 50000\n" );
602
return 1;
603
}
604
break;
605
case 'h':
606
usage();
607
return 0;
608
case 'i':
609
argv++; argc--;
610
opt_cabinet_id = wcstol( argv[1], NULL, 10 );
611
break;
612
case 'm':
613
argv++; argc--;
614
if (!wcscmp( argv[1], L"none" )) opt_compression = tcompTYPE_NONE;
615
else if (!wcscmp( argv[1], L"mszip" )) opt_compression = tcompTYPE_MSZIP;
616
else
617
{
618
WINE_MESSAGE( "cabarc: Unknown compression type %s\n", debugstr_w(argv[1]));
619
return 1;
620
}
621
break;
622
case 'p':
623
opt_preserve_paths = TRUE;
624
break;
625
case 'r':
626
opt_recurse = TRUE;
627
break;
628
case 's':
629
argv++; argc--;
630
opt_reserve_space = wcstol( argv[1], NULL, 10 );
631
break;
632
case 'v':
633
opt_verbose++;
634
break;
635
default:
636
usage();
637
return 1;
638
}
639
argv++; argc--;
640
}
641
642
command = argv[1];
643
if (argc < 3 || !command[0] || command[1])
644
{
645
usage();
646
return 1;
647
}
648
cab_file = strdupWtoA( CP_ACP, argv[2] );
649
argv += 2;
650
argc -= 2;
651
652
if (!GetFullPathNameA( cab_file, MAX_PATH, buffer, &file_part ) || !file_part)
653
{
654
WINE_ERR( "cannot get full name for %s\n", wine_dbgstr_a( cab_file ));
655
return 1;
656
}
657
strcpy(filename, file_part);
658
file_part[0] = 0;
659
660
/* map slash to backslash in all file arguments */
661
for (i = 1; i < argc; i++)
662
for (p = argv[i]; *p; p++)
663
if (*p == '/') *p = '\\';
664
opt_files = argv + 1;
665
opt_cab_file = filename;
666
667
switch (*command)
668
{
669
case 'l':
670
case 'L':
671
return list_cabinet( buffer );
672
case 'n':
673
case 'N':
674
return new_cabinet( buffer );
675
case 'x':
676
case 'X':
677
if (argc > 1) /* check for destination dir as last argument */
678
{
679
WCHAR *last = argv[argc - 1];
680
if (last[0] && last[lstrlenW(last) - 1] == '\\')
681
{
682
opt_dest_dir = last;
683
argv[--argc] = NULL;
684
}
685
}
686
WINE_TRACE("Extracting file(s) from cabinet %s\n", wine_dbgstr_a(cab_file));
687
return extract_cabinet( buffer );
688
default:
689
usage();
690
return 1;
691
}
692
}
693
694