4 * Implementation of the Microsoft Installer (msi.dll)
6 * Copyright 2003 Mike McCormack for CodeWeavers
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
39 #include "wine/debug.h"
40 #include "wine/exception.h"
41 #include "wine/list.h"
43 WINE_DEFAULT_DEBUG_CHANNEL
(msi
);
45 typedef
struct tag_yyinput
71 static LPWSTR COND_GetString
( COND_input
*info
, const struct cond_str
*str
);
72 static LPWSTR COND_GetLiteral
( COND_input
*info
, const struct cond_str
*str
);
73 static int cond_lex
( void *COND_lval
, COND_input
*info
);
74 static int cond_error
( COND_input
*info
, const char *str
);
76 static void *cond_alloc
( COND_input
*cond
, unsigned int sz
);
77 static void *cond_track_mem
( COND_input
*cond
, void *ptr
, unsigned int sz
);
78 static void cond_free
( void *ptr
);
80 static INT compare_int
( INT a
, INT operator
, INT b
);
81 static INT compare_string
( LPCWSTR a
, INT operator
, LPCWSTR b
, BOOL convert
);
83 static BOOL num_from_prop
( LPCWSTR p
, INT
*val
)
85 INT ret
= 0, sign
= 1;
98 if
( *p
< '0' ||
*p
> '9' )
100 ret
= ret
*10 + (*p
- '0');
107 static void value_free
( struct value val
)
109 if
(val.type
!= VALUE_INTEGER
)
110 cond_free
( val.u.
string );
115 %lex
-param
{ COND_input
*info
}
116 %parse
-param
{ COND_input
*info
}
128 %token COND_SPACE COND_EOF
129 %token COND_OR COND_AND COND_NOT COND_XOR COND_IMP COND_EQV
130 %token COND_LT COND_GT COND_EQ COND_NE COND_GE COND_LE
131 %token COND_ILT COND_IGT COND_IEQ COND_INE COND_IGE COND_ILE
132 %token COND_LPAR COND_RPAR COND_TILDA COND_SS COND_ISS
133 %token COND_ILHS COND_IRHS COND_LHS COND_RHS
134 %token COND_PERCENT COND_DOLLARS COND_QUESTION COND_AMPER COND_EXCLAM
135 %token
<str
> COND_IDENT
<str
> COND_NUMBER
<str
> COND_LITER
137 %nonassoc COND_ERROR COND_EOF
139 %type
<bool> expression boolean_term boolean_factor
141 %type
<identifier
> identifier
142 %type
<operator
> operator
149 COND_input
* cond
= (COND_input
*) info
;
154 COND_input
* cond
= (COND_input
*) info
;
155 cond
->result
= MSICONDITION_NONE
;
164 | expression COND_OR boolean_term
168 | expression COND_IMP boolean_term
172 | expression COND_XOR boolean_term
174 $$
= ( $1 ||
$3 ) && !( $1 && $3 );
176 | expression COND_EQV boolean_term
178 $$
= ( $1 && $3 ) ||
( !$1 && !$3 );
187 | boolean_term COND_AND boolean_factor
194 COND_NOT boolean_factor
200 if
($1.type
== VALUE_INTEGER
)
201 $$
= $1.u.integer ?
1 : 0;
203 $$
= $1.u.
string && $1.u.
string[0];
206 | value operator value
208 if
($1.type
== VALUE_INTEGER
&& $3.type
== VALUE_INTEGER
)
210 $$
= compare_int
($1.u.integer
, $2, $3.u.integer
);
212 else if
($1.type
!= VALUE_INTEGER
&& $3.type
!= VALUE_INTEGER
)
214 $$
= compare_string
($1.u.
string, $2, $3.u.
string,
215 $1.type
== VALUE_SYMBOL ||
$3.type
== VALUE_SYMBOL
);
217 else if
($1.type
== VALUE_LITERAL ||
$3.type
== VALUE_LITERAL
)
219 $$
= ($2 == COND_NE ||
$2 == COND_INE
);
221 else if
($1.type
== VALUE_SYMBOL
) /* symbol operator integer */
224 if
(num_from_prop
( $1.u.
string, &num
))
225 $$
= compare_int
( num
, $2, $3.u.integer
);
227 $$
= ($2 == COND_NE ||
$2 == COND_INE
);
229 else
/* integer operator symbol */
232 if
(num_from_prop
( $3.u.
string, &num
))
233 $$
= compare_int
( $1.u.integer
, $2, num
);
235 $$
= ($2 == COND_NE ||
$2 == COND_INE
);
241 | COND_LPAR expression COND_RPAR
248 /* common functions */
249 COND_EQ
{ $$
= COND_EQ
; }
250 | COND_NE
{ $$
= COND_NE
; }
251 | COND_LT
{ $$
= COND_LT
; }
252 | COND_GT
{ $$
= COND_GT
; }
253 | COND_LE
{ $$
= COND_LE
; }
254 | COND_GE
{ $$
= COND_GE
; }
255 | COND_SS
{ $$
= COND_SS
; }
256 | COND_IEQ
{ $$
= COND_IEQ
; }
257 | COND_INE
{ $$
= COND_INE
; }
258 | COND_ILT
{ $$
= COND_ILT
; }
259 | COND_IGT
{ $$
= COND_IGT
; }
260 | COND_ILE
{ $$
= COND_ILE
; }
261 | COND_IGE
{ $$
= COND_IGE
; }
262 | COND_ISS
{ $$
= COND_ISS
; }
263 | COND_LHS
{ $$
= COND_LHS
; }
264 | COND_RHS
{ $$
= COND_RHS
; }
265 | COND_ILHS
{ $$
= COND_ILHS
; }
266 | COND_IRHS
{ $$
= COND_IRHS
; }
272 COND_input
* cond
= (COND_input
*) info
;
275 $$.type
= VALUE_SYMBOL
;
276 $$.u.
string = msi_dup_property
( cond
->package
->db
, $1 );
279 len
= (lstrlenW
($$.u.
string) + 1) * sizeof
(WCHAR
);
280 $$.u.
string = cond_track_mem
( cond
, $$.u.
string, len
);
284 | COND_PERCENT identifier
286 COND_input
* cond
= (COND_input
*) info
;
287 UINT len
= GetEnvironmentVariableW
( $2, NULL
, 0 );
288 $$.type
= VALUE_SYMBOL
;
292 $$.u.
string = cond_alloc
( cond
, len
*sizeof
(WCHAR
) );
295 GetEnvironmentVariableW
( $2, $$.u.
string, len
);
301 COND_input
* cond
= (COND_input
*) info
;
302 $$.type
= VALUE_LITERAL
;
303 $$.u.
string = COND_GetLiteral
( cond
, &$1 );
309 COND_input
* cond
= (COND_input
*) info
;
310 LPWSTR szNum
= COND_GetString
( cond
, &$1 );
313 $$.type
= VALUE_INTEGER
;
314 $$.u.integer
= wcstol
( szNum
, NULL
, 10 );
317 | COND_DOLLARS identifier
319 COND_input
* cond
= (COND_input
*) info
;
320 INSTALLSTATE install
= INSTALLSTATE_UNKNOWN
, action
= INSTALLSTATE_UNKNOWN
;
322 if
(MSI_GetComponentStateW
(cond
->package
, $2, &install
, &action
) != ERROR_SUCCESS
)
324 $$.type
= VALUE_LITERAL
;
329 $$.type
= VALUE_INTEGER
;
330 $$.u.integer
= action
;
334 | COND_QUESTION identifier
336 COND_input
* cond
= (COND_input
*) info
;
337 INSTALLSTATE install
= INSTALLSTATE_UNKNOWN
, action
= INSTALLSTATE_UNKNOWN
;
339 if
(MSI_GetComponentStateW
(cond
->package
, $2, &install
, &action
) != ERROR_SUCCESS
)
341 $$.type
= VALUE_LITERAL
;
346 $$.type
= VALUE_INTEGER
;
347 $$.u.integer
= install
;
351 | COND_AMPER identifier
353 COND_input
* cond
= (COND_input
*) info
;
354 INSTALLSTATE install
, action
;
356 if
(MSI_GetFeatureStateW
(cond
->package
, $2, &install
, &action
) != ERROR_SUCCESS
)
358 $$.type
= VALUE_LITERAL
;
363 $$.type
= VALUE_INTEGER
;
364 $$.u.integer
= action
;
368 | COND_EXCLAM identifier
370 COND_input
* cond
= (COND_input
*) info
;
371 INSTALLSTATE install
= INSTALLSTATE_UNKNOWN
, action
= INSTALLSTATE_UNKNOWN
;
373 if
(MSI_GetFeatureStateW
(cond
->package
, $2, &install
, &action
) != ERROR_SUCCESS
)
375 $$.type
= VALUE_LITERAL
;
380 $$.type
= VALUE_INTEGER
;
381 $$.u.integer
= install
;
390 COND_input
* cond
= (COND_input
*) info
;
391 $$
= COND_GetString
( cond
, &$1 );
400 static int COND_IsAlpha
( WCHAR x
)
402 return
( ( ( x
>= 'A' ) && ( x
<= 'Z' ) ) ||
403 ( ( x
>= 'a' ) && ( x
<= 'z' ) ) ||
407 static int COND_IsNumber
( WCHAR x
)
409 return
( (( x
>= '0' ) && ( x
<= '9' )) ||
(x
=='-') ||
(x
=='.') );
412 static WCHAR
*strstriW
( const WCHAR
*str
, const WCHAR
*sub
)
414 LPWSTR strlower
, sublower
, r
;
415 strlower
= CharLowerW
( strdupW
( str
) );
416 sublower
= CharLowerW
( strdupW
( sub
) );
417 r
= wcsstr
( strlower
, sublower
);
419 r
= (LPWSTR
)str
+ (r
- strlower
);
420 msi_free
( strlower
);
421 msi_free
( sublower
);
425 static BOOL str_is_number
( LPCWSTR str
)
432 for
(i
= 0; i
< lstrlenW
( str
); i
++)
433 if
(!iswdigit
(str
[i
]))
439 static INT compare_substring
( LPCWSTR a
, INT operator
, LPCWSTR b
)
443 /* substring operators return 0 if LHS is missing */
447 /* substring operators return 1 if RHS is missing */
451 /* if both strings contain only numbers, use integer comparison */
452 lhs
= wcstol
(a
, NULL
, 10);
453 rhs
= wcstol
(b
, NULL
, 10);
454 if
(str_is_number
(a
) && str_is_number
(b
))
455 return compare_int
( lhs
, operator
, rhs
);
460 return wcsstr
( a
, b
) != 0;
462 return strstriW
( a
, b
) != 0;
465 int l
= lstrlenW
( a
);
466 int r
= lstrlenW
( b
);
468 return
!wcsncmp
( a
, b
, r
);
472 int l
= lstrlenW
( a
);
473 int r
= lstrlenW
( b
);
475 return
!wcsncmp
( a
+ (l
- r
), b
, r
);
479 int l
= lstrlenW
( a
);
480 int r
= lstrlenW
( b
);
482 return
!wcsnicmp
( a
, b
, r
);
486 int l
= lstrlenW
( a
);
487 int r
= lstrlenW
( b
);
489 return
!wcsnicmp
( a
+ (l
- r
), b
, r
);
492 ERR
("invalid substring operator\n");
498 static INT compare_string
( LPCWSTR a
, INT operator
, LPCWSTR b
, BOOL convert
)
500 if
(operator
>= COND_SS
&& operator
<= COND_RHS
)
501 return compare_substring
( a
, operator
, b
);
503 /* null and empty string are equivalent */
507 if
(convert
&& str_is_number
(a
) && str_is_number
(b
))
508 return compare_int
( wcstol
(a
, NULL
, 10), operator
, wcstol
(b
, NULL
, 10) );
510 /* a or b may be NULL */
514 return wcscmp
( a
, b
) < 0;
516 return wcscmp
( a
, b
) > 0;
518 return wcscmp
( a
, b
) == 0;
520 return wcscmp
( a
, b
) != 0;
522 return wcscmp
( a
, b
) >= 0;
524 return wcscmp
( a
, b
) <= 0;
526 return wcsicmp
( a
, b
) < 0;
528 return wcsicmp
( a
, b
) > 0;
530 return wcsicmp
( a
, b
) == 0;
532 return wcsicmp
( a
, b
) != 0;
534 return wcsicmp
( a
, b
) >= 0;
536 return wcsicmp
( a
, b
) <= 0;
538 ERR
("invalid string operator\n");
545 static INT compare_int
( INT a
, INT operator
, INT b
)
569 return
( a
& b
) ?
1 : 0;
571 return
( ( a
& 0xffff ) == b
) ?
1 : 0;
573 return
( ( (a
>>16) & 0xffff ) == b
) ?
1 : 0;
575 ERR
("invalid integer operator\n");
582 static int COND_IsIdent
( WCHAR x
)
584 return
( COND_IsAlpha
( x
) || COND_IsNumber
( x
) ||
( x
== '_' )
585 ||
( x
== '#' ) ||
(x
== '.') );
588 static int COND_GetOperator
( COND_input
*cond
)
590 static const struct {
594 { L
"~<=", COND_ILE
},
595 { L
"~><", COND_ISS
},
596 { L
"~>>", COND_IRHS
},
597 { L
"~<>", COND_INE
},
598 { L
"~>=", COND_IGE
},
599 { L
"~<<", COND_ILHS
},
613 LPCWSTR p
= &cond
->str
[cond
->n
];
618 len
= lstrlenW
( table
[i
].str
);
619 if
( !len ||
0 == wcsncmp
( table
[i
].str
, p
, len
) )
627 static int COND_GetOne
( struct cond_str
*str
, COND_input
*cond
)
632 str
->data
= &cond
->str
[cond
->n
];
639 case
'(': rc
= COND_LPAR
; break
;
640 case
')': rc
= COND_RPAR
; break
;
641 case
'&': rc
= COND_AMPER
; break
;
642 case
'!': rc
= COND_EXCLAM
; break
;
643 case
'$': rc
= COND_DOLLARS
; break
;
644 case
'?': rc
= COND_QUESTION
; break
;
645 case
'%': rc
= COND_PERCENT
; break
;
646 case
' ': rc
= COND_SPACE
; break
;
647 case
'=': rc
= COND_EQ
; break
;
652 rc
= COND_GetOperator
( cond
);
668 LPCWSTR p
= wcschr
( str
->data
+ 1, '"' );
669 if
(!p
) return COND_ERROR
;
670 len
= p
- str
->data
+ 1;
673 else if
( COND_IsAlpha
( ch
) )
675 while
( COND_IsIdent
( str
->data
[len
] ) )
681 if
( !wcsnicmp
( str
->data
, L
"NOT", len
) )
683 else if
( !wcsnicmp
( str
->data
, L
"AND", len
) )
685 else if
( !wcsnicmp
( str
->data
, L
"XOR", len
) )
687 else if
( !wcsnicmp
( str
->data
, L
"EQV", len
) )
689 else if
( !wcsnicmp
( str
->data
, L
"IMP", len
) )
692 else if
( (len
== 2) && !wcsnicmp
( str
->data
, L
"OR", len
) )
695 else if
( COND_IsNumber
( ch
) )
697 while
( COND_IsNumber
( str
->data
[len
] ) )
703 ERR
("Got unknown character %c(%x)\n",ch
,ch
);
713 static int cond_lex
( void *COND_lval
, COND_input
*cond
)
716 struct cond_str
*str
= COND_lval
;
719 rc
= COND_GetOne
( str
, cond
);
720 } while
(rc
== COND_SPACE
);
725 static LPWSTR COND_GetString
( COND_input
*cond
, const struct cond_str
*str
)
729 ret
= cond_alloc
( cond
, (str
->len
+1) * sizeof
(WCHAR
) );
732 memcpy
( ret
, str
->data
, str
->len
* sizeof
(WCHAR
));
735 TRACE
("Got identifier %s\n",debugstr_w
(ret
));
739 static LPWSTR COND_GetLiteral
( COND_input
*cond
, const struct cond_str
*str
)
743 ret
= cond_alloc
( cond
, (str
->len
-1) * sizeof
(WCHAR
) );
746 memcpy
( ret
, str
->data
+1, (str
->len
-2) * sizeof
(WCHAR
) );
749 TRACE
("Got literal %s\n",debugstr_w
(ret
));
753 static void *cond_alloc
( COND_input
*cond
, unsigned int sz
)
757 mem
= msi_alloc
( sizeof
(struct list
) + sz
);
761 list_add_head
( &(cond
->mem
), mem
);
765 static void *cond_track_mem
( COND_input
*cond
, void *ptr
, unsigned int sz
)
772 new_ptr
= cond_alloc
( cond
, sz
);
779 memcpy
( new_ptr
, ptr
, sz
);
784 static void cond_free
( void *ptr
)
786 struct list
*mem
= (struct list
*)ptr
- 1;
795 static int cond_error
( COND_input
*info
, const char *str
)
801 MSICONDITION MSI_EvaluateConditionW
( MSIPACKAGE
*package
, LPCWSTR szCondition
)
805 struct list
*mem
, *safety
;
807 TRACE
("%s\n", debugstr_w
( szCondition
) );
809 if
(szCondition
== NULL
) return MSICONDITION_NONE
;
811 cond.package
= package
;
812 cond.str
= szCondition
;
814 cond.result
= MSICONDITION_ERROR
;
816 list_init
( &cond.mem
);
818 if
( !cond_parse
( &cond
) )
821 r
= MSICONDITION_ERROR
;
823 LIST_FOR_EACH_SAFE
( mem
, safety
, &cond.mem
)
825 /* The tracked memory lives directly after the list struct */
827 if
( r
!= MSICONDITION_ERROR
)
828 WARN
( "condition parser failed to free up some memory: %p\n", ptr
);
832 TRACE
("%i <- %s\n", r
, debugstr_w
(szCondition
));
836 MSICONDITION WINAPI MsiEvaluateConditionW
( MSIHANDLE hInstall
, LPCWSTR szCondition
)
841 package
= msihandle2msiinfo
( hInstall
, MSIHANDLETYPE_PACKAGE
);
846 if
(!(remote
= msi_get_remote
(hInstall
)))
847 return MSICONDITION_ERROR
;
850 return MSICONDITION_NONE
;
854 ret
= remote_EvaluateCondition
(remote
, szCondition
);
858 ret
= GetExceptionCode
();
865 ret
= MSI_EvaluateConditionW
( package
, szCondition
);
866 msiobj_release
( &package
->hdr
);
870 MSICONDITION WINAPI MsiEvaluateConditionA
( MSIHANDLE hInstall
, LPCSTR szCondition
)
872 LPWSTR szwCond
= NULL
;
875 szwCond
= strdupAtoW
( szCondition
);
876 if
( szCondition
&& !szwCond
)
877 return MSICONDITION_ERROR
;
879 r
= MsiEvaluateConditionW
( hInstall
, szwCond
);