cabarc: Initial version of the cabarc.exe tool, with support for creating a cabinet.
This commit is contained in:
parent
5ae2b0f219
commit
30bdbedc2d
|
@ -15370,6 +15370,7 @@ wine_fn_config_makefile libs/wpp enable_libs_wpp
|
|||
wine_fn_config_makefile loader enable_loader
|
||||
wine_fn_config_program aspnet_regiis enable_aspnet_regiis install
|
||||
wine_fn_config_program attrib enable_attrib install
|
||||
wine_fn_config_program cabarc enable_cabarc install
|
||||
wine_fn_config_program cacls enable_cacls install
|
||||
wine_fn_config_program clock enable_clock po,install
|
||||
wine_fn_config_program cmd enable_cmd po,install
|
||||
|
|
|
@ -2871,6 +2871,7 @@ WINE_CONFIG_MAKEFILE([libs/wpp])
|
|||
WINE_CONFIG_MAKEFILE([loader])
|
||||
WINE_CONFIG_PROGRAM(aspnet_regiis,,[install])
|
||||
WINE_CONFIG_PROGRAM(attrib,,[install])
|
||||
WINE_CONFIG_PROGRAM(cabarc,,[install])
|
||||
WINE_CONFIG_PROGRAM(cacls,,[install])
|
||||
WINE_CONFIG_PROGRAM(clock,,[po,install])
|
||||
WINE_CONFIG_PROGRAM(cmd,,[po,install])
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
MODULE = cabarc.exe
|
||||
APPMODE = -mconsole -municode
|
||||
IMPORTS = cabinet
|
||||
EXTRADEFS = -DWINE_NO_UNICODE_MACROS
|
||||
|
||||
C_SRCS = cabarc.c
|
||||
|
||||
@MAKE_PROG_RULES@
|
|
@ -0,0 +1,484 @@
|
|||
/*
|
||||
* Tool to manipulate cabinet files
|
||||
*
|
||||
* Copyright 2011 Alexandre Julliard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "wine/port.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include "windows.h"
|
||||
#include "fci.h"
|
||||
#include "fdi.h"
|
||||
|
||||
#include "wine/unicode.h"
|
||||
#include "wine/debug.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(cabarc);
|
||||
|
||||
/* from msvcrt */
|
||||
#define _O_RDONLY 0
|
||||
#define _O_WRONLY 1
|
||||
#define _O_RDWR 2
|
||||
#define _O_ACCMODE (_O_RDONLY|_O_WRONLY|_O_RDWR)
|
||||
#define _O_APPEND 0x0008
|
||||
#define _O_RANDOM 0x0010
|
||||
#define _O_SEQUENTIAL 0x0020
|
||||
#define _O_TEMPORARY 0x0040
|
||||
#define _O_NOINHERIT 0x0080
|
||||
#define _O_CREAT 0x0100
|
||||
#define _O_TRUNC 0x0200
|
||||
#define _O_EXCL 0x0400
|
||||
#define _O_SHORT_LIVED 0x1000
|
||||
#define _O_TEXT 0x4000
|
||||
#define _O_BINARY 0x8000
|
||||
|
||||
#define _SH_COMPAT 0x00
|
||||
#define _SH_DENYRW 0x10
|
||||
#define _SH_DENYWR 0x20
|
||||
#define _SH_DENYRD 0x30
|
||||
#define _SH_DENYNO 0x40
|
||||
|
||||
#define _A_RDONLY 0x01
|
||||
#define _A_HIDDEN 0x02
|
||||
#define _A_SYSTEM 0x04
|
||||
#define _A_ARCH 0x20
|
||||
|
||||
/* command-line options */
|
||||
static int opt_cabinet_id;
|
||||
static int opt_compression = tcompTYPE_MSZIP;
|
||||
static int opt_recurse;
|
||||
static int opt_preserve_paths;
|
||||
static int opt_reserve_space;
|
||||
static int opt_verbose;
|
||||
static WCHAR **opt_files;
|
||||
|
||||
static void * CDECL cab_alloc( ULONG size )
|
||||
{
|
||||
return HeapAlloc( GetProcessHeap(), 0, size );
|
||||
}
|
||||
|
||||
static void CDECL cab_free( void *ptr )
|
||||
{
|
||||
HeapFree( GetProcessHeap(), 0, ptr );
|
||||
}
|
||||
|
||||
static WCHAR *strdupAtoW( UINT cp, const char *str )
|
||||
{
|
||||
WCHAR *ret = NULL;
|
||||
if (str)
|
||||
{
|
||||
DWORD len = MultiByteToWideChar( cp, 0, str, -1, NULL, 0 );
|
||||
if ((ret = cab_alloc( len * sizeof(WCHAR) )))
|
||||
MultiByteToWideChar( cp, 0, str, -1, ret, len );
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char *strdupWtoA( UINT cp, const WCHAR *str )
|
||||
{
|
||||
char *ret = NULL;
|
||||
if (str)
|
||||
{
|
||||
DWORD len = WideCharToMultiByte( cp, 0, str, -1, NULL, 0, NULL, NULL );
|
||||
if ((ret = cab_alloc( len )))
|
||||
WideCharToMultiByte( cp, 0, str, -1, ret, len, NULL, NULL );
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int CDECL fci_file_placed( CCAB *cab, char *file, LONG size, BOOL continuation, void *ptr )
|
||||
{
|
||||
if (!continuation && opt_verbose) printf( "adding %s\n", file );
|
||||
return 0;
|
||||
}
|
||||
|
||||
static INT_PTR CDECL fci_open( char *file, int oflag, int pmode, int *err, void *ptr )
|
||||
{
|
||||
DWORD creation = 0, sharing = 0;
|
||||
int ioflag = 0;
|
||||
HANDLE handle;
|
||||
|
||||
switch (oflag & _O_ACCMODE)
|
||||
{
|
||||
case _O_RDONLY: ioflag |= GENERIC_READ; break;
|
||||
case _O_WRONLY: ioflag |= GENERIC_WRITE; break;
|
||||
case _O_RDWR: ioflag |= GENERIC_READ | GENERIC_WRITE; break;
|
||||
}
|
||||
|
||||
if (oflag & _O_CREAT)
|
||||
{
|
||||
if (oflag & _O_EXCL) creation = CREATE_NEW;
|
||||
else if (oflag & _O_TRUNC) creation = CREATE_ALWAYS;
|
||||
else creation = OPEN_ALWAYS;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (oflag & _O_TRUNC) creation = TRUNCATE_EXISTING;
|
||||
else creation = OPEN_EXISTING;
|
||||
}
|
||||
|
||||
switch (pmode & 0x70)
|
||||
{
|
||||
case _SH_DENYRW: sharing = 0; break;
|
||||
case _SH_DENYWR: sharing = FILE_SHARE_READ; break;
|
||||
case _SH_DENYRD: sharing = FILE_SHARE_WRITE; break;
|
||||
default: sharing = FILE_SHARE_READ | FILE_SHARE_WRITE; break;
|
||||
}
|
||||
|
||||
handle = CreateFileA( file, ioflag, sharing, NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL );
|
||||
if (handle == INVALID_HANDLE_VALUE) *err = GetLastError();
|
||||
return (INT_PTR)handle;
|
||||
}
|
||||
|
||||
static UINT CDECL fci_read( INT_PTR hf, void *pv, UINT cb, int *err, void *ptr )
|
||||
{
|
||||
DWORD num_read;
|
||||
|
||||
if (!ReadFile( (HANDLE)hf, pv, cb, &num_read, NULL ))
|
||||
{
|
||||
*err = GetLastError();
|
||||
return -1;
|
||||
}
|
||||
return num_read;
|
||||
}
|
||||
|
||||
static UINT CDECL fci_write( INT_PTR hf, void *pv, UINT cb, int *err, void *ptr )
|
||||
{
|
||||
DWORD written;
|
||||
|
||||
if (!WriteFile( (HANDLE) hf, pv, cb, &written, NULL ))
|
||||
{
|
||||
*err = GetLastError();
|
||||
return -1;
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
static int CDECL fci_close( INT_PTR hf, int *err, void *ptr )
|
||||
{
|
||||
if (!CloseHandle( (HANDLE)hf ))
|
||||
{
|
||||
*err = GetLastError();
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static LONG CDECL fci_lseek( INT_PTR hf, LONG dist, int seektype, int *err, void *ptr )
|
||||
{
|
||||
DWORD ret;
|
||||
|
||||
ret = SetFilePointer( (HANDLE)hf, dist, NULL, seektype );
|
||||
if (ret == INVALID_SET_FILE_POINTER && GetLastError())
|
||||
{
|
||||
*err = GetLastError();
|
||||
return -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int CDECL fci_delete( char *file, int *err, void *ptr )
|
||||
{
|
||||
if (!DeleteFileA( file ))
|
||||
{
|
||||
*err = GetLastError();
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BOOL CDECL fci_get_temp( char *name, int size, void *ptr )
|
||||
{
|
||||
char path[MAX_PATH];
|
||||
|
||||
if (!GetTempPathA( MAX_PATH, path )) return FALSE;
|
||||
if (!GetTempFileNameA( path, "cab", 0, name )) return FALSE;
|
||||
DeleteFileA( name );
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL CDECL fci_get_next_cab( CCAB *cab, ULONG prev_size, void *ptr )
|
||||
{
|
||||
WINE_ERR( "shouldn't happen\n" );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static LONG CDECL fci_status( UINT type, ULONG cb1, ULONG cb2, void *ptr )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static INT_PTR CDECL fci_get_open_info( char *name, USHORT *date, USHORT *time,
|
||||
USHORT *attribs, int *err, void *ptr )
|
||||
{
|
||||
HANDLE handle;
|
||||
BY_HANDLE_FILE_INFORMATION info;
|
||||
WCHAR *p, *nameW = strdupAtoW( CP_UTF8, name );
|
||||
|
||||
handle = CreateFileW( nameW, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL, OPEN_EXISTING, 0, NULL );
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
*err = GetLastError();
|
||||
WINE_ERR( "failed to open %s: error %u\n", wine_dbgstr_w(nameW), *err );
|
||||
cab_free( nameW );
|
||||
return -1;
|
||||
}
|
||||
if (!GetFileInformationByHandle( handle, &info ))
|
||||
{
|
||||
*err = GetLastError();
|
||||
CloseHandle( handle );
|
||||
cab_free( nameW );
|
||||
return -1;
|
||||
}
|
||||
FileTimeToDosDateTime( &info.ftLastWriteTime, date, time );
|
||||
*attribs = info.dwFileAttributes & (_A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_ARCH);
|
||||
for (p = nameW; *p; p++) if (*p >= 0x80) break;
|
||||
if (*p) *attribs |= _A_NAME_IS_UTF;
|
||||
cab_free( nameW );
|
||||
return (INT_PTR)handle;
|
||||
}
|
||||
|
||||
|
||||
static BOOL add_file( HFCI fci, WCHAR *name )
|
||||
{
|
||||
BOOL ret;
|
||||
char *filename, *path = strdupWtoA( CP_UTF8, name );
|
||||
|
||||
if (!opt_preserve_paths)
|
||||
{
|
||||
if ((filename = strrchr( path, '\\' ))) filename++;
|
||||
else filename = path;
|
||||
}
|
||||
else
|
||||
{
|
||||
filename = path;
|
||||
while (*filename == '\\') filename++; /* remove leading backslashes */
|
||||
}
|
||||
ret = FCIAddFile( fci, path, filename, FALSE,
|
||||
fci_get_next_cab, fci_status, fci_get_open_info, opt_compression );
|
||||
cab_free( path );
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BOOL add_directory( HFCI fci, WCHAR *dir )
|
||||
{
|
||||
static const WCHAR wildcardW[] = {'*',0};
|
||||
WCHAR *p, *buffer;
|
||||
HANDLE handle;
|
||||
WIN32_FIND_DATAW data;
|
||||
BOOL ret = TRUE;
|
||||
|
||||
if (!(buffer = cab_alloc( (strlenW(dir) + MAX_PATH + 2) * sizeof(WCHAR) ))) return FALSE;
|
||||
strcpyW( buffer, dir );
|
||||
p = buffer + strlenW( buffer );
|
||||
if (p > buffer && p[-1] != '\\') *p++ = '\\';
|
||||
strcpyW( p, wildcardW );
|
||||
|
||||
if ((handle = FindFirstFileW( buffer, &data )) != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (data.cFileName[0] == '.' && !data.cFileName[1]) continue;
|
||||
if (data.cFileName[0] == '.' && data.cFileName[1] == '.' && !data.cFileName[2]) continue;
|
||||
if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) continue;
|
||||
|
||||
strcpyW( p, data.cFileName );
|
||||
if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
ret = add_directory( fci, buffer );
|
||||
else
|
||||
ret = add_file( fci, buffer );
|
||||
if (!ret) break;
|
||||
} while (FindNextFileW( handle, &data ));
|
||||
FindClose( handle );
|
||||
}
|
||||
cab_free( buffer );
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL add_file_or_directory( HFCI fci, WCHAR *name )
|
||||
{
|
||||
DWORD attr = GetFileAttributesW( name );
|
||||
|
||||
if (attr == INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
WINE_MESSAGE( "cannot open %s\n", wine_dbgstr_w(name) );
|
||||
return FALSE;
|
||||
}
|
||||
if (attr & FILE_ATTRIBUTE_DIRECTORY)
|
||||
{
|
||||
if (opt_recurse) return add_directory( fci, name );
|
||||
WINE_MESSAGE( "cabarc: Cannot add %s, it's a directory (use -r for recursive add)\n",
|
||||
wine_dbgstr_w(name) );
|
||||
return FALSE;
|
||||
}
|
||||
return add_file( fci, name );
|
||||
}
|
||||
|
||||
static int new_cabinet( char *cab_dir, char *cab_file )
|
||||
{
|
||||
WCHAR **file;
|
||||
ERF erf;
|
||||
BOOL ret = FALSE;
|
||||
HFCI fci;
|
||||
CCAB cab;
|
||||
|
||||
cab.cb = CB_MAX_DISK;
|
||||
cab.cbFolderThresh = CB_MAX_DISK;
|
||||
cab.cbReserveCFHeader = opt_reserve_space;
|
||||
cab.cbReserveCFFolder = 0;
|
||||
cab.cbReserveCFData = 0;
|
||||
cab.iCab = 1;
|
||||
cab.iDisk = 0;
|
||||
cab.setID = opt_cabinet_id;
|
||||
cab.szDisk[0] = 0;
|
||||
|
||||
strcpy( cab.szCabPath, cab_dir );
|
||||
strcat( cab.szCabPath, "\\" );
|
||||
strcpy( cab.szCab, cab_file );
|
||||
|
||||
fci = FCICreate( &erf, fci_file_placed, cab_alloc, cab_free,fci_open, fci_read,
|
||||
fci_write, fci_close, fci_lseek, fci_delete, fci_get_temp, &cab, NULL );
|
||||
|
||||
for (file = opt_files; *file; file++)
|
||||
if (!(ret = add_file_or_directory( fci, *file ))) break;
|
||||
|
||||
if (ret)
|
||||
{
|
||||
if (!(ret = FCIFlushCabinet( fci, FALSE, fci_get_next_cab, fci_status )))
|
||||
WINE_MESSAGE( "cabarc: Failed to create cabinet %s\n", wine_dbgstr_a(cab_file) );
|
||||
}
|
||||
FCIDestroy( fci );
|
||||
return !ret;
|
||||
}
|
||||
|
||||
static void usage( void )
|
||||
{
|
||||
WINE_MESSAGE(
|
||||
"Usage: cabarc [options] command file.cab [files...] [dest_dir\\]\n"
|
||||
"\nCommands:\n"
|
||||
" L List the contents of the cabinet\n"
|
||||
" N Create a new cabinet\n"
|
||||
" X Extract files from the cabinet into dest_dir\n"
|
||||
"\nOptions:\n"
|
||||
" -h Display this help\n"
|
||||
" -i id Set cabinet id\n"
|
||||
" -m type Set compression type (mszip|none)\n"
|
||||
" -p Preserve directory names\n"
|
||||
" -r Recurse into directories\n"
|
||||
" -s size Reserve space in the cabinet header\n"
|
||||
" -v More verbose output\n" );
|
||||
}
|
||||
|
||||
int wmain( int argc, WCHAR *argv[] )
|
||||
{
|
||||
static const WCHAR noneW[] = {'n','o','n','e',0};
|
||||
static const WCHAR mszipW[] = {'m','s','z','i','p',0};
|
||||
|
||||
WCHAR *p, *command;
|
||||
char buffer[MAX_PATH];
|
||||
char *cab_file, *file_part;
|
||||
int i;
|
||||
|
||||
while (argv[1] && argv[1][0] == '-')
|
||||
{
|
||||
switch (argv[1][1])
|
||||
{
|
||||
case 'h':
|
||||
usage();
|
||||
return 0;
|
||||
case 'i':
|
||||
argv++; argc--;
|
||||
opt_cabinet_id = atoiW( argv[1] );
|
||||
break;
|
||||
case 'm':
|
||||
argv++; argc--;
|
||||
if (!strcmpiW( argv[1], noneW )) opt_compression = tcompTYPE_NONE;
|
||||
else if (!strcmpiW( argv[1], mszipW )) opt_compression = tcompTYPE_MSZIP;
|
||||
else
|
||||
{
|
||||
WINE_MESSAGE( "cabarc: Unknown compression type '%s'\n", optarg );
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
opt_preserve_paths = 1;
|
||||
break;
|
||||
case 'r':
|
||||
opt_recurse = 1;
|
||||
break;
|
||||
case 's':
|
||||
argv++; argc--;
|
||||
opt_reserve_space = atoiW( argv[1] );
|
||||
break;
|
||||
case 'v':
|
||||
opt_verbose++;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
argv++; argc--;
|
||||
}
|
||||
|
||||
command = argv[1];
|
||||
if (argc < 3 || !command[0] || command[1])
|
||||
{
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
cab_file = strdupWtoA( CP_ACP, argv[2] );
|
||||
argv += 2;
|
||||
argc -= 2;
|
||||
|
||||
if (!GetFullPathNameA( cab_file, MAX_PATH, buffer, &file_part ) || !file_part)
|
||||
{
|
||||
WINE_ERR( "cannot get full name for %s\n", wine_dbgstr_a( cab_file ));
|
||||
return 1;
|
||||
}
|
||||
file_part[-1] = 0;
|
||||
cab_free( cab_file );
|
||||
|
||||
/* map slash to backslash in all file arguments */
|
||||
for (i = 1; i < argc; i++)
|
||||
for (p = argv[i]; *p; p++)
|
||||
if (*p == '/') *p = '\\';
|
||||
opt_files = argv + 1;
|
||||
|
||||
switch (*command)
|
||||
{
|
||||
case 'l':
|
||||
case 'L':
|
||||
WINE_FIXME( "list not implemented\n" );
|
||||
return 1;
|
||||
case 'n':
|
||||
case 'N':
|
||||
return new_cabinet( buffer, file_part );
|
||||
case 'x':
|
||||
case 'X':
|
||||
WINE_FIXME( "extraction not implemented\n" );
|
||||
return 1;
|
||||
default:
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue