Create a stub function to apply a single table transform and call it
[wine] / dlls / msi / cond.y
1 %{
2
3 /*
4  * Implementation of the Microsoft Installer (msi.dll)
5  *
6  * Copyright 2003 Mike McCormack for CodeWeavers
7  *
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.
12  *
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.
17  *
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include "config.h"
24
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28
29 #include "windef.h"
30 #include "winbase.h"
31 #include "wine/debug.h"
32 #include "wine/unicode.h"
33
34 #include "msi.h"
35 #include "msiquery.h"
36 #include "msipriv.h"
37
38 #define YYLEX_PARAM info
39 #define YYPARSE_PARAM info
40
41 static int COND_error(const char *str);
42
43 WINE_DEFAULT_DEBUG_CHANNEL(msi);
44
45 typedef struct tag_yyinput
46 {
47     MSIPACKAGE *package;
48     LPCWSTR str;
49     INT    n;
50     MSICONDITION result;
51 } COND_input;
52
53 struct cond_str {
54     LPCWSTR data;
55     INT len;
56 };
57
58 static LPWSTR COND_GetString( struct cond_str *str );
59 static LPWSTR COND_GetLiteral( struct cond_str *str );
60 static int COND_lex( void *COND_lval, COND_input *info);
61
62 typedef INT (*comp_int)(INT a, INT b);
63 typedef INT (*comp_str)(LPWSTR a, LPWSTR b, BOOL caseless);
64 typedef INT (*comp_m1)(LPWSTR a,int b);
65 typedef INT (*comp_m2)(int a,LPWSTR b);
66
67 static INT comp_lt_i(INT a, INT b);
68 static INT comp_gt_i(INT a, INT b);
69 static INT comp_le_i(INT a, INT b);
70 static INT comp_ge_i(INT a, INT b);
71 static INT comp_eq_i(INT a, INT b);
72 static INT comp_ne_i(INT a, INT b);
73 static INT comp_bitand(INT a, INT b);
74 static INT comp_highcomp(INT a, INT b);
75 static INT comp_lowcomp(INT a, INT b);
76
77 static INT comp_eq_s(LPWSTR a, LPWSTR b, BOOL casless);
78 static INT comp_ne_s(LPWSTR a, LPWSTR b, BOOL casless);
79 static INT comp_lt_s(LPWSTR a, LPWSTR b, BOOL casless);
80 static INT comp_gt_s(LPWSTR a, LPWSTR b, BOOL casless);
81 static INT comp_le_s(LPWSTR a, LPWSTR b, BOOL casless);
82 static INT comp_ge_s(LPWSTR a, LPWSTR b, BOOL casless);
83 static INT comp_substring(LPWSTR a, LPWSTR b, BOOL casless);
84 static INT comp_start(LPWSTR a, LPWSTR b, BOOL casless);
85 static INT comp_end(LPWSTR a, LPWSTR b, BOOL casless);
86
87 static INT comp_eq_m1(LPWSTR a, INT b);
88 static INT comp_ne_m1(LPWSTR a, INT b);
89 static INT comp_lt_m1(LPWSTR a, INT b);
90 static INT comp_gt_m1(LPWSTR a, INT b);
91 static INT comp_le_m1(LPWSTR a, INT b);
92 static INT comp_ge_m1(LPWSTR a, INT b);
93
94 static INT comp_eq_m2(INT a, LPWSTR b);
95 static INT comp_ne_m2(INT a, LPWSTR b);
96 static INT comp_lt_m2(INT a, LPWSTR b);
97 static INT comp_gt_m2(INT a, LPWSTR b);
98 static INT comp_le_m2(INT a, LPWSTR b);
99 static INT comp_ge_m2(INT a, LPWSTR b);
100
101 %}
102
103 %pure-parser
104
105 %union
106 {
107     struct cond_str str;
108     LPWSTR    string;
109     INT       value;
110     comp_int  fn_comp_int;
111     comp_str  fn_comp_str;
112     comp_m1   fn_comp_m1;
113     comp_m2   fn_comp_m2;
114 }
115
116 %token COND_SPACE COND_EOF COND_SPACE
117 %token COND_OR COND_AND COND_NOT
118 %token COND_LT COND_GT COND_EQ 
119 %token COND_LPAR COND_RPAR COND_TILDA
120 %token COND_PERCENT COND_DOLLARS COND_QUESTION COND_AMPER COND_EXCLAM
121 %token <str> COND_IDENT <str> COND_NUMBER <str> COND_LITER
122
123 %nonassoc COND_EOF COND_ERROR
124
125 %type <value> expression boolean_term boolean_factor 
126 %type <value> term value_i symbol_i integer
127 %type <string> identifier value_s symbol_s literal
128 %type <fn_comp_int> comp_op_i
129 %type <fn_comp_str> comp_op_s 
130 %type <fn_comp_m1>  comp_op_m1 
131 %type <fn_comp_m2>  comp_op_m2
132
133 %%
134
135 condition:
136     expression
137         {
138             COND_input* cond = (COND_input*) info;
139             cond->result = $1;
140         }
141     ;
142
143 expression:
144     boolean_term 
145         {
146             $$ = $1;
147         }
148   | boolean_term COND_OR expression
149         {
150             $$ = $1 || $3;
151         }
152     ;
153
154 boolean_term:
155     boolean_factor
156         {
157             $$ = $1;
158         }
159     | boolean_term COND_AND boolean_factor
160         {
161             $$ = $1 && $3;
162         }
163     ;
164
165 boolean_factor:
166     term
167         {
168             $$ = $1;
169         }
170   | COND_NOT term
171         {
172             $$ = ! $2;
173         }
174     ;
175
176
177 term:
178     value_i
179         {
180             $$ = $1;
181         }
182   | value_s
183         {
184             $$ = ($1 && $1[0]) ? MSICONDITION_TRUE : MSICONDITION_FALSE;
185             msi_free( $1 );
186         }
187   | value_i comp_op_i value_i
188         {
189             $$ = $2( $1, $3 );
190         }
191   | value_s comp_op_s value_s
192         {
193             $$ = $2( $1, $3, FALSE );
194             msi_free( $1 );
195             msi_free( $3 );
196         }
197   | value_s COND_TILDA comp_op_s value_s
198         {
199             $$ = $3( $1, $4, TRUE );
200             msi_free( $1 );
201             msi_free( $4 );
202         }
203   | value_s comp_op_m1 value_i
204         {
205             $$ = $2( $1, $3 );
206             msi_free( $1 );
207         }
208   | value_i comp_op_m2 value_s
209         {
210             $$ = $2( $1, $3 );
211             msi_free( $3 );
212         }
213   | COND_LPAR expression COND_RPAR
214         {
215             $$ = $2;
216         }
217     ;
218
219 comp_op_i:
220     /* common functions */
221    COND_EQ
222         {
223             $$ = comp_eq_i;
224         }
225   | COND_LT COND_GT
226         {
227             $$ = comp_ne_i;
228         }
229   | COND_LT
230         {
231             $$ = comp_lt_i;
232         }
233   | COND_GT
234         {
235             $$ = comp_gt_i;
236         }
237   | COND_LT COND_EQ
238         {
239             $$ = comp_le_i;
240         }
241   | COND_GT COND_EQ
242         {
243             $$ = comp_ge_i;
244         }
245   /*Int only*/
246   | COND_GT COND_LT
247         {
248             $$ = comp_bitand;
249         }
250   | COND_LT COND_LT
251         {
252             $$ = comp_highcomp;
253         }
254   | COND_GT COND_GT
255         {
256             $$ = comp_lowcomp;
257         }
258     ;
259
260 comp_op_s:
261     /* common functions */
262    COND_EQ
263         {
264             $$ = comp_eq_s;
265         }
266   | COND_LT COND_GT
267         {
268             $$ = comp_ne_s;
269         }
270   | COND_LT
271         {
272             $$ = comp_lt_s;
273         }
274   | COND_GT
275         {
276             $$ = comp_gt_s;
277         }
278   | COND_LT COND_EQ
279         {
280             $$ = comp_le_s;
281         }
282   | COND_GT COND_EQ
283         {
284             $$ = comp_ge_s;
285         }
286   /*string only*/
287   | COND_GT COND_LT
288         {
289             $$ = comp_substring;
290         }
291   | COND_LT COND_LT
292         {
293             $$ = comp_start;
294         }
295   | COND_GT COND_GT
296         {
297             $$ = comp_end;
298         }
299     ;
300
301 comp_op_m1:
302     /* common functions */
303    COND_EQ
304         {
305             $$ = comp_eq_m1;
306         }
307   | COND_LT COND_GT
308         {
309             $$ = comp_ne_m1;
310         }
311   | COND_LT
312         {
313             $$ = comp_lt_m1;
314         }
315   | COND_GT
316         {
317             $$ = comp_gt_m1;
318         }
319   | COND_LT COND_EQ
320         {
321             $$ = comp_le_m1;
322         }
323   | COND_GT COND_EQ
324         {
325             $$ = comp_ge_m1;
326         }
327   /*Not valid for mixed compares*/
328   | COND_GT COND_LT
329         {
330             $$ = 0;
331         }
332   | COND_LT COND_LT
333         {
334             $$ = 0;
335         }
336   | COND_GT COND_GT
337         {
338             $$ = 0;
339         }
340     ;
341
342 comp_op_m2:
343     /* common functions */
344    COND_EQ
345         {
346             $$ = comp_eq_m2;
347         }
348   | COND_LT COND_GT
349         {
350             $$ = comp_ne_m2;
351         }
352   | COND_LT
353         {
354             $$ = comp_lt_m2;
355         }
356   | COND_GT
357         {
358             $$ = comp_gt_m2;
359         }
360   | COND_LT COND_EQ
361         {
362             $$ = comp_le_m2;
363         }
364   | COND_GT COND_EQ
365         {
366             $$ = comp_ge_m2;
367         }
368   /*Not valid for mixed compares*/
369   | COND_GT COND_LT
370         {
371             $$ = 0;
372         }
373   | COND_LT COND_LT
374         {
375             $$ = 0;
376         }
377   | COND_GT COND_GT
378         {
379             $$ = 0;
380         }
381     ;
382
383 value_i:
384     symbol_i
385         {
386             $$ = $1;
387         }
388   | integer
389         {
390             $$ = $1;
391         }
392     ;
393
394 value_s:
395   symbol_s
396     {
397         $$ = $1;
398     } 
399   | literal
400     {
401         $$ = $1;
402     }
403     ;
404
405 literal:
406     COND_LITER
407         {
408             $$ = COND_GetLiteral(&$1);
409             if( !$$ )
410                 YYABORT;
411         }
412     ;
413
414 symbol_i:
415     COND_DOLLARS identifier
416         {
417             COND_input* cond = (COND_input*) info;
418             INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN;
419       
420             MSI_GetComponentStateW(cond->package, $2, &install, &action );
421             $$ = action;
422             msi_free( $2 );
423         }
424   | COND_QUESTION identifier
425         {
426             COND_input* cond = (COND_input*) info;
427             INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN;
428       
429             MSI_GetComponentStateW(cond->package, $2, &install, &action );
430             $$ = install;
431             msi_free( $2 );
432         }
433   | COND_AMPER identifier
434         {
435             COND_input* cond = (COND_input*) info;
436             INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN;
437       
438             MSI_GetFeatureStateW(cond->package, $2, &install, &action );
439             $$ = action;
440             msi_free( $2 );
441         }
442   | COND_EXCLAM identifier
443         {
444             COND_input* cond = (COND_input*) info;
445             INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN;
446       
447             MSI_GetFeatureStateW(cond->package, $2, &install, &action );
448             $$ = install;
449             msi_free( $2 );
450         }
451     ;
452
453 symbol_s:
454     identifier
455         {
456             DWORD sz;
457             COND_input* cond = (COND_input*) info;
458
459             sz = 0;
460             MSI_GetPropertyW(cond->package, $1, NULL, &sz);
461             if (sz == 0)
462             {
463                 $$ = msi_alloc( sizeof(WCHAR));
464                 $$[0] = 0;
465             }
466             else
467             {
468                 sz ++;
469                 $$ = msi_alloc( sz*sizeof (WCHAR) );
470
471                 /* Lookup the identifier */
472
473                 MSI_GetPropertyW(cond->package,$1,$$,&sz);
474             }
475             msi_free( $1 );
476         }
477     | COND_PERCENT identifier
478         {
479             UINT len = GetEnvironmentVariableW( $2, NULL, 0 );
480             if( len++ )
481             {
482                 $$ = msi_alloc( len*sizeof (WCHAR) );
483                 if( $$ )
484                     GetEnvironmentVariableW( $2, $$, len );
485             }
486             msi_free( $2 );
487         }
488     ;
489
490 identifier:
491     COND_IDENT
492         {
493             $$ = COND_GetString(&$1);
494             if( !$$ )
495                 YYABORT;
496         }
497     ;
498
499 integer:
500     COND_NUMBER
501         {
502             LPWSTR szNum = COND_GetString(&$1);
503             if( !szNum )
504                 YYABORT;
505             $$ = atoiW( szNum );
506             msi_free( szNum );
507         }
508     ;
509
510 %%
511
512
513 static int COND_IsAlpha( WCHAR x )
514 {
515     return( ( ( x >= 'A' ) && ( x <= 'Z' ) ) ||
516             ( ( x >= 'a' ) && ( x <= 'z' ) ) ||
517             ( ( x == '_' ) ) );
518 }
519
520 static int COND_IsNumber( WCHAR x )
521 {
522     return( (( x >= '0' ) && ( x <= '9' ))  || (x =='-') || (x =='.') );
523 }
524
525
526 /* the mess of comparison functions */
527
528 static INT comp_lt_i(INT a, INT b)
529 { return (a < b); }
530 static INT comp_gt_i(INT a, INT b)
531 { return (a > b); }
532 static INT comp_le_i(INT a, INT b)
533 { return (a <= b); }
534 static INT comp_ge_i(INT a, INT b)
535 { return (a >= b); }
536 static INT comp_eq_i(INT a, INT b)
537 { return (a == b); }
538 static INT comp_ne_i(INT a, INT b)
539 { return (a != b); }
540 static INT comp_bitand(INT a, INT b)
541 { return a & b;}
542 static INT comp_highcomp(INT a, INT b)
543 { return HIWORD(a)==b; }
544 static INT comp_lowcomp(INT a, INT b)
545 { return LOWORD(a)==b; }
546
547 static INT comp_eq_s(LPWSTR a, LPWSTR b, BOOL casless)
548 { if (casless) return !strcmpiW(a,b); else return !strcmpW(a,b);}
549 static INT comp_ne_s(LPWSTR a, LPWSTR b, BOOL casless)
550 { if (casless) return strcmpiW(a,b); else  return strcmpW(a,b);}
551 static INT comp_lt_s(LPWSTR a, LPWSTR b, BOOL casless)
552 { if (casless) return strcmpiW(a,b)<0; else return strcmpW(a,b)<0;}
553 static INT comp_gt_s(LPWSTR a, LPWSTR b, BOOL casless)
554 { if (casless) return strcmpiW(a,b)>0; else return strcmpW(a,b)>0;}
555 static INT comp_le_s(LPWSTR a, LPWSTR b, BOOL casless)
556 { if (casless) return strcmpiW(a,b)<=0; else return strcmpW(a,b)<=0;}
557 static INT comp_ge_s(LPWSTR a, LPWSTR b, BOOL casless)
558 { if (casless) return strcmpiW(a,b)>=0; else return  strcmpW(a,b)>=0;}
559 static INT comp_substring(LPWSTR a, LPWSTR b, BOOL casless)
560 /* ERROR NOT WORKING REWRITE */
561 { if (casless) return strstrW(a,b)!=NULL; else return strstrW(a,b)!=NULL;}
562 static INT comp_start(LPWSTR a, LPWSTR b, BOOL casless)
563 { if (casless) return strncmpiW(a,b,strlenW(b))==0; 
564   else return strncmpW(a,b,strlenW(b))==0;}
565 static INT comp_end(LPWSTR a, LPWSTR b, BOOL casless)
566
567     int i = strlenW(a); 
568     int j = strlenW(b); 
569     if (j>i)
570         return 0;
571     if (casless) return (!strcmpiW(&a[i-j-1],b));
572     else  return (!strcmpW(&a[i-j-1],b));
573 }
574
575
576 static INT comp_eq_m1(LPWSTR a, INT b)
577 { if (COND_IsNumber(a[0])) return atoiW(a)==b; else return 0;}
578 static INT comp_ne_m1(LPWSTR a, INT b)
579 { if (COND_IsNumber(a[0])) return atoiW(a)!=b; else return 1;}
580 static INT comp_lt_m1(LPWSTR a, INT b)
581 { if (COND_IsNumber(a[0])) return atoiW(a)<b; else return 0;}
582 static INT comp_gt_m1(LPWSTR a, INT b)
583 { if (COND_IsNumber(a[0])) return atoiW(a)>b; else return 0;}
584 static INT comp_le_m1(LPWSTR a, INT b)
585 { if (COND_IsNumber(a[0])) return atoiW(a)<=b; else return 0;}
586 static INT comp_ge_m1(LPWSTR a, INT b)
587 { if (COND_IsNumber(a[0])) return atoiW(a)>=b; else return 0;}
588
589 static INT comp_eq_m2(INT a, LPWSTR b)
590 { if (COND_IsNumber(b[0])) return a == atoiW(b); else return 0;}
591 static INT comp_ne_m2(INT a, LPWSTR b)
592 { if (COND_IsNumber(b[0])) return a != atoiW(b); else return 1;}
593 static INT comp_lt_m2(INT a, LPWSTR b)
594 { if (COND_IsNumber(b[0])) return a < atoiW(b); else return 0;}
595 static INT comp_gt_m2(INT a, LPWSTR b)
596 { if (COND_IsNumber(b[0])) return a > atoiW(b); else return 0;}
597 static INT comp_le_m2(INT a, LPWSTR b)
598 { if (COND_IsNumber(b[0])) return a <= atoiW(b); else return 0;}
599 static INT comp_ge_m2(INT a, LPWSTR b)
600 { if (COND_IsNumber(b[0])) return a >= atoiW(b); else return 0;}
601
602
603
604 static int COND_IsIdent( WCHAR x )
605 {
606     return( COND_IsAlpha( x ) || COND_IsNumber( x ) || ( x == '_' ) 
607             || ( x == '#' ) || (x == '.') );
608 }
609
610 static int COND_GetOne( struct cond_str *str, COND_input *cond )
611 {
612     static const WCHAR szNot[] = {'N','O','T',0};
613     static const WCHAR szAnd[] = {'A','N','D',0};
614     static const WCHAR szOr[] = {'O','R',0};
615     WCHAR ch;
616     int rc, len = 1;
617
618     str->data = &cond->str[cond->n];
619
620     ch = str->data[0];
621     switch( ch )
622     {
623     case 0: return 0;
624     case '(': rc = COND_LPAR; break;
625     case ')': rc = COND_RPAR; break;
626     case '&': rc = COND_AMPER; break;
627     case '!': rc = COND_EXCLAM; break;
628     case '$': rc = COND_DOLLARS; break;
629     case '?': rc = COND_QUESTION; break;
630     case '%': rc = COND_PERCENT; break;
631     case ' ': rc = COND_SPACE; break;
632     case '=': rc = COND_EQ; break;
633     case '~': rc = COND_TILDA; break;
634     case '<': rc = COND_LT; break;
635     case '>': rc = COND_GT; break;
636     case '"':
637         {
638             const WCHAR *ch2 = str->data + 1;
639
640
641             while ( *ch2 && *ch2 != '"' )
642                 ++ch2;
643             if (*ch2 == '"')
644             {
645                 len = ch2 - str->data + 1;
646                 rc = COND_LITER;
647                 break;
648             }
649         }
650         ERR("Unterminated string\n");
651         rc = COND_ERROR;
652         break;
653     default: 
654         if( COND_IsAlpha( ch ) )
655         {
656             while( COND_IsIdent( str->data[len] ) )
657                 len++;
658             rc = COND_IDENT;
659             break;
660         }
661
662         if( COND_IsNumber( ch ) )
663         {
664             while( COND_IsNumber( str->data[len] ) )
665                 len++;
666             rc = COND_NUMBER;
667             break;
668         }
669
670         ERR("Got unknown character %c(%x)\n",ch,ch);
671         rc = COND_ERROR;
672         break;
673     }
674
675     /* keyword identifiers */
676     if( rc == COND_IDENT )
677     {
678         if( (len==3) && (strncmpiW(str->data,szNot,len)==0) )
679             rc = COND_NOT;
680         else if( (len==3) && (strncmpiW(str->data,szAnd,len)==0) )
681             rc = COND_AND;
682         else if( (len==2) && (strncmpiW(str->data,szOr,len)==0) )
683             rc = COND_OR;
684     }
685
686     cond->n += len;
687     str->len = len;
688
689     return rc;
690 }
691
692 static int COND_lex( void *COND_lval, COND_input *cond )
693 {
694     int rc;
695     struct cond_str *str = COND_lval;
696
697     do {
698         rc = COND_GetOne( str, cond );
699     } while (rc == COND_SPACE);
700     
701     return rc;
702 }
703
704 static LPWSTR COND_GetString( struct cond_str *str )
705 {
706     LPWSTR ret;
707
708     ret = msi_alloc( (str->len+1) * sizeof (WCHAR) );
709     if( ret )
710     {
711         memcpy( ret, str->data, str->len * sizeof(WCHAR));
712         ret[str->len]=0;
713     }
714     TRACE("Got identifier %s\n",debugstr_w(ret));
715     return ret;
716 }
717
718 static LPWSTR COND_GetLiteral( struct cond_str *str )
719 {
720     LPWSTR ret;
721
722     ret = msi_alloc( (str->len-1) * sizeof (WCHAR) );
723     if( ret )
724     {
725         memcpy( ret, str->data+1, (str->len-2) * sizeof(WCHAR) );
726         ret[str->len - 2]=0;
727     }
728     TRACE("Got literal %s\n",debugstr_w(ret));
729     return ret;
730 }
731
732 static int COND_error(const char *str)
733 {
734     return 0;
735 }
736
737 MSICONDITION MSI_EvaluateConditionW( MSIPACKAGE *package, LPCWSTR szCondition )
738 {
739     COND_input cond;
740     MSICONDITION r;
741
742     cond.package = package;
743     cond.str   = szCondition;
744     cond.n     = 0;
745     cond.result = -1;
746     
747     TRACE("Evaluating %s\n",debugstr_w(szCondition));    
748
749     if ( szCondition == NULL || szCondition[0] == 0)
750         r = MSICONDITION_NONE;
751     else if ( !COND_parse( &cond ) )
752         r = cond.result;
753     else
754         r = MSICONDITION_ERROR;
755
756     TRACE("Evaluates to %i\n",r);
757     return r;
758 }
759
760 MSICONDITION WINAPI MsiEvaluateConditionW( MSIHANDLE hInstall, LPCWSTR szCondition )
761 {
762     MSIPACKAGE *package;
763     UINT ret;
764
765     package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE);
766     if( !package)
767         return ERROR_INVALID_HANDLE;
768     ret = MSI_EvaluateConditionW( package, szCondition );
769     msiobj_release( &package->hdr );
770     return ret;
771 }
772
773 MSICONDITION WINAPI MsiEvaluateConditionA( MSIHANDLE hInstall, LPCSTR szCondition )
774 {
775     LPWSTR szwCond = NULL;
776     MSICONDITION r;
777
778     if( szCondition )
779     {
780         UINT len = MultiByteToWideChar( CP_ACP, 0, szCondition, -1, NULL, 0 );
781         szwCond = msi_alloc( len * sizeof (WCHAR) );
782         MultiByteToWideChar( CP_ACP, 0, szCondition, -1, szwCond, len );
783     }
784
785     r = MsiEvaluateConditionW( hInstall, szwCond );
786
787     msi_free( szwCond );
788
789     return r;
790 }