Add a bunch of tests for MsiEvaluateCondition and make them pass.
[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 "winuser.h"
32 #include "wine/debug.h"
33 #include "wine/unicode.h"
34
35 #include "msi.h"
36 #include "msiquery.h"
37 #include "msipriv.h"
38 #include "action.h"
39
40 #define YYLEX_PARAM info
41 #define YYPARSE_PARAM info
42
43 static int COND_error(const char *str);
44
45 WINE_DEFAULT_DEBUG_CHANNEL(msi);
46
47 typedef struct tag_yyinput
48 {
49     MSIPACKAGE *package;
50     LPCWSTR str;
51     INT    n;
52     MSICONDITION result;
53 } COND_input;
54
55 struct cond_str {
56     LPCWSTR data;
57     INT len;
58 };
59
60 static LPWSTR COND_GetString( struct cond_str *str );
61 static LPWSTR COND_GetLiteral( struct cond_str *str );
62 static int COND_lex( void *COND_lval, COND_input *info);
63 static const WCHAR szEmpty[] = { 0 };
64
65 static INT compare_int( INT a, INT operator, INT b );
66 static INT compare_string( LPCWSTR a, INT operator, LPCWSTR b );
67
68 static INT compare_and_free_strings( LPWSTR a, INT op, LPWSTR b )
69 {
70     INT r;
71
72     r = compare_string( a, op, b );
73     msi_free( a );
74     msi_free( b );
75     return r;
76 }
77
78 %}
79
80 %pure-parser
81
82 %union
83 {
84     struct cond_str str;
85     LPWSTR    string;
86     INT       value;
87 }
88
89 %token COND_SPACE COND_EOF COND_SPACE
90 %token COND_OR COND_AND COND_NOT COND_XOR COND_IMP COND_EQV
91 %token COND_LT COND_GT COND_EQ COND_NE COND_GE COND_LE
92 %token COND_ILT COND_IGT COND_IEQ COND_INE COND_IGE COND_ILE
93 %token COND_LPAR COND_RPAR COND_TILDA COND_SS COND_ISS
94 %token COND_ILHS COND_IRHS COND_LHS COND_RHS
95 %token COND_PERCENT COND_DOLLARS COND_QUESTION COND_AMPER COND_EXCLAM
96 %token <str> COND_IDENT <str> COND_NUMBER <str> COND_LITER
97
98 %nonassoc COND_ERROR COND_EOF
99
100 %type <value> expression boolean_term boolean_factor 
101 %type <value> value_i integer operator
102 %type <string> identifier symbol_s value_s literal
103
104 %%
105
106 condition:
107     expression 
108         {
109             COND_input* cond = (COND_input*) info;
110             cond->result = $1;
111         }
112   | /* empty */
113         {
114             COND_input* cond = (COND_input*) info;
115             cond->result = MSICONDITION_NONE;
116         }
117     ;
118
119 expression:
120     boolean_term 
121         {
122             $$ = $1;
123         }
124   | expression COND_OR boolean_term
125         {
126             $$ = $1 || $3;
127         }
128   | expression COND_IMP boolean_term
129         {
130             $$ = !$1 || $3;
131         }
132   | expression COND_XOR boolean_term
133         {
134             $$ = ( $1 || $3 ) && !( $1 && $3 );
135         }
136   | expression COND_EQV boolean_term
137         {
138             $$ = ( $1 && $3 ) || ( !$1 && !$3 );
139         }
140     ;
141
142 boolean_term:
143     boolean_factor
144         {
145             $$ = $1;
146         }
147   | boolean_term COND_AND boolean_factor
148         {
149             $$ = $1 && $3;
150         }
151     ;
152
153 boolean_factor:
154     COND_NOT boolean_factor
155         {
156             $$ = $2 ? 0 : 1;
157         }
158   | value_i
159         {
160             $$ = $1 ? 1 : 0;
161         }
162   | value_s
163         {
164             $$ = ($1 && $1[0]) ? 1 : 0;
165         }
166   | value_i operator value_i
167         {
168             $$ = compare_int( $1, $2, $3 );
169         }
170   | symbol_s operator value_i
171         {
172             $$ = compare_int( $1 ? atoiW($1) : 0, $2, $3 );
173         }
174   | value_i operator symbol_s
175         {
176             $$ = compare_int( $1, $2, $3 ? atoiW($3) : 0 );
177         }
178   | symbol_s operator symbol_s
179         {
180             $$ = compare_and_free_strings( $1, $2, $3 );
181         }
182   | symbol_s operator literal
183         {
184             $$ = compare_and_free_strings( $1, $2, $3 );
185         }
186   | literal operator symbol_s
187         {
188             $$ = compare_and_free_strings( $1, $2, $3 );
189         }
190   | literal operator literal
191         {
192             $$ = compare_and_free_strings( $1, $2, $3 );
193         }
194   | literal operator value_i
195         {
196             $$ = 0;
197         }
198   | value_i operator literal
199         {
200             $$ = 0;
201         }
202   | COND_LPAR expression COND_RPAR
203         {
204             $$ = $2;
205         }
206     ;
207
208 operator:
209     /* common functions */
210     COND_EQ { $$ = COND_EQ }
211   | COND_NE { $$ = COND_NE }
212   | COND_LT { $$ = COND_LT }
213   | COND_GT { $$ = COND_GT }
214   | COND_LE { $$ = COND_LE }
215   | COND_GE { $$ = COND_GE }
216   | COND_SS { $$ = COND_SS }
217   | COND_IEQ { $$ = COND_IEQ }
218   | COND_INE { $$ = COND_INE }
219   | COND_ILT { $$ = COND_ILT }
220   | COND_IGT { $$ = COND_IGT }
221   | COND_ILE { $$ = COND_ILE }
222   | COND_IGE { $$ = COND_IGE }
223   | COND_ISS { $$ = COND_ISS }
224   | COND_LHS { $$ = COND_LHS }
225   | COND_RHS { $$ = COND_RHS }
226   | COND_ILHS { $$ = COND_ILHS }
227   | COND_IRHS { $$ = COND_IRHS }
228     ;
229
230 value_s:
231     symbol_s
232     {
233         $$ = $1;
234     } 
235   | literal
236     {
237         $$ = $1;
238     }
239     ;
240
241 literal:
242     COND_LITER
243         {
244             $$ = COND_GetLiteral(&$1);
245             if( !$$ )
246                 YYABORT;
247         }
248     ;
249
250 value_i:
251     integer
252         {
253             $$ = $1;
254         }
255   | COND_DOLLARS identifier
256         {
257             COND_input* cond = (COND_input*) info;
258             INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN;
259       
260             MSI_GetComponentStateW(cond->package, $2, &install, &action );
261             $$ = action;
262             msi_free( $2 );
263         }
264   | COND_QUESTION identifier
265         {
266             COND_input* cond = (COND_input*) info;
267             INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN;
268       
269             MSI_GetComponentStateW(cond->package, $2, &install, &action );
270             $$ = install;
271             msi_free( $2 );
272         }
273   | COND_AMPER identifier
274         {
275             COND_input* cond = (COND_input*) info;
276             INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN;
277       
278             MSI_GetFeatureStateW(cond->package, $2, &install, &action );
279             $$ = action;
280             msi_free( $2 );
281         }
282   | COND_EXCLAM identifier
283         {
284             COND_input* cond = (COND_input*) info;
285             INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN;
286       
287             MSI_GetFeatureStateW(cond->package, $2, &install, &action );
288             $$ = install;
289             msi_free( $2 );
290         }
291     ;
292
293 symbol_s:
294     identifier
295         {
296             COND_input* cond = (COND_input*) info;
297
298             $$ = msi_dup_property( cond->package, $1 );
299             msi_free( $1 );
300         }
301     | COND_PERCENT identifier
302         {
303             UINT len = GetEnvironmentVariableW( $2, NULL, 0 );
304             $$ = NULL;
305             if (len++)
306             {
307                 $$ = msi_alloc( len*sizeof (WCHAR) );
308                 GetEnvironmentVariableW( $2, $$, len );
309             }
310             msi_free( $2 );
311         }
312     ;
313
314 identifier:
315     COND_IDENT
316         {
317             $$ = COND_GetString(&$1);
318             if( !$$ )
319                 YYABORT;
320         }
321     ;
322
323 integer:
324     COND_NUMBER
325         {
326             LPWSTR szNum = COND_GetString(&$1);
327             if( !szNum )
328                 YYABORT;
329             $$ = atoiW( szNum );
330             msi_free( szNum );
331         }
332     ;
333
334 %%
335
336
337 static int COND_IsAlpha( WCHAR x )
338 {
339     return( ( ( x >= 'A' ) && ( x <= 'Z' ) ) ||
340             ( ( x >= 'a' ) && ( x <= 'z' ) ) ||
341             ( ( x == '_' ) ) );
342 }
343
344 static int COND_IsNumber( WCHAR x )
345 {
346     return( (( x >= '0' ) && ( x <= '9' ))  || (x =='-') || (x =='.') );
347 }
348
349 static WCHAR *strstriW( const WCHAR *str, const WCHAR *sub )
350 {
351     LPWSTR strlower, sublower, r;
352     strlower = CharLowerW( strdupW( str ) );
353     sublower = CharLowerW( strdupW( sub ) );
354     r = strstrW( strlower, sublower );
355     if (r)
356         r = (LPWSTR)str + (r - strlower);
357     msi_free( strlower );
358     msi_free( sublower );
359     return r;
360 }
361
362 static INT compare_string( LPCWSTR a, INT operator, LPCWSTR b )
363 {
364     /* a or b may be NULL */
365     switch (operator)
366     {
367     case COND_LT:
368         return -1 == lstrcmpW( a, b );
369     case COND_GT:
370         return  1 == lstrcmpW( a, b );
371     case COND_EQ:
372         return  0 == lstrcmpW( a, b );
373     case COND_NE:
374         return  0 != lstrcmpW( a, b );
375     case COND_GE:
376         return -1 != lstrcmpW( a, b );
377     case COND_LE:
378         return  1 != lstrcmpW( a, b );
379     case COND_SS: /* substring */
380         return ( a && b && strstrW( a, b ) ) ? 1 : 0;
381     case COND_ILT:
382         return -1 == lstrcmpiW( a, b );
383     case COND_IGT:
384         return  1 == lstrcmpiW( a, b );
385     case COND_IEQ:
386         return  0 == lstrcmpiW( a, b );
387     case COND_INE:
388         return  0 != lstrcmpiW( a, b );
389     case COND_IGE:
390         return -1 != lstrcmpiW( a, b );
391     case COND_ILE:
392         return  1 != lstrcmpiW( a, b );
393     case COND_ISS:
394         return ( a && b && strstriW( a, b ) ) ? 1 : 0;
395     case COND_LHS:
396     case COND_RHS:
397     case COND_ILHS:
398     case COND_IRHS:
399         ERR("unimplemented string comparison\n");
400         break;
401     default:
402         ERR("invalid integer operator\n");
403         return 0;
404     }
405     return 0;
406 }
407
408
409 static INT compare_int( INT a, INT operator, INT b )
410 {
411     switch (operator)
412     {
413     case COND_LT:
414     case COND_ILT:
415         return a < b;
416     case COND_GT:
417     case COND_IGT:
418         return a > b;
419     case COND_EQ:
420     case COND_IEQ:
421         return a == b;
422     case COND_NE:
423     case COND_INE:
424         return a != b;
425     case COND_GE:
426     case COND_IGE:
427         return a >= b;
428     case COND_LE:
429     case COND_ILE:
430         return a >= b;
431     case COND_SS:
432     case COND_ISS:
433         return ( a & b ) ? 1 : 0;
434     case COND_RHS:
435         return ( ( a & 0xffff ) == b ) ? 1 : 0;
436     case COND_LHS:
437         return ( ( (a>>16) & 0xffff ) == b ) ? 1 : 0;
438     default:
439         ERR("invalid integer operator\n");
440         return 0;
441     }
442     return 0;
443 }
444
445
446 static int COND_IsIdent( WCHAR x )
447 {
448     return( COND_IsAlpha( x ) || COND_IsNumber( x ) || ( x == '_' ) 
449             || ( x == '#' ) || (x == '.') );
450 }
451
452 static int COND_GetOperator( COND_input *cond )
453 {
454     static const struct {
455         const WCHAR str[4];
456         int id;
457     } table[] = {
458         { {'~','=',0},     COND_IEQ },
459         { {'~','>','=',0}, COND_ILE },
460         { {'~','>','<',0}, COND_ISS },
461         { {'~','>','>',0}, COND_IRHS },
462         { {'~','>',0},     COND_ILT },
463         { {'~','<','>',0}, COND_INE },
464         { {'~','<','=',0}, COND_IGE },
465         { {'~','<','<',0}, COND_ILHS },
466         { {'~','<',0},     COND_IGT },
467         { {'>','=',0},     COND_GE  },
468         { {'>','<',0},     COND_SS  },
469         { {'>','>',0},     COND_LHS },
470         { {'>',0},         COND_GT  },
471         { {'<','>',0},     COND_NE  },
472         { {'<','=',0},     COND_LE  },
473         { {'<','<',0},     COND_RHS },
474         { {'<',0},         COND_LT  },
475         { {0},             0        }
476     };
477     LPCWSTR p = &cond->str[cond->n];
478     int i = 0, len;
479
480     while ( 1 )
481     {
482         len = lstrlenW( table[i].str );
483         if ( !len || 0 == strncmpW( table[i].str, p, len ) )
484             break;
485         i++;
486     }
487     cond->n += len;
488     return table[i].id;
489 }
490
491 static int COND_GetOne( struct cond_str *str, COND_input *cond )
492 {
493     int rc, len = 1;
494     WCHAR ch;
495
496     str->data = &cond->str[cond->n];
497
498     ch = str->data[0];
499
500     switch( ch )
501     {
502     case 0: return 0;
503     case '(': rc = COND_LPAR; break;
504     case ')': rc = COND_RPAR; break;
505     case '&': rc = COND_AMPER; break;
506     case '!': rc = COND_EXCLAM; break;
507     case '$': rc = COND_DOLLARS; break;
508     case '?': rc = COND_QUESTION; break;
509     case '%': rc = COND_PERCENT; break;
510     case ' ': rc = COND_SPACE; break;
511     case '=': rc = COND_EQ; break;
512         break;
513
514     case '~':
515     case '<':
516     case '>':
517         rc = COND_GetOperator( cond );
518         if (!rc)
519             rc = COND_ERROR;
520         return rc;
521     default:
522         rc = 0;
523     }
524
525     if ( rc )
526     {
527         cond->n += len;
528         return rc;
529     }
530
531     if (ch == '"' )
532     {
533         LPCWSTR p = strchrW( str->data + 1, '"' );
534         if (!p)
535             return COND_ERROR;
536         len = p - str->data + 1;
537         rc = COND_LITER;
538     }
539     else if( COND_IsAlpha( ch ) )
540     {
541         static const WCHAR szNot[] = {'N','O','T',0};
542         static const WCHAR szAnd[] = {'A','N','D',0};
543         static const WCHAR szXor[] = {'X','O','R',0};
544         static const WCHAR szEqv[] = {'E','Q','V',0};
545         static const WCHAR szImp[] = {'I','M','P',0};
546         static const WCHAR szOr[] = {'O','R',0};
547
548         while( COND_IsIdent( str->data[len] ) )
549             len++;
550         rc = COND_IDENT;
551
552         if ( len == 3 )
553         {
554             if ( !strncmpiW( str->data, szNot, len ) )
555                 rc = COND_NOT;
556             else if( !strncmpiW( str->data, szAnd, len ) )
557                 rc = COND_AND;
558             else if( !strncmpiW( str->data, szXor, len ) )
559                 rc = COND_XOR;
560             else if( !strncmpiW( str->data, szEqv, len ) )
561                 rc = COND_EQV;
562             else if( !strncmpiW( str->data, szImp, len ) )
563                 rc = COND_IMP;
564         }
565         else if( (len == 2) && !strncmpiW( str->data, szOr, len ) )
566             rc = COND_OR;
567     }
568     else if( COND_IsNumber( ch ) )
569     {
570         while( COND_IsNumber( str->data[len] ) )
571             len++;
572         rc = COND_NUMBER;
573     }
574     else
575     {
576         ERR("Got unknown character %c(%x)\n",ch,ch);
577         return COND_ERROR;
578     }
579
580     cond->n += len;
581     str->len = len;
582
583     return rc;
584 }
585
586 static int COND_lex( void *COND_lval, COND_input *cond )
587 {
588     int rc;
589     struct cond_str *str = COND_lval;
590
591     do {
592         rc = COND_GetOne( str, cond );
593     } while (rc == COND_SPACE);
594     
595     return rc;
596 }
597
598 static LPWSTR COND_GetString( struct cond_str *str )
599 {
600     LPWSTR ret;
601
602     ret = msi_alloc( (str->len+1) * sizeof (WCHAR) );
603     if( ret )
604     {
605         memcpy( ret, str->data, str->len * sizeof(WCHAR));
606         ret[str->len]=0;
607     }
608     TRACE("Got identifier %s\n",debugstr_w(ret));
609     return ret;
610 }
611
612 static LPWSTR COND_GetLiteral( struct cond_str *str )
613 {
614     LPWSTR ret;
615
616     ret = msi_alloc( (str->len-1) * sizeof (WCHAR) );
617     if( ret )
618     {
619         memcpy( ret, str->data+1, (str->len-2) * sizeof(WCHAR) );
620         ret[str->len - 2]=0;
621     }
622     TRACE("Got literal %s\n",debugstr_w(ret));
623     return ret;
624 }
625
626 static int COND_error(const char *str)
627 {
628     TRACE("%s\n", str );
629     return 0;
630 }
631
632 MSICONDITION MSI_EvaluateConditionW( MSIPACKAGE *package, LPCWSTR szCondition )
633 {
634     COND_input cond;
635     MSICONDITION r;
636
637     TRACE("%s\n", debugstr_w( szCondition ) );
638
639     if ( szCondition == NULL )
640         return MSICONDITION_NONE;
641
642     cond.package = package;
643     cond.str   = szCondition;
644     cond.n     = 0;
645     cond.result = MSICONDITION_ERROR;
646     
647     if ( !COND_parse( &cond ) )
648         r = cond.result;
649     else
650         r = MSICONDITION_ERROR;
651
652     TRACE("%i <- %s\n", r, debugstr_w(szCondition));
653     return r;
654 }
655
656 MSICONDITION WINAPI MsiEvaluateConditionW( MSIHANDLE hInstall, LPCWSTR szCondition )
657 {
658     MSIPACKAGE *package;
659     UINT ret;
660
661     package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE);
662     if( !package)
663         return MSICONDITION_ERROR;
664     ret = MSI_EvaluateConditionW( package, szCondition );
665     msiobj_release( &package->hdr );
666     return ret;
667 }
668
669 MSICONDITION WINAPI MsiEvaluateConditionA( MSIHANDLE hInstall, LPCSTR szCondition )
670 {
671     LPWSTR szwCond = NULL;
672     MSICONDITION r;
673
674     szwCond = strdupAtoW( szCondition );
675     if( szCondition && !szwCond )
676         return MSICONDITION_ERROR;
677
678     r = MsiEvaluateConditionW( hInstall, szwCond );
679     msi_free( szwCond );
680     return r;
681 }