| /* | |
| * 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 */ | |
| #ifndef _O_RDONLY | |
| #define _O_RDONLY 0 | |
| #define _O_WRONLY 1 | |
| #define _O_RDWR 2 | |
| #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 | |
| #endif | |
| #ifndef _O_ACCMODE | |
| #define _O_ACCMODE (_O_RDONLY|_O_WRONLY|_O_RDWR) | |
| #endif | |
| #ifndef _SH_COMPAT | |
| #define _SH_COMPAT 0x00 | |
| #define _SH_DENYRW 0x10 | |
| #define _SH_DENYWR 0x20 | |
| #define _SH_DENYRD 0x30 | |
| #define _SH_DENYNO 0x40 | |
| #endif | |
| #ifndef _A_RDONLY | |
| #define _A_RDONLY 0x01 | |
| #define _A_HIDDEN 0x02 | |
| #define _A_SYSTEM 0x04 | |
| #define _A_ARCH 0x20 | |
| #endif | |
| /* command-line options */ | |
| static int opt_cabinet_size = CB_MAX_DISK; | |
| static int opt_cabinet_id; | |
| static int opt_compression = tcompTYPE_MSZIP; | |
| static BOOL opt_recurse; | |
| static BOOL opt_preserve_paths; | |
| static int opt_reserve_space; | |
| static int opt_verbose; | |
| static char *opt_cab_file; | |
| static WCHAR *opt_dest_dir; | |
| 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; | |
| } | |
| /* format a cabinet name by replacing the '*' wildcard by the cabinet id */ | |
| static BOOL format_cab_name( char *dest, int id, const char *name ) | |
| { | |
| const char *num = strchr( name, '*' ); | |
| int len; | |
| if (!num) | |
| { | |
| if (id == 1) | |
| { | |
| strcpy( dest, name ); | |
| return TRUE; | |
| } | |
| WINE_MESSAGE( "cabarc: Cabinet name must contain a '*' character\n" ); | |
| return FALSE; | |
| } | |
| len = num - name; | |
| memcpy( dest, name, len ); | |
| len += sprintf( dest + len, "%u", id ); | |
| lstrcpynA( dest + len, num + 1, CB_MAX_CABINET_NAME - len ); | |
| return TRUE; | |
| } | |
| 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 ) | |
| { | |
| return format_cab_name( cab->szCab, cab->iCab + 1, opt_cab_file ); | |
| } | |
| 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 INT_PTR CDECL fdi_open( char *file, int oflag, int pmode ) | |
| { | |
| int err; | |
| return fci_open( file, oflag, pmode, &err, NULL ); | |
| } | |
| static UINT CDECL fdi_read( INT_PTR hf, void *pv, UINT cb ) | |
| { | |
| int err; | |
| return fci_read( hf, pv, cb, &err, NULL ); | |
| } | |
| static UINT CDECL fdi_write( INT_PTR hf, void *pv, UINT cb ) | |
| { | |
| int err; | |
| return fci_write( hf, pv, cb, &err, NULL ); | |
| } | |
| static int CDECL fdi_close( INT_PTR hf ) | |
| { | |
| int err; | |
| return fci_close( hf, &err, NULL ); | |
| } | |
| static LONG CDECL fdi_lseek( INT_PTR hf, LONG dist, int whence ) | |
| { | |
| int err; | |
| return fci_lseek( hf, dist, whence, &err, NULL ); | |
| } | |
| /* create directories leading to a given file */ | |
| static void create_directories( const WCHAR *name ) | |
| { | |
| WCHAR *path, *p; | |
| /* create the directory/directories */ | |
| path = cab_alloc( (strlenW(name) + 1) * sizeof(WCHAR) ); | |
| strcpyW(path, name); | |
| p = strchrW(path, '\\'); | |
| while (p != NULL) | |
| { | |
| *p = 0; | |
| if (!CreateDirectoryW( path, NULL )) | |
| WINE_TRACE("Couldn't create directory %s - error: %d\n", wine_dbgstr_w(path), GetLastError()); | |
| *p = '\\'; | |
| p = strchrW(p+1, '\\'); | |
| } | |
| cab_free( path ); | |
| } | |
| /* check if file name matches against one of the files specification */ | |
| static BOOL match_files( const WCHAR *name ) | |
| { | |
| int i; | |
| if (!*opt_files) return TRUE; | |
| for (i = 0; opt_files[i]; i++) | |
| { | |
| unsigned int len = strlenW( opt_files[i] ); | |
| /* FIXME: do smarter matching, and wildcards */ | |
| if (!len) continue; | |
| if (strncmpiW( name, opt_files[i], len )) continue; | |
| if (opt_files[i][len - 1] == '\\' || !name[len] || name[len] == '\\') return TRUE; | |
| } | |
| return FALSE; | |
| } | |
| static INT_PTR CDECL list_notify( FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin ) | |
| { | |
| WCHAR *nameW; | |
| switch (fdint) | |
| { | |
| case fdintCABINET_INFO: | |
| return 0; | |
| case fdintCOPY_FILE: | |
| nameW = strdupAtoW( (pfdin->attribs & _A_NAME_IS_UTF) ? CP_UTF8 : CP_ACP, pfdin->psz1 ); | |
| if (match_files( nameW )) | |
| { | |
| char *nameU = strdupWtoA( CP_UNIXCP, nameW ); | |
| if (opt_verbose) | |
| { | |
| char attrs[] = "rxash"; | |
| if (!(pfdin->attribs & _A_RDONLY)) attrs[0] = '-'; | |
| if (!(pfdin->attribs & _A_EXEC)) attrs[1] = '-'; | |
| if (!(pfdin->attribs & _A_ARCH)) attrs[2] = '-'; | |
| if (!(pfdin->attribs & _A_SYSTEM)) attrs[3] = '-'; | |
| if (!(pfdin->attribs & _A_HIDDEN)) attrs[4] = '-'; | |
| printf( " %s %9u %04u/%02u/%02u %02u:%02u:%02u ", attrs, pfdin->cb, | |
| (pfdin->date >> 9) + 1980, (pfdin->date >> 5) & 0x0f, pfdin->date & 0x1f, | |
| pfdin->time >> 11, (pfdin->time >> 5) & 0x3f, (pfdin->time & 0x1f) * 2 ); | |
| } | |
| printf( "%s\n", nameU ); | |
| cab_free( nameU ); | |
| } | |
| cab_free( nameW ); | |
| return 0; | |
| default: | |
| WINE_FIXME( "Unexpected notification type %d.\n", fdint ); | |
| return 0; | |
| } | |
| } | |
| static int list_cabinet( char *cab_dir ) | |
| { | |
| ERF erf; | |
| int ret = 0; | |
| HFDI fdi = FDICreate( cab_alloc, cab_free, fdi_open, fdi_read, | |
| fdi_write, fdi_close, fdi_lseek, cpuUNKNOWN, &erf ); | |
| if (!FDICopy( fdi, opt_cab_file, cab_dir, 0, list_notify, NULL, NULL )) ret = GetLastError(); | |
| FDIDestroy( fdi ); | |
| return ret; | |
| } | |
| static INT_PTR CDECL extract_notify( FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin ) | |
| { | |
| WCHAR *file, *nameW, *path = NULL; | |
| INT_PTR ret; | |
| switch (fdint) | |
| { | |
| case fdintCABINET_INFO: | |
| return 0; | |
| case fdintCOPY_FILE: | |
| nameW = strdupAtoW( (pfdin->attribs & _A_NAME_IS_UTF) ? CP_UTF8 : CP_ACP, pfdin->psz1 ); | |
| if (opt_preserve_paths) | |
| { | |
| file = nameW; | |
| while (*file == '\\') file++; /* remove leading backslashes */ | |
| } | |
| else | |
| { | |
| if ((file = strrchrW( nameW, '\\' ))) file++; | |
| else file = nameW; | |
| } | |
| if (opt_dest_dir) | |
| { | |
| path = cab_alloc( (strlenW(opt_dest_dir) + strlenW(file) + 1) * sizeof(WCHAR) ); | |
| strcpyW( path, opt_dest_dir ); | |
| strcatW( path, file ); | |
| } | |
| else path = file; | |
| if (match_files( file )) | |
| { | |
| if (opt_verbose) | |
| { | |
| char *nameU = strdupWtoA( CP_UNIXCP, path ); | |
| printf( "extracting %s\n", nameU ); | |
| cab_free( nameU ); | |
| } | |
| create_directories( path ); | |
| /* FIXME: check for existing file and overwrite mode */ | |
| ret = (INT_PTR)CreateFileW( path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, | |
| NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); | |
| } | |
| else ret = 0; | |
| cab_free( nameW ); | |
| if (path != file) cab_free( path ); | |
| return ret; | |
| case fdintCLOSE_FILE_INFO: | |
| CloseHandle( (HANDLE)pfdin->hf ); | |
| return 0; | |
| case fdintNEXT_CABINET: | |
| WINE_TRACE("Next cab: status %u, path '%s', file '%s'\n", pfdin->fdie, pfdin->psz3, pfdin->psz1); | |
| return pfdin->fdie == FDIERROR_NONE ? 0 : -1; | |
| case fdintENUMERATE: | |
| return 0; | |
| default: | |
| WINE_FIXME( "Unexpected notification type %d.\n", fdint ); | |
| return 0; | |
| } | |
| } | |
| static int extract_cabinet( char *cab_dir ) | |
| { | |
| ERF erf; | |
| int ret = 0; | |
| HFDI fdi = FDICreate( cab_alloc, cab_free, fdi_open, fdi_read, | |
| fdi_write, fdi_close, fdi_lseek, cpuUNKNOWN, &erf ); | |
| if (!FDICopy( fdi, opt_cab_file, cab_dir, 0, extract_notify, NULL, NULL )) | |
| { | |
| ret = GetLastError(); | |
| WINE_WARN("FDICopy() failed: code %u\n", ret); | |
| } | |
| FDIDestroy( fdi ); | |
| return ret; | |
| } | |
| 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 ) | |
| { | |
| static const WCHAR plusW[] = {'+',0}; | |
| WCHAR **file; | |
| ERF erf; | |
| BOOL ret = FALSE; | |
| HFCI fci; | |
| CCAB cab; | |
| cab.cb = opt_cabinet_size; | |
| cab.cbFolderThresh = CB_MAX_DISK; | |
| cab.cbReserveCFHeader = opt_reserve_space; | |
| cab.cbReserveCFFolder = 0; | |
| cab.cbReserveCFData = 0; | |
| cab.iCab = 0; | |
| cab.iDisk = 0; | |
| cab.setID = opt_cabinet_id; | |
| cab.szDisk[0] = 0; | |
| strcpy( cab.szCabPath, cab_dir ); | |
| strcat( cab.szCabPath, "\\" ); | |
| format_cab_name( cab.szCab, 1, opt_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 (!strcmpW( *file, plusW )) | |
| FCIFlushFolder( fci, fci_get_next_cab, fci_status ); | |
| else | |
| 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(opt_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" | |
| " -d size Set maximum disk size\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 filename[MAX_PATH]; | |
| char *cab_file, *file_part; | |
| int i; | |
| while (argv[1] && argv[1][0] == '-') | |
| { | |
| switch (argv[1][1]) | |
| { | |
| case 'd': | |
| argv++; argc--; | |
| opt_cabinet_size = atoiW( argv[1] ); | |
| if (opt_cabinet_size < 50000) | |
| { | |
| WINE_MESSAGE( "cabarc: Cabinet size must be at least 50000\n" ); | |
| return 1; | |
| } | |
| break; | |
| 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 | |
| { | |
| char *arg = strdupWtoA( CP_ACP, argv[1] ); | |
| WINE_MESSAGE( "cabarc: Unknown compression type '%s'\n", arg); | |
| return 1; | |
| } | |
| break; | |
| case 'p': | |
| opt_preserve_paths = TRUE; | |
| break; | |
| case 'r': | |
| opt_recurse = TRUE; | |
| 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; | |
| } | |
| strcpy(filename, file_part); | |
| file_part[0] = 0; | |
| /* 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; | |
| opt_cab_file = filename; | |
| switch (*command) | |
| { | |
| case 'l': | |
| case 'L': | |
| return list_cabinet( buffer ); | |
| case 'n': | |
| case 'N': | |
| return new_cabinet( buffer ); | |
| case 'x': | |
| case 'X': | |
| if (argc > 1) /* check for destination dir as last argument */ | |
| { | |
| WCHAR *last = argv[argc - 1]; | |
| if (last[0] && last[strlenW(last) - 1] == '\\') | |
| { | |
| opt_dest_dir = last; | |
| argv[--argc] = NULL; | |
| } | |
| } | |
| WINE_TRACE("Extracting file(s) from cabinet %s\n", wine_dbgstr_a(cab_file)); | |
| return extract_cabinet( buffer ); | |
| default: | |
| usage(); | |
| return 1; | |
| } | |
| } |