diff --git a/bin/aio-builder.sh b/bin/aio-builder.sh index b2389f3..0353db2 100755 --- a/bin/aio-builder.sh +++ b/bin/aio-builder.sh @@ -44,6 +44,10 @@ PRG=$(readlink -f $0) BASEDIR=`dirname "$PRG"` BASEDIR=`cd "$BASEDIR" && pwd`/.. +OLDPATH=$PATH +export PATH=$BASEDIR/libexec:$PATH +echo $PATH + # Parameters PRODUCT_DIR=$1 TARGET=$2 @@ -64,6 +68,8 @@ fi # call main builder_controller +export PATH=$OLDPATH + # exit exit 0 diff --git a/doc/beautify_bash.py b/doc/beautify_bash.py new file mode 100644 index 0000000..caa702f --- /dev/null +++ b/doc/beautify_bash.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +#************************************************************************** +# Copyright (C) 2011, Paul Lutus * +# * +# This program is free software; you can redistribute it and/or modify * +# it under the terms of the GNU General Public License as published by * +# the Free Software Foundation; either version 2 of the License, or * +# (at your option) any later version. * +# * +# This program 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 General Public License for more details. * +# * +# You should have received a copy of the GNU General Public License * +# along with this program; if not, write to the * +# Free Software Foundation, Inc., * +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +#************************************************************************** + +import re, sys + +PVERSION = '1.0' + +class BeautifyBash: + + def __init__(self): + self.tab_str = ' ' + self.tab_size = 2 + + def read_file(self,fp): + with open(fp) as f: + return f.read() + + def write_file(self,fp,data): + with open(fp,'w') as f: + f.write(data) + + def beautify_string(self,data,path = ''): + tab = 0 + case_stack = [] + in_here_doc = False + defer_ext_quote = False + in_ext_quote = False + ext_quote_string = '' + here_string = '' + output = [] + line = 1 + for record in re.split('\n',data): + record = record.rstrip() + stripped_record = record.strip() + + # collapse multiple quotes between ' ... ' + test_record = re.sub(r'\'.*?\'','',stripped_record) + # collapse multiple quotes between " ... " + test_record = re.sub(r'".*?"','',test_record) + # collapse multiple quotes between ` ... ` + test_record = re.sub(r'`.*?`','',test_record) + # collapse multiple quotes between \` ... ' (weird case) + test_record = re.sub(r'\\`.*?\'','',test_record) + # strip out any escaped single characters + test_record = re.sub(r'\\.','',test_record) + # remove '#' comments + test_record = re.sub(r'(\A|\s)(#.*)','',test_record,1) + if(not in_here_doc): + if(re.search('<<-?',test_record)): + here_string = re.sub('.*<<-?\s*[\'|"]?([_|\w]+)[\'|"]?.*','\\1',stripped_record,1) + in_here_doc = (len(here_string) > 0) + if(in_here_doc): # pass on with no changes + output.append(record) + # now test for here-doc termination string + if(re.search(here_string,test_record) and not re.search('<<',test_record)): + in_here_doc = False + else: # not in here doc + if(in_ext_quote): + if(re.search(ext_quote_string,test_record)): + # provide line after quotes + test_record = re.sub('.*%s(.*)' % ext_quote_string,'\\1',test_record,1) + in_ext_quote = False + else: # not in ext quote + if(re.search(r'(\A|\s)(\'|")',test_record)): + # apply only after this line has been processed + defer_ext_quote = True + ext_quote_string = re.sub('.*([\'"]).*','\\1',test_record,1) + # provide line before quote + test_record = re.sub('(.*)%s.*' % ext_quote_string,'\\1',test_record,1) + if(in_ext_quote): + # pass on unchanged + output.append(record) + else: # not in ext quote + inc = len(re.findall('(\s|\A|;)(case|then|do)(;|\Z|\s)',test_record)) + inc += len(re.findall('(\{|\(|\[)',test_record)) + outc = len(re.findall('(\s|\A|;)(esac|fi|done|elif)(;|\)|\||\Z|\s)',test_record)) + outc += len(re.findall('(\}|\)|\])',test_record)) + if(re.search(r'\besac\b',test_record)): + if(len(case_stack) == 0): + sys.stderr.write( + 'File %s: error: "esac" before "case" in line %d.\n' % (path,line) + ) + else: + outc += case_stack.pop() + # sepcial handling for bad syntax within case ... esac + if(len(case_stack) > 0): + if(re.search('\A[^(]*\)',test_record)): + # avoid overcount + outc -= 2 + case_stack[-1] += 1 + if(re.search(';;',test_record)): + outc += 1 + case_stack[-1] -= 1 + # an ad-hoc solution for the "else" keyword + else_case = (0,-1)[re.search('^(else)',test_record) != None] + net = inc - outc + tab += min(net,0) + extab = tab + else_case + extab = max(0,extab) + output.append((self.tab_str * self.tab_size * extab) + stripped_record) + tab += max(net,0) + if(defer_ext_quote): + in_ext_quote = True + defer_ext_quote = False + if(re.search(r'\bcase\b',test_record)): + case_stack.append(0) + line += 1 + error = (tab != 0) + if(error): + sys.stderr.write('File %s: error: indent/outdent mismatch: %d.\n' % (path,tab)) + return '\n'.join(output), error + + def beautify_file(self,path): + error = False + if(path == '-'): + data = sys.stdin.read() + result,error = self.beautify_string(data,'(stdin)') + sys.stdout.write(result) + else: # named file + data = self.read_file(path) + result,error = self.beautify_string(data,path) + if(data != result): + # make a backup copy + self.write_file(path + '~',data) + self.write_file(path,result) + return error + + def main(self): + error = False + sys.argv.pop(0) + if(len(sys.argv) < 1): + sys.stderr.write('usage: shell script filenames or \"-\" for stdin.\n') + else: + for path in sys.argv: + error |= self.beautify_file(path) + sys.exit((0,1)[error]) + +# if not called as a module +if(__name__ == '__main__'): + BeautifyBash().main() + diff --git a/lib/builder-targets.sh b/lib/builder-targets.sh index ab38ac5..baff09b 100644 --- a/lib/builder-targets.sh +++ b/lib/builder-targets.sh @@ -11,18 +11,20 @@ builder_config() { # Define commands - CMD_7z="`which 7z.exe`" ; builder_check_error "Command '7z' not installed" - CMD_unzip="`which unzip.exe`" ; builder_check_error "Command 'unzip' not installed" - CMD_unrar="`which UnRAR.exe`" ; builder_check_error "Command 'UnRAR' not installed" - CMD_zip="`which zip.exe`" ; builder_check_error "Command 'zip' not installed" - CMD_lha="`which lha.exe`" ; builder_check_error "Command 'lha' not installed" - CMD_tar="`which tar.exe`" ; builder_check_error "Command 'tar' not installed" - CMD_cabextract="`which cabextract.exe`" ; builder_check_error "Command 'cabextract' not installed" - CMD_unix2dos="`which unix2dos.exe`" ; builder_check_error "Command 'unix2dos' not installed" - CMD_sha1sum="`which sha1sum.exe`" ; builder_check_error "Command 'sha1sum' not installed" - CMD_inifile="`which inifile.exe`" ; builder_check_error "Command 'inifile' not installed (http://www.horstmuc.de/wbat32d.htm#inifile)" - CMD_showver="`which ShowVer.exe`" ; builder_check_error "Command 'ShowVer' not installed (http://www.codeproject.com/Articles/2457/ShowVer-exe-command-line-VERSIONINFO-display-progr)" - CMD_tidy="`which tidy.exe`" ; builder_check_error "Command 'tidy' not installed" + CMD_7z="`which 7z`" ; builder_check_error "Command '7z' not installed" + CMD_unzip="`which unzip.exe`" ; builder_check_error "Command 'unzip' not installed" + CMD_unrar="`which UnRAR.exe`" ; builder_check_error "Command 'UnRAR' not installed" + CMD_zip="`which zip.exe`" ; builder_check_error "Command 'zip' not installed" + CMD_tar="`which tar.exe`" ; builder_check_error "Command 'tar' not installed" + CMD_cabextract="`which cabextract.exe`" ; builder_check_error "Command 'cabextract' not installed" + CMD_unix2dos="`which unix2dos.exe`" ; builder_check_error "Command 'unix2dos' not installed" + CMD_sha1sum="`which sha1sum.exe`" ; builder_check_error "Command 'sha1sum' not installed" + CMD_inifile="`which inifile.exe`" ; builder_check_error "Command 'inifile' not installed (http://www.horstmuc.de/wbat32d.htm#inifile)" + CMD_showver="`which ShowVer.exe`" ; builder_check_error "Command 'ShowVer' not installed (http://www.codeproject.com/Articles/2457/ShowVer-exe-command-line-VERSIONINFO-display-progr)" + CMD_tidy="`which tidy.exe`" ; builder_check_error "Command 'tidy' not installed" + CMD_msiexec="`which msiexec.exe`" ; builder_check_error "Command 'msiexec' not installed" + CMD_innounp="`which innounp.exe`" ; builder_check_error "Command 'innounp' not installed" + CMD_xmlstarlet="`which xmlstarlet.exe`" ; builder_check_error "Command 'xmlstarlat' not installed" # Check temp dir test -d ${TMP_DIR} @@ -188,14 +190,14 @@ builder_create() { # Copy files and convert text files to dos format # cp -Rv ${PRODUCT_DIR}/AIO $INST_DIR - # cp -Rv ${PRODUCT_DIR}/CLIENT_DATA $INST_DIR + # cp -Rv ${PRODUCT_DIR}/Temp $INST_DIR # if [ -d "${PRODUCT_DIR}/SERVER_DATA" ] ; then # cp -Rv ${PRODUCT_DIR}/SERVER_DATA $INST_DIR # fi # copy binaries for (( i = 0 ; i < ${#DL_SOURCE[@]} ; i++ )) ; do - DL_EXTRACT_PATH[$i]=${INST_DIR}/CLIENT_DATA/${DL_ARCH[$i]}/${DL_EXTRACT_TO[$i]} + DL_EXTRACT_PATH[$i]=${INST_DIR}/Temp/${DL_ARCH[$i]}/${DL_EXTRACT_TO[$i]} local format=${DL_EXTRACT_FORMAT[$i]} if [ -z "$format" ] ; then format="cp"; fi local option=${DL_EXTRACT_OPTION[$i]} @@ -240,9 +242,10 @@ builder_package() { local release_new=${CREATOR_TAG}${RELEASE} local aio_file=${PN}_${VERSION}-${release_new}.7z + rm -rf $INST_DIR/Temp pushd ${OUTPUT_DIR} rm -f ${aio_file} ${AIO_REPOS_FILE_PATTERN}.7z - 7z a ${AIO_REPOS_FILE_PATTERN}.7z -v $INST_DIR/* + $CMD_7z a ${AIO_REPOS_FILE_PATTERN}.7z $INST_DIR/* builder_check_error "Building AIO-package" popd diff --git a/lib/builder-utils.sh b/lib/builder-utils.sh index 88b5e77..ae08bf5 100644 --- a/lib/builder-utils.sh +++ b/lib/builder-utils.sh @@ -1,3 +1,4 @@ + ############################################# # void retrieve_file (src, dst) # @@ -67,8 +68,6 @@ function process_file() { $CMD_unzip $option -o $src -d $dst elif [ "$format" = "unrar" ]; then $CMD_unrar x -y $option $src $dst - elif [ "$format" = "lha" ]; then - $CMD_lha x $option -w=$dst $src elif [ "$format" = "targz" ]; then $CMD_tar xzvf $option $src -C $dst elif [ "$format" = "tarbz2" ]; then @@ -201,16 +200,28 @@ write_ini_file() { # Var: # ################### -get_file_version() { - local getver_file=$1 - local getver_var=$2 - - if [ -f "$getver_file" ] ; then - $CMD_showver $getver_file | grep FileVersion: | rev | cut -d: -f1 | rev | tr -d ' ' > $getver_ver - echo "file $getver_file has version $getver_ver" - else - echo "$getver_file not found" - fi +vercomp() { + [ ! $(echo -e "$1\n$2" | sort --version-sort | head -1) = "$1" ] +} + +################### +# Get Verison of File +# +# Copy file only if it is newer +# +# Parameter +# File: to process +# Var: +# +################### + + +get_file_ver() { + local getver_file=$2 + local __resultvar=$1 + local myresult=`$CMD_showver $(cygpath -pw "$getver_file") | grep FileVersion: | rev | cut -d: -f1 | rev | tr -d ' '` + echo "$getver_file -> $myresult" + eval $__resultvar="'$myresult'" } ################### @@ -226,10 +237,29 @@ get_file_version() { copyver() { local source_file=$1 local target_file=$2 - local source_ver - local target_ver + local source_ver="" + local target_ver="" - get_file_version $source_file $source_ver + echo "Source File: $source_file Target Version: $target_file" + if [ -f "$source_file" ] && [ -f "$target_file" ] ; then + get_file_ver source_ver $source_file + get_file_ver target_ver $target_file + if [ vercomp $source_ver $target_ver ] ; then + echo "Source Verssion: $source_ver > Target Version: $target_ver" + echo "Copying File" + cp $source_file $target_file + else + echo "Source Verssion: $source_ver =< Target Version: $target_ver" + echo "Not Copying File" + fi + elif [ -f "$source_file" ] && [ ! -f "$target_file" ] ; then + source_ver=$(get_file_version $source_file) + echo "Source Verssion: $source_ver" + echo "Copying File" + cp $source_file $target_file + else + echo "Nothing to copy" + fi } ################### @@ -249,3 +279,68 @@ tidy_xml() { cat $source_file | $CMD_tidy -utf8 -xml -w 255 -i -c -q -asxml | sed -e 's!>!>!g' -e 's!<! $target_file } +############################################# +# void extract_file (format, src, dst) +# +# Description: Extract a file +# +# Parameter +# format: compression format +# src: source file to be used +# dst: path to extract the file +# +############################################# +function extract_file() { + + local format=$1 + local src=$2 + local dst=$3 + local option=$4 + local src_dir=`dirname "$src"` + local src_file=`basename "$src"` + local dst_dir=`dirname "$dst"` + local dst_file=`basename "$dst"` + + log_debug "Compression format: $format" + + mkdir -p $dst + if [ "$format" = "msi-a" ]; then + mkdir /cygdrive/c/msi-adm + pushd $src_dir + echo $src_file + $CMD_msiexec /a "$src_file" $option TARGETDIR="c:\msi-adm" + popd + cp -r /cygdrive/c/msi-adm/* $dst + rm -rf /cygdrive/c/msi-adm + elif [ "$format" = "msp-a" ]; then + pushd $dst_dir + cp $src $dst_dir + $CMD_msiexec /p "$src_file" /a "$dst_file" SHORTFILENAMES=TRUE /qb + popd + elif [ "$format" = "7zip" ]; then + $CMD_7z x -y $option -o$dst $src + elif [ "$format" = "unzip" ]; then + $CMD_unzip $option -o $src -d $dst + elif [ "$format" = "unrar" ]; then + $CMD_unrar x -y $option $(cygpath -pw "$src") $(cygpath -pw "$dst") + elif [ "$format" = "cab" ]; then + $CMD_cabextract $option -d $dst $src + elif [ "$format" = "cab-sfx" ]; then + $CMD_cabextract $option -d $dst $src + $CMD_xmlstarlet sel -N X="http://schemas.microsoft.com/wix/2008/Burn" -t -m "//X:Payload" -v "concat(@SourcePath,'-',@FilePath)" -n $dst/0 | tr '\\' '/' > $dst/rename.list + for i in `cat $dst/rename.list` + do + pushd $dst + local src_var=$(echo $i | cut -f1 -d-) + local dst_var=$(echo $i | cut -f2 -d-) + mkdir -p $(dirname $dst_var) + mv -v $src_var $dst_var + popd + done + elif [ "$format" = "inno" ]; then + $CMD_innounp $option -d $dst $src + else + fatal_error "Unknown compression format: $format" + fi +} + diff --git a/libexec/MsiX.exe b/libexec/MsiX.exe new file mode 100755 index 0000000..988a528 Binary files /dev/null and b/libexec/MsiX.exe differ diff --git a/libexec/MsiX/MsiX.cpp b/libexec/MsiX/MsiX.cpp new file mode 100644 index 0000000..6b03efe --- /dev/null +++ b/libexec/MsiX/MsiX.cpp @@ -0,0 +1,534 @@ + +#include "msix.h" +#pragma comment(lib, "msi.lib") + +// Entry point. +int _tmain(int argc, _TCHAR* argv[]) +{ + DWORD dwError = NOERROR; + HRESULT hr = NOERROR; + ARGS args = { 0 }; + IStorage* pRootStorage = NULL; + IEnumSTATSTG* pEnum = NULL; + LPCTSTR pszPersist = (LPTSTR)MSIDBOPEN_READONLY; + STATSTG stg = { 0 }; + PMSIHANDLE hDatabase = NULL; + PMSIHANDLE hView = NULL; + PMSIHANDLE hRecord = NULL; + + dwError = ParseArguments(argc, argv, &args); + if (ERROR_SUCCESS != dwError) + { + return dwError; + } + + // Open the root storage file and extract storages first. Storages cannot + // be extracted using MSI APIs so we must use the compound file implementation + // for IStorage. + hr = StgOpenStorage( + CT2W(args.Path), + NULL, + STGM_READ | STGM_SHARE_EXCLUSIVE, + NULL, + 0, + &pRootStorage); + if (SUCCEEDED(hr) && pRootStorage) + { + // Determine if the file path specifies an MSP file. + // This will be used later to open the database with MSI APIs. + if (IsPatch(pRootStorage)) + { + pszPersist = MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE; + } + + hr = pRootStorage->EnumElements(0, NULL, 0, &pEnum); + if (SUCCEEDED(hr)) + { + while (S_OK == (hr = pEnum->Next(1, &stg, NULL))) + { + if (STGTY_STORAGE == stg.type) + { + hr = SaveStorage(pRootStorage, args.Directory, stg.pwcsName, + args.IncludeExtension ? TEXT(".mst") : NULL); + if (FAILED(hr)) + { + break; + } + } + } + SAFE_RELEASE(pEnum); + } + } + SAFE_RELEASE(pRootStorage); + + // Now open the database using MSI APIs. Patches cannot be opened simultaneously + // since exclusive access is required and no MSI APIs are exported that accept + // an IStorage pointer. + if (SUCCEEDED(hr)) + { + dwError = MsiOpenDatabase(args.Path, pszPersist, &hDatabase); + if (ERROR_SUCCESS == dwError) + { + dwError = MsiDatabaseOpenView(hDatabase, + TEXT("SELECT `Name`, `Data` FROM `_Streams`"), &hView); + if (ERROR_SUCCESS == dwError) + { + dwError = MsiViewExecute(hView, NULL); + if (ERROR_SUCCESS == dwError) + { + while (ERROR_SUCCESS == (dwError = MsiViewFetch(hView, &hRecord))) + { + dwError = SaveStream(hRecord, args.Directory, args.IncludeExtension); + if (ERROR_SUCCESS != dwError) + { + break; + } + } + + // If there are no more records indicate success. + if (ERROR_NO_MORE_ITEMS == dwError) + { + dwError = ERROR_SUCCESS; + } + } + } + } + } + + // If a Win32 error has occurred return only the win32 error portion. + if (FACILITY_WIN32 == HRESULT_FACILITY(hr)) + { + dwError = HRESULT_CODE(hr); + } + else if (FAILED(hr)) + { + // Just set it to the HRESULT. Many common HRESULTs + // will yield an error string from FormatMessage. + dwError = hr; + } + + // Print the error to the console. + if (ERROR_SUCCESS != dwError) + { + win32_error(dwError); + } + + return dwError; +} + +// Parse the file path, and optionally output directory and extension guessing switch. +DWORD ParseArguments(int argc, [Pre(Null=No)] _TCHAR* argv[], [Pre(Null=No)] LPARGS args) +{ + _ASSERTE(argv); + _ASSERTE(args); + + int iParamIndex = 1; + + // Validate arguments. + if (2 > argc) + { + error(TEXT("Error: you must specify a Windows Installer file from which to extract files.\n")); + usage(argv[0], stderr); + return ERROR_INVALID_PARAMETER; + } + + if (0 == _tcsicmp(TEXT("/?"), argv[iParamIndex]) || 0 == _tcsicmp(TEXT("-?"), argv[iParamIndex])) + { + // Display the usage text. + usage(argv[0], stdout); + return ERROR_SUCCESS; + } + else if (TEXT('/') == argv[iParamIndex][0] || TEXT('-') == argv[iParamIndex][0]) + { + // Filename should not begin with a command-switch character. + error(TEXT("Error: invalid file name.\n")); + usage(argv[0], stderr); + return ERROR_INVALID_PARAMETER; + } + else + { + // Set the path argument. + args->Path = const_cast(argv[iParamIndex]); + } + + // Get the output directory if requested. + while (++iParamIndex < argc) + { + // The directory in which files are extracted. + if (0 == _tcsicmp(TEXT("/out"), argv[iParamIndex]) || 0 == _tcsicmp(TEXT("-out"), argv[iParamIndex])) + { + if (++iParamIndex < argc && TEXT('/') != argv[iParamIndex][0] && TEXT('-') != argv[iParamIndex][0]) + { + args->Directory = const_cast(argv[iParamIndex]); + } + else + { + error(TEXT("Error: you must specify an output directory with /out.\n")); + usage(argv[0], stderr); + return ERROR_INVALID_PARAMETER; + } + } + // Whether or not to include or guess at extensions for output file names. + else if (0 == _tcsicmp(TEXT("/ext"), argv[iParamIndex]) || 0 == _tcsicmp(TEXT("-ext"), argv[iParamIndex])) + { + args->IncludeExtension = TRUE; + } + else + { + error(TEXT("Error: unknown option: %s.\n"), argv[iParamIndex]); + usage (argv[0], stderr); + return ERROR_INVALID_PARAMETER; + } + } + + return ERROR_SUCCESS; +} + +// Prints usage to the given output file stream. +void usage(LPCTSTR pszPath, FILE* out) +{ + _ASSERTE(pszPath); + _ASSERTE(out); + + LPTSTR pszName = NULL; + pszName = (LPTSTR)_tcsrchr(pszPath, TEXT('\\')); + if (pszName) + { + // Advance past the backslash. + pszName++; + } + else + { + // Set the executable name. + pszName = const_cast(pszPath); + } + + _ftprintf(out, TEXT("Usage: %s [/out ] [/ext]\n\n"), pszName); + _ftprintf(out, TEXT("\tfile - Path to an MSI, MSM, MSP, or PCP file.\n")); + _ftprintf(out, TEXT("\tout - Extract streams and storages to the directory.\n")); + _ftprintf(out, TEXT("\text - Append appropriate extensions to output files.\n")); + _ftprintf(out, TEXT("\nExtracts transforms and cabinets from a Windows Installer file.\n")); +} + +// Colors errors on the console red and prints the formatted error. +// You can use positional format specifiers with CRT8. +void error(LPCTSTR pszFormat, ...) +{ + _ASSERTE(pszFormat); + + CONSOLE_SCREEN_BUFFER_INFO csbi; + HANDLE hStdErr = INVALID_HANDLE_VALUE; + va_list args; + + // Set console colors to error values. + hStdErr = GetStdHandle(STD_ERROR_HANDLE); + if (INVALID_HANDLE_VALUE != hStdErr) + { + if (GetConsoleScreenBufferInfo(hStdErr, &csbi)) + { + // Set new console colors. + SetConsoleTextAttribute(hStdErr, COLOR_ERROR); + } + } + + // Print error. + va_start(args, pszFormat); + _vftprintf_p(stderr, pszFormat, args); + va_end(args); + + // Reset the console colors to original values. + if (INVALID_HANDLE_VALUE != hStdErr) + { + SetConsoleTextAttribute(hStdErr, csbi.wAttributes); + } +} + +// Wrapper around FormatMessage for getting error text. +// Calls error() to print the error to the console. +void win32_error(DWORD dwError) +{ + LPTSTR pszError; + + // Format the error. Error ends with new line. + if (FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + dwError, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &pszError, + 0, + NULL)) + { + error(TEXT("Error 0x%1$08x (%1$d): %2$s"), dwError, pszError); + LocalFree(pszError); + } +} + +// Creates a patch from components, using the current working +// directory if pszDir is NULL. +// pszExt should be either NULL or start with a ".". +LPTSTR MakePath(LPTSTR pszDest, size_t cchDest, LPCTSTR pszDir, LPCTSTR pszName, LPCTSTR pszExt) +{ + size_t len = 0; + + _ASSERTE(pszDest); + _ASSERTE(cchDest); + _ASSERTE(pszName); + + // Make sure pszDest is NULL-terminated. + pszDest[0] = TEXT('\0'); + + if (pszDir) + { + // Get the length of pszDir. + len = _tcslen(pszDir); + + if (len && 0 != _tcsncpy_s(pszDest, cchDest, pszDir, len)) + { + return NULL; + } + + if (len && TEXT('\\') != pszDest[len - 1]) + { + // Make sure the path ends with a "\". + if (0 != _tcsncat_s(pszDest, cchDest, TEXT("\\"), _TRUNCATE)) + { + return NULL; + } + } + } + + // Append the file name. + if (0 != _tcsncat_s(pszDest, cchDest, pszName, _TRUNCATE)) + { + return NULL; + } + + // Append the extension. + if (pszExt) + { + if (0 != _tcsncat_s(pszDest, cchDest, pszExt, _TRUNCATE)) + { + return NULL; + } + } + + return pszDest; +} + +// Wrapper around allocating and filling a buffer using MsiRecordGetString(). +UINT GetString(MSIHANDLE hRecord, UINT iField, LPTSTR* ppszProperty, DWORD* pcchProperty) +{ + _ASSERTE(hRecord); + _ASSERTE(iField > 0); + _ASSERTE(ppszProperty); + _ASSERTE(pcchProperty); + + UINT iErr = NOERROR; + DWORD cchProperty = 0; + + iErr = MsiRecordGetString(hRecord, iField, TEXT(""), &cchProperty); + if (ERROR_MORE_DATA == iErr) + { + *ppszProperty = new TCHAR[++cchProperty]; + *pcchProperty = cchProperty; + + iErr = MsiRecordGetString(hRecord, iField, *ppszProperty, &cchProperty); + if (ERROR_SUCCESS != iErr) + { + delete [] *ppszProperty; + *ppszProperty = NULL; + *pcchProperty = 0; + } + } + + return iErr; +} + +// Determines if the given IStorage* is for a patch +// using the STATSTG for the IStorage object. +BOOL IsPatch(IStorage* pStorage) +{ + _ASSERTE(pStorage); + + HRESULT hr = NOERROR; + STATSTG stg = { 0 }; + + hr = pStorage->Stat(&stg, STATFLAG_NONAME); + if (SUCCEEDED(hr)) + { + return !memcmp(&stg.clsid, &CLSID_MsiPatch, sizeof(CLSID)); + } + + return FALSE; +} + +// Creates a new storage file and saves the named sub-storage of pRootStorage +// to the new storage file. +HRESULT SaveStorage(IStorage* pRootStorage, LPCTSTR pszDir, PCWSTR pszName, LPCTSTR pszExt) +{ + HRESULT hr = NOERROR; + TCHAR szPath[MAX_PATH] = { TEXT('\0') }; + IStorage* pStg = NULL; + IStorage* pFileStg = NULL; + + _ASSERTE(pRootStorage); + _ASSERTE(pszName); + + hr = pRootStorage->OpenStorage( + pszName, + NULL, + STGM_READ | STGM_SHARE_EXCLUSIVE, + NULL, + 0, + &pStg); + if (SUCCEEDED(hr) && pStg) + { + if (!MakePath(szPath, MAX_PATH, pszDir, CW2T(pszName), pszExt)) + { + hr = E_INVALIDARG; + } + else + { + _tprintf(TEXT("%s\n"), szPath); + + // Create the storage file. + hr = StgCreateDocfile( + CT2W(szPath), + STGM_WRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, + 0, + &pFileStg); + if (SUCCEEDED(hr) && pFileStg) + { + hr = pStg->CopyTo(0, NULL, NULL, pFileStg); + } + } + } + + SAFE_RELEASE(pFileStg); + SAFE_RELEASE(pStg); + + if (FAILED(hr)) + { + error(TEXT("Error: failed to save storage '%s'.\n"), CW2T(pszName)); + } + + return hr; +} + +// Saves the stream from the given record to a file with or without +// an extension based on whether or not fIncludeExt is set to TRUE. +UINT SaveStream(MSIHANDLE hRecord, [Pre(Null=Maybe)] LPCTSTR pszDir, BOOL fIncludeExt) +{ + UINT uiError = NOERROR; + TCHAR szPath[MAX_PATH]; + LPTSTR pszName = NULL; + DWORD cchName = 0; + CHAR szBuffer[256]; + DWORD cbBuffer = sizeof(szBuffer); + std::ofstream file; + + try + { + // Get the name of the stream but skip if \005SummaryInformation stream. + if (ERROR_SUCCESS == GetString(hRecord, 1, &pszName, &cchName) && + 0 != _tcsncmp(pszName, TEXT("\005"), 1)) + { + // Create the local file with the simple CFile write-only class. + do + { + uiError = MsiRecordReadStream(hRecord, 2, szBuffer, &cbBuffer); + if (ERROR_SUCCESS == uiError) + { + if (!file.is_open()) + { + // Create the file path if the file is not created and assume the extension + // if requested by fIncludeExt. + if (!MakePathForData(szPath, MAX_PATH, pszDir, pszName, szBuffer, cbBuffer, fIncludeExt)) + { + throw std::exception("Could not create the output path name"); + } + + // Create the local file in which data is written. + _tprintf(TEXT("%s\n"), szPath); + file.open(CT2A(szPath), std::ios_base::binary); + } + + file.write(szBuffer, cbBuffer ); + } + else + { + throw std::exception("Could not read from stream."); + } + } while (cbBuffer); + } + } + catch (std::exception& ex) + { + error(TEXT("Error: %s\n"), CA2T(ex.what())); + uiError = ERROR_CANNOT_MAKE; + } + + file.close(); + if (pszName) + { + delete [] pszName; + pszName = NULL; + } + + return uiError; +} + +// Creates a patch for the given file using MakePath, but uses what of the +// buffer it can to guess the file type and infer a common file extension. +LPTSTR MakePathForData(LPTSTR pszDest, size_t cchDest, LPCTSTR pszDir, LPCTSTR pszName, + LPCVOID pBuffer, size_t cbBuffer, BOOL fIncludeExt) +{ + LPTSTR pszExt = NULL; + if (fIncludeExt) + { + // Cabinet (*.cab) files. + if (0 == memcmp(pBuffer, "MSCF", 4)) + { + pszExt = TEXT(".cab"); + } + + // Executable files. Assumed to be .dll (more common). + else if (0 == memcmp(pBuffer, "MZ", 2)) + { + pszExt = TEXT(".dll"); + } + + // Icon (*.ico) files. Only assumed because they're common. + else if (0 == memcmp(pBuffer, "\0\0\1\0", 4)) + { + pszExt = TEXT(".ico"); + } + + // Bitmap (*.bmp) files. + else if (0 == memcmp(pBuffer, "BM", 2)) + { + pszExt = TEXT(".bmp"); + } + + // GIF (*.gif) files. + else if (0 == memcmp(pBuffer, "GIF", 3)) + { + pszExt = TEXT(".gif"); + } + + // PING (*.png) files. + else if (0 == memcmp(pBuffer, "\x89PNG", 4)) + { + pszExt = TEXT(".png"); + } + + // TIFF (*.tif) files. + else if (0 == memcmp(pBuffer, "II", 2)) + { + pszExt = TEXT(".tif"); + } + } + + return MakePath(pszDest, cchDest, pszDir, pszName, pszExt); +} \ No newline at end of file diff --git a/libexec/MsiX/MsiX.h b/libexec/MsiX/MsiX.h new file mode 100644 index 0000000..8845737 --- /dev/null +++ b/libexec/MsiX/MsiX.h @@ -0,0 +1,266 @@ + +#pragma once + + +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +using namespace vc_attributes; + +#define SAFE_RELEASE(ptr) if (ptr) { (ptr)->Release(); ptr = NULL; } +#define COLOR_ERROR FOREGROUND_RED | FOREGROUND_INTENSITY + +void usage([Pre(Null=No)] LPCTSTR pszPath, [Pre(Null=No)] FILE* out); +void error([Pre(Null=No)] LPCTSTR pszFormat, ...); +void win32_error(DWORD dwError); + +typedef struct _ARGS +{ + LPTSTR Path; + LPTSTR Directory; + BOOL IncludeExtension; +} ARGS, *LPARGS; + +DWORD ParseArguments(int argc, [Pre(Null=No)] _TCHAR* argv[], [Pre(Null=No)] LPARGS args); +LPTSTR MakePath([Pre(Null=No)] LPTSTR pszDest, size_t cchDest, [Pre(Null=Maybe)] LPCTSTR pszDir, + [Pre(Null=No)] LPCTSTR pszName, [Pre(Null=Maybe)] LPCTSTR pszExt); +UINT GetString(MSIHANDLE hRecord, UINT iField, + [Pre(Null=No)] LPTSTR* ppszProperty, [Pre(Null=No)] DWORD* pcchProperty); +HRESULT SaveStorage([Pre(Null=No)] IStorage* pRootStorage, [Pre(Null=Maybe)] LPCTSTR pszDir, + [Pre(Null=No)] PCWSTR pszName, [Pre(Null=Maybe)] LPCTSTR pszExt); +UINT SaveStream(MSIHANDLE hRecord, [Pre(Null=Maybe)] LPCTSTR pszDir, BOOL fIncludeExt); +LPTSTR MakePathForData([Pre(Null=No)] LPTSTR pszDest, size_t cchDest, + [Pre(Null=Maybe)] LPCTSTR pszDir, [Pre(Null=No)] LPCTSTR pszName, + [Pre(Null=No)] LPCVOID pBuffer, size_t cbBuffer, BOOL fIncludeExt); + +BOOL IsPatch([Pre(Null=No)] IStorage* pStorage); +EXTERN_C const CLSID CLSID_MsiPatch = {0xC1086, 0x0, 0x0, {0xC0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46}}; + +// Simple string conversion classes. +class CW2W +{ +public: + + CW2W(LPCWSTR psz) throw(...) : + m_psz(psz) + { + _ASSERTE(psz); + } + + CW2W(LPCWSTR psz, UINT nCodePage) throw(...) : + m_psz(psz) + { + _ASSERTE(psz); + (void)nCodePage; + } + + ~CW2W() throw() + { + } + + operator LPWSTR() const throw() + { + return const_cast(m_psz); + } + +private: + + LPCWSTR m_psz; + + // Hide the copy constructor and assignment operator. + CW2W( const CW2W& ) throw(); + CW2W& operator=( const CW2W& ) throw(); +}; + +class CW2A +{ +public: + + CW2A(LPCWSTR psz) throw(...) : + m_psz(NULL) + { + _ASSERTE(psz); + Init(psz, CP_ACP); + } + + CW2A(LPCWSTR psz, UINT nCodePage) throw(...) : + m_psz(NULL) + { + _ASSERTE(psz); + Init(psz, nCodePage); + } + + ~CW2A() throw() + { + if (m_psz) + { + delete [] m_psz; + m_psz = NULL; + } + } + + operator LPSTR() const throw() + { + return m_psz; + } + +private: + + LPSTR m_psz; + + void Init(LPCWSTR psz, UINT nCodePage) throw(...) + { + int iErr = NOERROR; + int cch = 0; + + cch = WideCharToMultiByte(nCodePage, 0, psz, -1, NULL, 0, NULL, NULL); + if (cch) + { + // cch includes the NULL terminator character. + m_psz = new CHAR[cch]; // Throws if no memory. + if (!WideCharToMultiByte(nCodePage, 0, psz, -1, m_psz, cch, NULL, NULL)) + { + iErr = (int)GetLastError(); + } + } + else + { + iErr = (int)GetLastError(); + } + + if (ERROR_SUCCESS != iErr) + { + throw iErr; + } + } + + // Hide the copy constructor and assignment operator. + CW2A( const CW2A& ) throw(); + CW2A& operator=( const CW2A& ) throw(); +}; + +class CA2W +{ +public: + + CA2W(LPCSTR psz) throw(...) : + m_psz(NULL) + { + _ASSERTE(psz); + Init(psz, CP_ACP); + } + + CA2W(LPCSTR psz, UINT nCodePage) throw(...) : + m_psz(NULL) + { + _ASSERTE(psz); + Init(psz, nCodePage); + } + + ~CA2W() throw() + { + if (m_psz) + { + delete [] m_psz; + m_psz = NULL; + } + } + + operator LPWSTR() const throw() + { + return m_psz; + } + +private: + + LPWSTR m_psz; + + void Init(LPCSTR psz, UINT nCodePage) throw(...) + { + int iErr = NOERROR; + int cch = 0; + + cch = MultiByteToWideChar(nCodePage, MB_PRECOMPOSED, psz, -1, NULL, 0); + if (cch) + { + // cch includes the NULL terminator character. + m_psz = new WCHAR[cch]; // Throws if no memory. + if (!MultiByteToWideChar(nCodePage, MB_PRECOMPOSED, psz, -1, m_psz, cch)) + { + iErr = (int)GetLastError(); + } + } + else + { + iErr = (int)GetLastError(); + } + + if (ERROR_SUCCESS != iErr) + { + throw iErr; + } + } + + // Hide the copy constructor and assignment operator. + CA2W( const CA2W& ) throw(); + CA2W& operator=( const CA2W& ) throw(); +}; + +class CA2A +{ +public: + + CA2A(LPCSTR psz) throw(...) : + m_psz(psz) + { + _ASSERTE(psz); + } + + CA2A(LPCSTR psz, UINT nCodePage) throw(...) : + m_psz(psz) + { + _ASSERTE(psz); + (void)nCodePage; + } + + ~CA2A() throw() + { + } + + operator LPSTR() const throw() + { + return const_cast(m_psz); + } + +private: + + LPCSTR m_psz; + + // Hide the copy constructor and assignment operator. + CA2A( const CA2A& ) throw(); + CA2A& operator=( const CA2A& ) throw(); +}; + +// Declare macros for proper conversion +// based on whether UNICODE being defined. +#ifdef UNICODE +#define CT2W CW2W +#define CT2A CW2A +#define CW2T CW2W +#define CA2T CA2W +#else +#define CT2W CA2W +#define CT2A CA2A +#define CW2T CW2A +#define CA2T CA2A +#endif // UNICODE diff --git a/libexec/MsiX/MsiX.pdb b/libexec/MsiX/MsiX.pdb new file mode 100644 index 0000000..62166ed Binary files /dev/null and b/libexec/MsiX/MsiX.pdb differ diff --git a/libexec/MsiX/MsiX.rc b/libexec/MsiX/MsiX.rc new file mode 100644 index 0000000..2afca38 --- /dev/null +++ b/libexec/MsiX/MsiX.rc @@ -0,0 +1,103 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,0 + PRODUCTVERSION 1,0,0,0 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", "Extracts transforms and streams from Windows Installer files." + VALUE "CompanyName", "Microsoft Corporation" + VALUE "FileDescription", "MSI Extractor" + VALUE "FileVersion", "1, 0, 0, 0" + VALUE "InternalName", "MsiX" + VALUE "LegalCopyright", "Copyright (C) Microsoft Corporation" + VALUE "OriginalFilename", "MsiX.exe" + VALUE "ProductName", "MSI Extractor" + VALUE "ProductVersion", "1, 0, 0, 0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/libexec/MsiX/MsiX.vcproj b/libexec/MsiX/MsiX.vcproj new file mode 100644 index 0000000..690af73 --- /dev/null +++ b/libexec/MsiX/MsiX.vcproj @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libexec/MsiX/resource.h b/libexec/MsiX/resource.h new file mode 100644 index 0000000..8ad7e4b --- /dev/null +++ b/libexec/MsiX/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by MsiX.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/libexec/ShowVer.exe b/libexec/ShowVer.exe old mode 100644 new mode 100755 diff --git a/libexec/UnRAR.exe b/libexec/UnRAR.exe old mode 100644 new mode 100755 diff --git a/libexec/infozip/funzip.exe b/libexec/infozip/funzip.exe new file mode 100644 index 0000000..4ae6303 Binary files /dev/null and b/libexec/infozip/funzip.exe differ diff --git a/libexec/infozip/unzip.exe b/libexec/infozip/unzip.exe new file mode 100644 index 0000000..750d511 Binary files /dev/null and b/libexec/infozip/unzip.exe differ diff --git a/libexec/infozip/unzipsfx.exe b/libexec/infozip/unzipsfx.exe new file mode 100644 index 0000000..29672f6 Binary files /dev/null and b/libexec/infozip/unzipsfx.exe differ diff --git a/libexec/infozip/zip.exe b/libexec/infozip/zip.exe new file mode 100644 index 0000000..a802e31 Binary files /dev/null and b/libexec/infozip/zip.exe differ diff --git a/libexec/infozip/zipcloak.exe b/libexec/infozip/zipcloak.exe new file mode 100644 index 0000000..b1f307e Binary files /dev/null and b/libexec/infozip/zipcloak.exe differ diff --git a/libexec/infozip/zipnote.exe b/libexec/infozip/zipnote.exe new file mode 100644 index 0000000..fc408d1 Binary files /dev/null and b/libexec/infozip/zipnote.exe differ diff --git a/libexec/infozip/zipsplit.exe b/libexec/infozip/zipsplit.exe new file mode 100644 index 0000000..efc3042 Binary files /dev/null and b/libexec/infozip/zipsplit.exe differ diff --git a/libexec/inifile.exe b/libexec/inifile.exe old mode 100644 new mode 100755 diff --git a/libexec/innounp.exe b/libexec/innounp.exe old mode 100644 new mode 100755