diff options
Diffstat (limited to 'libmsi/cond.y')
-rw-r--r-- | libmsi/cond.y | 898 |
1 files changed, 898 insertions, 0 deletions
diff --git a/libmsi/cond.y b/libmsi/cond.y new file mode 100644 index 0000000..97bb002 --- /dev/null +++ b/libmsi/cond.y @@ -0,0 +1,898 @@ +%{ + +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2003 Mike McCormack for CodeWeavers + * + * 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 + */ + +#define COBJMACROS + +#include "config.h" + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "oleauto.h" + +#include "msipriv.h" +#include "msiserver.h" +#include "wine/debug.h" +#include "wine/unicode.h" +#include "wine/list.h" + +#define YYLEX_PARAM info +#define YYPARSE_PARAM info + +static int cond_error(const char *str); + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +typedef struct tag_yyinput +{ + MSIPACKAGE *package; + LPCWSTR str; + INT n; + MSICONDITION result; + struct list mem; +} COND_input; + +struct cond_str { + LPCWSTR data; + INT len; +}; + +static LPWSTR COND_GetString( COND_input *info, const struct cond_str *str ); +static LPWSTR COND_GetLiteral( COND_input *info, const struct cond_str *str ); +static int cond_lex( void *COND_lval, COND_input *info); + +static void *cond_alloc( COND_input *cond, unsigned int sz ); +static void *cond_track_mem( COND_input *cond, void *ptr, unsigned int sz ); +static void cond_free( void *ptr ); + +static INT compare_int( INT a, INT operator, INT b ); +static INT compare_string( LPCWSTR a, INT operator, LPCWSTR b, BOOL convert ); + +static INT compare_and_free_strings( LPWSTR a, INT op, LPWSTR b, BOOL convert ) +{ + INT r; + + r = compare_string( a, op, b, convert ); + cond_free( a ); + cond_free( b ); + return r; +} + +static BOOL num_from_prop( LPCWSTR p, INT *val ) +{ + INT ret = 0, sign = 1; + + if (!p) + return FALSE; + if (*p == '-') + { + sign = -1; + p++; + } + if (!*p) + return FALSE; + while (*p) + { + if( *p < '0' || *p > '9' ) + return FALSE; + ret = ret*10 + (*p - '0'); + p++; + } + *val = ret*sign; + return TRUE; +} + +%} + +%pure-parser + +%union +{ + struct cond_str str; + LPWSTR string; + INT value; +} + +%token COND_SPACE COND_EOF +%token COND_OR COND_AND COND_NOT COND_XOR COND_IMP COND_EQV +%token COND_LT COND_GT COND_EQ COND_NE COND_GE COND_LE +%token COND_ILT COND_IGT COND_IEQ COND_INE COND_IGE COND_ILE +%token COND_LPAR COND_RPAR COND_TILDA COND_SS COND_ISS +%token COND_ILHS COND_IRHS COND_LHS COND_RHS +%token COND_PERCENT COND_DOLLARS COND_QUESTION COND_AMPER COND_EXCLAM +%token <str> COND_IDENT <str> COND_NUMBER <str> COND_LITER + +%nonassoc COND_ERROR COND_EOF + +%type <value> expression boolean_term boolean_factor +%type <value> value_i integer operator +%type <string> identifier symbol_s value_s literal + +%% + +condition: + expression + { + COND_input* cond = (COND_input*) info; + cond->result = $1; + } + | /* empty */ + { + COND_input* cond = (COND_input*) info; + cond->result = MSICONDITION_NONE; + } + ; + +expression: + boolean_term + { + $$ = $1; + } + | expression COND_OR boolean_term + { + $$ = $1 || $3; + } + | expression COND_IMP boolean_term + { + $$ = !$1 || $3; + } + | expression COND_XOR boolean_term + { + $$ = ( $1 || $3 ) && !( $1 && $3 ); + } + | expression COND_EQV boolean_term + { + $$ = ( $1 && $3 ) || ( !$1 && !$3 ); + } + ; + +boolean_term: + boolean_factor + { + $$ = $1; + } + | boolean_term COND_AND boolean_factor + { + $$ = $1 && $3; + } + ; + +boolean_factor: + COND_NOT boolean_factor + { + $$ = $2 ? 0 : 1; + } + | value_i + { + $$ = $1 ? 1 : 0; + } + | value_s + { + $$ = ($1 && $1[0]) ? 1 : 0; + cond_free( $1 ); + } + | value_i operator value_i + { + $$ = compare_int( $1, $2, $3 ); + } + | symbol_s operator value_i + { + int num; + if (num_from_prop( $1, &num )) + $$ = compare_int( num, $2, $3 ); + else + $$ = ($2 == COND_NE || $2 == COND_INE ); + cond_free( $1 ); + } + | value_i operator symbol_s + { + int num; + if (num_from_prop( $3, &num )) + $$ = compare_int( $1, $2, num ); + else + $$ = ($2 == COND_NE || $2 == COND_INE ); + cond_free( $3 ); + } + | symbol_s operator symbol_s + { + $$ = compare_and_free_strings( $1, $2, $3, TRUE ); + } + | symbol_s operator literal + { + $$ = compare_and_free_strings( $1, $2, $3, TRUE ); + } + | literal operator symbol_s + { + $$ = compare_and_free_strings( $1, $2, $3, TRUE ); + } + | literal operator literal + { + $$ = compare_and_free_strings( $1, $2, $3, FALSE ); + } + | literal operator value_i + { + $$ = 0; + cond_free( $1 ); + } + | value_i operator literal + { + $$ = 0; + cond_free( $3 ); + } + | COND_LPAR expression COND_RPAR + { + $$ = $2; + } + ; + +operator: + /* common functions */ + COND_EQ { $$ = COND_EQ; } + | COND_NE { $$ = COND_NE; } + | COND_LT { $$ = COND_LT; } + | COND_GT { $$ = COND_GT; } + | COND_LE { $$ = COND_LE; } + | COND_GE { $$ = COND_GE; } + | COND_SS { $$ = COND_SS; } + | COND_IEQ { $$ = COND_IEQ; } + | COND_INE { $$ = COND_INE; } + | COND_ILT { $$ = COND_ILT; } + | COND_IGT { $$ = COND_IGT; } + | COND_ILE { $$ = COND_ILE; } + | COND_IGE { $$ = COND_IGE; } + | COND_ISS { $$ = COND_ISS; } + | COND_LHS { $$ = COND_LHS; } + | COND_RHS { $$ = COND_RHS; } + | COND_ILHS { $$ = COND_ILHS; } + | COND_IRHS { $$ = COND_IRHS; } + ; + +value_s: + symbol_s + { + $$ = $1; + } + | literal + { + $$ = $1; + } + ; + +literal: + COND_LITER + { + COND_input* cond = (COND_input*) info; + $$ = COND_GetLiteral( cond, &$1 ); + if( !$$ ) + YYABORT; + } + ; + +value_i: + integer + { + $$ = $1; + } + | COND_DOLLARS identifier + { + COND_input* cond = (COND_input*) info; + INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN; + + MSI_GetComponentStateW(cond->package, $2, &install, &action ); + $$ = action; + cond_free( $2 ); + } + | COND_QUESTION identifier + { + COND_input* cond = (COND_input*) info; + INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN; + + MSI_GetComponentStateW(cond->package, $2, &install, &action ); + $$ = install; + cond_free( $2 ); + } + | COND_AMPER identifier + { + COND_input* cond = (COND_input*) info; + INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN; + + MSI_GetFeatureStateW(cond->package, $2, &install, &action ); + if (action == INSTALLSTATE_UNKNOWN) + $$ = MSICONDITION_FALSE; + else + $$ = action; + + cond_free( $2 ); + } + | COND_EXCLAM identifier + { + COND_input* cond = (COND_input*) info; + INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN; + + MSI_GetFeatureStateW(cond->package, $2, &install, &action ); + $$ = install; + cond_free( $2 ); + } + ; + +symbol_s: + identifier + { + COND_input* cond = (COND_input*) info; + UINT len; + + $$ = msi_dup_property( cond->package->db, $1 ); + if ($$) + { + len = (lstrlenW($$) + 1) * sizeof (WCHAR); + $$ = cond_track_mem( cond, $$, len ); + } + cond_free( $1 ); + } + | COND_PERCENT identifier + { + COND_input* cond = (COND_input*) info; + UINT len = GetEnvironmentVariableW( $2, NULL, 0 ); + $$ = NULL; + if (len++) + { + $$ = cond_alloc( cond, len*sizeof (WCHAR) ); + if( !$$ ) + YYABORT; + GetEnvironmentVariableW( $2, $$, len ); + } + cond_free( $2 ); + } + ; + +identifier: + COND_IDENT + { + COND_input* cond = (COND_input*) info; + $$ = COND_GetString( cond, &$1 ); + if( !$$ ) + YYABORT; + } + ; + +integer: + COND_NUMBER + { + COND_input* cond = (COND_input*) info; + LPWSTR szNum = COND_GetString( cond, &$1 ); + if( !szNum ) + YYABORT; + $$ = atoiW( szNum ); + cond_free( szNum ); + } + ; + +%% + + +static int COND_IsAlpha( WCHAR x ) +{ + return( ( ( x >= 'A' ) && ( x <= 'Z' ) ) || + ( ( x >= 'a' ) && ( x <= 'z' ) ) || + ( ( x == '_' ) ) ); +} + +static int COND_IsNumber( WCHAR x ) +{ + return( (( x >= '0' ) && ( x <= '9' )) || (x =='-') || (x =='.') ); +} + +static WCHAR *strstriW( const WCHAR *str, const WCHAR *sub ) +{ + LPWSTR strlower, sublower, r; + strlower = CharLowerW( strdupW( str ) ); + sublower = CharLowerW( strdupW( sub ) ); + r = strstrW( strlower, sublower ); + if (r) + r = (LPWSTR)str + (r - strlower); + msi_free( strlower ); + msi_free( sublower ); + return r; +} + +static BOOL str_is_number( LPCWSTR str ) +{ + int i; + + if (!*str) + return FALSE; + + for (i = 0; i < lstrlenW( str ); i++) + if (!isdigitW(str[i])) + return FALSE; + + return TRUE; +} + +static INT compare_substring( LPCWSTR a, INT operator, LPCWSTR b ) +{ + int lhs, rhs; + + /* substring operators return 0 if LHS is missing */ + if (!a || !*a) + return 0; + + /* substring operators return 1 if RHS is missing */ + if (!b || !*b) + return 1; + + /* if both strings contain only numbers, use integer comparison */ + lhs = atoiW(a); + rhs = atoiW(b); + if (str_is_number(a) && str_is_number(b)) + return compare_int( lhs, operator, rhs ); + + switch (operator) + { + case COND_SS: + return strstrW( a, b ) != 0; + case COND_ISS: + return strstriW( a, b ) != 0; + case COND_LHS: + { + int l = strlenW( a ); + int r = strlenW( b ); + if (r > l) return 0; + return !strncmpW( a, b, r ); + } + case COND_RHS: + { + int l = strlenW( a ); + int r = strlenW( b ); + if (r > l) return 0; + return !strncmpW( a + (l - r), b, r ); + } + case COND_ILHS: + { + int l = strlenW( a ); + int r = strlenW( b ); + if (r > l) return 0; + return !strncmpiW( a, b, r ); + } + case COND_IRHS: + { + int l = strlenW( a ); + int r = strlenW( b ); + if (r > l) return 0; + return !strncmpiW( a + (l - r), b, r ); + } + default: + ERR("invalid substring operator\n"); + return 0; + } + return 0; +} + +static INT compare_string( LPCWSTR a, INT operator, LPCWSTR b, BOOL convert ) +{ + if (operator >= COND_SS && operator <= COND_RHS) + return compare_substring( a, operator, b ); + + /* null and empty string are equivalent */ + if (!a) a = szEmpty; + if (!b) b = szEmpty; + + if (convert && str_is_number(a) && str_is_number(b)) + return compare_int( atoiW(a), operator, atoiW(b) ); + + /* a or b may be NULL */ + switch (operator) + { + case COND_LT: + return strcmpW( a, b ) < 0; + case COND_GT: + return strcmpW( a, b ) > 0; + case COND_EQ: + return strcmpW( a, b ) == 0; + case COND_NE: + return strcmpW( a, b ) != 0; + case COND_GE: + return strcmpW( a, b ) >= 0; + case COND_LE: + return strcmpW( a, b ) <= 0; + case COND_ILT: + return strcmpiW( a, b ) < 0; + case COND_IGT: + return strcmpiW( a, b ) > 0; + case COND_IEQ: + return strcmpiW( a, b ) == 0; + case COND_INE: + return strcmpiW( a, b ) != 0; + case COND_IGE: + return strcmpiW( a, b ) >= 0; + case COND_ILE: + return strcmpiW( a, b ) <= 0; + default: + ERR("invalid string operator\n"); + return 0; + } + return 0; +} + + +static INT compare_int( INT a, INT operator, INT b ) +{ + switch (operator) + { + case COND_LT: + case COND_ILT: + return a < b; + case COND_GT: + case COND_IGT: + return a > b; + case COND_EQ: + case COND_IEQ: + return a == b; + case COND_NE: + case COND_INE: + return a != b; + case COND_GE: + case COND_IGE: + return a >= b; + case COND_LE: + case COND_ILE: + return a <= b; + case COND_SS: + case COND_ISS: + return ( a & b ) ? 1 : 0; + case COND_RHS: + return ( ( a & 0xffff ) == b ) ? 1 : 0; + case COND_LHS: + return ( ( (a>>16) & 0xffff ) == b ) ? 1 : 0; + default: + ERR("invalid integer operator\n"); + return 0; + } + return 0; +} + + +static int COND_IsIdent( WCHAR x ) +{ + return( COND_IsAlpha( x ) || COND_IsNumber( x ) || ( x == '_' ) + || ( x == '#' ) || (x == '.') ); +} + +static int COND_GetOperator( COND_input *cond ) +{ + static const struct { + const WCHAR str[4]; + int id; + } table[] = { + { {'~','<','=',0}, COND_ILE }, + { {'~','>','<',0}, COND_ISS }, + { {'~','>','>',0}, COND_IRHS }, + { {'~','<','>',0}, COND_INE }, + { {'~','>','=',0}, COND_IGE }, + { {'~','<','<',0}, COND_ILHS }, + { {'~','=',0}, COND_IEQ }, + { {'~','<',0}, COND_ILT }, + { {'~','>',0}, COND_IGT }, + { {'>','=',0}, COND_GE }, + { {'>','<',0}, COND_SS }, + { {'<','<',0}, COND_LHS }, + { {'<','>',0}, COND_NE }, + { {'<','=',0}, COND_LE }, + { {'>','>',0}, COND_RHS }, + { {'>',0}, COND_GT }, + { {'<',0}, COND_LT }, + { {0}, 0 } + }; + LPCWSTR p = &cond->str[cond->n]; + int i = 0, len; + + while ( 1 ) + { + len = lstrlenW( table[i].str ); + if ( !len || 0 == strncmpW( table[i].str, p, len ) ) + break; + i++; + } + cond->n += len; + return table[i].id; +} + +static int COND_GetOne( struct cond_str *str, COND_input *cond ) +{ + int rc, len = 1; + WCHAR ch; + + str->data = &cond->str[cond->n]; + + ch = str->data[0]; + + switch( ch ) + { + case 0: return 0; + case '(': rc = COND_LPAR; break; + case ')': rc = COND_RPAR; break; + case '&': rc = COND_AMPER; break; + case '!': rc = COND_EXCLAM; break; + case '$': rc = COND_DOLLARS; break; + case '?': rc = COND_QUESTION; break; + case '%': rc = COND_PERCENT; break; + case ' ': rc = COND_SPACE; break; + case '=': rc = COND_EQ; break; + + case '~': + case '<': + case '>': + rc = COND_GetOperator( cond ); + if (!rc) + rc = COND_ERROR; + return rc; + default: + rc = 0; + } + + if ( rc ) + { + cond->n += len; + return rc; + } + + if (ch == '"' ) + { + LPCWSTR p = strchrW( str->data + 1, '"' ); + if (!p) return COND_ERROR; + len = p - str->data + 1; + rc = COND_LITER; + } + else if( COND_IsAlpha( ch ) ) + { + static const WCHAR szNot[] = {'N','O','T',0}; + static const WCHAR szAnd[] = {'A','N','D',0}; + static const WCHAR szXor[] = {'X','O','R',0}; + static const WCHAR szEqv[] = {'E','Q','V',0}; + static const WCHAR szImp[] = {'I','M','P',0}; + static const WCHAR szOr[] = {'O','R',0}; + + while( COND_IsIdent( str->data[len] ) ) + len++; + rc = COND_IDENT; + + if ( len == 3 ) + { + if ( !strncmpiW( str->data, szNot, len ) ) + rc = COND_NOT; + else if( !strncmpiW( str->data, szAnd, len ) ) + rc = COND_AND; + else if( !strncmpiW( str->data, szXor, len ) ) + rc = COND_XOR; + else if( !strncmpiW( str->data, szEqv, len ) ) + rc = COND_EQV; + else if( !strncmpiW( str->data, szImp, len ) ) + rc = COND_IMP; + } + else if( (len == 2) && !strncmpiW( str->data, szOr, len ) ) + rc = COND_OR; + } + else if( COND_IsNumber( ch ) ) + { + while( COND_IsNumber( str->data[len] ) ) + len++; + rc = COND_NUMBER; + } + else + { + ERR("Got unknown character %c(%x)\n",ch,ch); + return COND_ERROR; + } + + cond->n += len; + str->len = len; + + return rc; +} + +static int cond_lex( void *COND_lval, COND_input *cond ) +{ + int rc; + struct cond_str *str = COND_lval; + + do { + rc = COND_GetOne( str, cond ); + } while (rc == COND_SPACE); + + return rc; +} + +static LPWSTR COND_GetString( COND_input *cond, const struct cond_str *str ) +{ + LPWSTR ret; + + ret = cond_alloc( cond, (str->len+1) * sizeof (WCHAR) ); + if( ret ) + { + memcpy( ret, str->data, str->len * sizeof(WCHAR)); + ret[str->len]=0; + } + TRACE("Got identifier %s\n",debugstr_w(ret)); + return ret; +} + +static LPWSTR COND_GetLiteral( COND_input *cond, const struct cond_str *str ) +{ + LPWSTR ret; + + ret = cond_alloc( cond, (str->len-1) * sizeof (WCHAR) ); + if( ret ) + { + memcpy( ret, str->data+1, (str->len-2) * sizeof(WCHAR) ); + ret[str->len - 2]=0; + } + TRACE("Got literal %s\n",debugstr_w(ret)); + return ret; +} + +static void *cond_alloc( COND_input *cond, unsigned int sz ) +{ + struct list *mem; + + mem = msi_alloc( sizeof (struct list) + sz ); + if( !mem ) + return NULL; + + list_add_head( &(cond->mem), mem ); + return mem + 1; +} + +static void *cond_track_mem( COND_input *cond, void *ptr, unsigned int sz ) +{ + void *new_ptr; + + if( !ptr ) + return ptr; + + new_ptr = cond_alloc( cond, sz ); + if( !new_ptr ) + { + msi_free( ptr ); + return NULL; + } + + memcpy( new_ptr, ptr, sz ); + msi_free( ptr ); + return new_ptr; +} + +static void cond_free( void *ptr ) +{ + struct list *mem = (struct list *)ptr - 1; + + if( ptr ) + { + list_remove( mem ); + msi_free( mem ); + } +} + +static int cond_error(const char *str) +{ + TRACE("%s\n", str ); + return 0; +} + +MSICONDITION MSI_EvaluateConditionW( MSIPACKAGE *package, LPCWSTR szCondition ) +{ + COND_input cond; + MSICONDITION r; + struct list *mem, *safety; + + TRACE("%s\n", debugstr_w( szCondition ) ); + + if (szCondition == NULL) return MSICONDITION_NONE; + + cond.package = package; + cond.str = szCondition; + cond.n = 0; + cond.result = MSICONDITION_ERROR; + + list_init( &cond.mem ); + + if ( !cond_parse( &cond ) ) + r = cond.result; + else + r = MSICONDITION_ERROR; + + LIST_FOR_EACH_SAFE( mem, safety, &cond.mem ) + { + /* The tracked memory lives directly after the list struct */ + void *ptr = mem + 1; + if ( r != MSICONDITION_ERROR ) + WARN( "condition parser failed to free up some memory: %p\n", ptr ); + cond_free( ptr ); + } + + TRACE("%i <- %s\n", r, debugstr_w(szCondition)); + return r; +} + +MSICONDITION WINAPI MsiEvaluateConditionW( MSIHANDLE hInstall, LPCWSTR szCondition ) +{ + MSIPACKAGE *package; + UINT ret; + + package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE); + if( !package ) + { + HRESULT hr; + BSTR condition; + IWineMsiRemotePackage *remote_package; + + remote_package = (IWineMsiRemotePackage *)msi_get_remote( hInstall ); + if (!remote_package) + return MSICONDITION_ERROR; + + condition = SysAllocString( szCondition ); + if (!condition) + { + IWineMsiRemotePackage_Release( remote_package ); + return ERROR_OUTOFMEMORY; + } + + hr = IWineMsiRemotePackage_EvaluateCondition( remote_package, condition ); + + SysFreeString( condition ); + IWineMsiRemotePackage_Release( remote_package ); + + if (FAILED(hr)) + { + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) + return HRESULT_CODE(hr); + + return ERROR_FUNCTION_FAILED; + } + + return ERROR_SUCCESS; + } + + ret = MSI_EvaluateConditionW( package, szCondition ); + msiobj_release( &package->hdr ); + return ret; +} + +MSICONDITION WINAPI MsiEvaluateConditionA( MSIHANDLE hInstall, LPCSTR szCondition ) +{ + LPWSTR szwCond = NULL; + MSICONDITION r; + + szwCond = strdupAtoW( szCondition ); + if( szCondition && !szwCond ) + return MSICONDITION_ERROR; + + r = MsiEvaluateConditionW( hInstall, szwCond ); + msi_free( szwCond ); + return r; +} |