ReadFile and WriteFile must be passed a parameter for the number of
[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(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             $$ = atoiW($1);
185         }
186   | value_i comp_op_i value_i
187         {
188             $$ = $2( $1, $3 );
189         }
190   | value_s comp_op_s value_s
191         {
192             $$ = $2( $1, $3, FALSE );
193         }
194   | value_s COND_TILDA comp_op_s value_s
195         {
196             $$ = $3( $1, $4, TRUE );
197         }
198   | value_s comp_op_m1 value_i
199         {
200             $$ = $2( $1, $3 );
201         }
202   | value_i comp_op_m2 value_s
203         {
204             $$ = $2( $1, $3 );
205         }
206   | COND_LPAR expression COND_RPAR
207         {
208             $$ = $2;
209         }
210     ;
211
212 comp_op_i:
213     /* common functions */
214    COND_EQ
215         {
216             $$ = comp_eq_i;
217         }
218   | COND_LT COND_GT
219         {
220             $$ = comp_ne_i;
221         }
222   | COND_LT
223         {
224             $$ = comp_lt_i;
225         }
226   | COND_GT
227         {
228             $$ = comp_gt_i;
229         }
230   | COND_LT COND_EQ
231         {
232             $$ = comp_le_i;
233         }
234   | COND_GT COND_EQ
235         {
236             $$ = comp_ge_i;
237         }
238   /*Int only*/
239   | COND_GT COND_LT
240         {
241             $$ = comp_bitand;
242         }
243   | COND_LT COND_LT
244         {
245             $$ = comp_highcomp;
246         }
247   | COND_GT COND_GT
248         {
249             $$ = comp_lowcomp;
250         }
251     ;
252
253 comp_op_s:
254     /* common functions */
255    COND_EQ
256         {
257             $$ = comp_eq_s;
258         }
259   | COND_LT COND_GT
260         {
261             $$ = comp_ne_s;
262         }
263   | COND_LT
264         {
265             $$ = comp_lt_s;
266         }
267   | COND_GT
268         {
269             $$ = comp_gt_s;
270         }
271   | COND_LT COND_EQ
272         {
273             $$ = comp_le_s;
274         }
275   | COND_GT COND_EQ
276         {
277             $$ = comp_ge_s;
278         }
279   /*string only*/
280   | COND_GT COND_LT
281         {
282             $$ = comp_substring;
283         }
284   | COND_LT COND_LT
285         {
286             $$ = comp_start;
287         }
288   | COND_GT COND_GT
289         {
290             $$ = comp_end;
291         }
292     ;
293
294 comp_op_m1:
295     /* common functions */
296    COND_EQ
297         {
298             $$ = comp_eq_m1;
299         }
300   | COND_LT COND_GT
301         {
302             $$ = comp_ne_m1;
303         }
304   | COND_LT
305         {
306             $$ = comp_lt_m1;
307         }
308   | COND_GT
309         {
310             $$ = comp_gt_m1;
311         }
312   | COND_LT COND_EQ
313         {
314             $$ = comp_le_m1;
315         }
316   | COND_GT COND_EQ
317         {
318             $$ = comp_ge_m1;
319         }
320   /*Not valid for mixed compares*/
321   | COND_GT COND_LT
322         {
323             $$ = 0;
324         }
325   | COND_LT COND_LT
326         {
327             $$ = 0;
328         }
329   | COND_GT COND_GT
330         {
331             $$ = 0;
332         }
333     ;
334
335 comp_op_m2:
336     /* common functions */
337    COND_EQ
338         {
339             $$ = comp_eq_m2;
340         }
341   | COND_LT COND_GT
342         {
343             $$ = comp_ne_m2;
344         }
345   | COND_LT
346         {
347             $$ = comp_lt_m2;
348         }
349   | COND_GT
350         {
351             $$ = comp_gt_m2;
352         }
353   | COND_LT COND_EQ
354         {
355             $$ = comp_le_m2;
356         }
357   | COND_GT COND_EQ
358         {
359             $$ = comp_ge_m2;
360         }
361   /*Not valid for mixed compares*/
362   | COND_GT COND_LT
363         {
364             $$ = 0;
365         }
366   | COND_LT COND_LT
367         {
368             $$ = 0;
369         }
370   | COND_GT COND_GT
371         {
372             $$ = 0;
373         }
374     ;
375
376 value_i:
377     symbol_i
378         {
379             $$ = $1;
380         }
381   | integer
382         {
383             $$ = $1;
384         }
385     ;
386
387 value_s:
388   symbol_s
389     {
390         $$ = $1;
391     } 
392   | literal
393     {
394         $$ = $1;
395     }
396     ;
397
398 literal:
399     COND_LITER
400         {
401             $$ = COND_GetLiteral(&$1);
402             if( !$$ )
403                 YYABORT;
404         }
405     ;
406
407 symbol_i:
408     COND_DOLLARS identifier
409         {
410             COND_input* cond = (COND_input*) info;
411             INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN;
412       
413             MSI_GetComponentStateW(cond->package, $2, &install, &action );
414             $$ = action;
415             HeapFree( GetProcessHeap(), 0, $2 );
416         }
417   | COND_QUESTION identifier
418         {
419             COND_input* cond = (COND_input*) info;
420             INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN;
421       
422             MSI_GetComponentStateW(cond->package, $2, &install, &action );
423             $$ = install;
424             HeapFree( GetProcessHeap(), 0, $2 );
425         }
426   | COND_AMPER identifier
427         {
428             COND_input* cond = (COND_input*) info;
429             INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN;
430       
431             MSI_GetFeatureStateW(cond->package, $2, &install, &action );
432             $$ = action;
433             HeapFree( GetProcessHeap(), 0, $2 );
434         }
435   | COND_EXCLAM identifier
436         {
437             COND_input* cond = (COND_input*) info;
438             INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN;
439       
440             MSI_GetFeatureStateW(cond->package, $2, &install, &action );
441             $$ = install;
442             HeapFree( GetProcessHeap(), 0, $2 );
443         }
444     ;
445
446 symbol_s:
447     identifier
448         {
449             DWORD sz;
450             COND_input* cond = (COND_input*) info;
451             $$ = HeapAlloc( GetProcessHeap(), 0, 0x100*sizeof (WCHAR) );
452
453             /* Lookup the identifier */
454
455             sz=0x100;
456             if (MSI_GetPropertyW(cond->package,$1,$$,&sz) != ERROR_SUCCESS)
457             {
458                 $$[0]=0;
459             }
460             HeapFree( GetProcessHeap(), 0, $1 );
461         }
462     | COND_PERCENT identifier
463         {
464             UINT len = GetEnvironmentVariableW( $2, NULL, 0 );
465             if( len++ )
466             {
467                 $$ = HeapAlloc( GetProcessHeap(), 0, len*sizeof (WCHAR) );
468                 if( $$ )
469                     GetEnvironmentVariableW( $2, $$, len );
470             }
471             HeapFree( GetProcessHeap(), 0, $2 );
472         }
473     ;
474
475 identifier:
476     COND_IDENT
477         {
478             $$ = COND_GetString(&$1);
479             if( !$$ )
480                 YYABORT;
481         }
482     ;
483
484 integer:
485     COND_NUMBER
486         {
487             LPWSTR szNum = COND_GetString(&$1);
488             if( !szNum )
489                 YYABORT;
490             $$ = atoiW( szNum );
491             HeapFree( GetProcessHeap(), 0, szNum );
492         }
493     ;
494
495 %%
496
497
498 static int COND_IsAlpha( WCHAR x )
499 {
500     return( ( ( x >= 'A' ) && ( x <= 'Z' ) ) ||
501             ( ( x >= 'a' ) && ( x <= 'z' ) ) ||
502             ( ( x == '_' ) ) );
503 }
504
505 static int COND_IsNumber( WCHAR x )
506 {
507     return( (( x >= '0' ) && ( x <= '9' ))  || (x =='-') || (x =='.') );
508 }
509
510
511 /* the mess of comparison functions */
512
513 static INT comp_lt_i(INT a, INT b)
514 { return (a < b); }
515 static INT comp_gt_i(INT a, INT b)
516 { return (a > b); }
517 static INT comp_le_i(INT a, INT b)
518 { return (a <= b); }
519 static INT comp_ge_i(INT a, INT b)
520 { return (a >= b); }
521 static INT comp_eq_i(INT a, INT b)
522 { return (a == b); }
523 static INT comp_ne_i(INT a, INT b)
524 { return (a != b); }
525 static INT comp_bitand(INT a, INT b)
526 { return a & b;}
527 static INT comp_highcomp(INT a, INT b)
528 { return HIWORD(a)==b; }
529 static INT comp_lowcomp(INT a, INT b)
530 { return LOWORD(a)==b; }
531
532 static INT comp_eq_s(LPWSTR a, LPWSTR b, BOOL casless)
533 { if (casless) return !strcmpiW(a,b); else return !strcmpW(a,b);}
534 static INT comp_ne_s(LPWSTR a, LPWSTR b, BOOL casless)
535 { if (casless) return strcmpiW(a,b); else  return strcmpW(a,b);}
536 static INT comp_lt_s(LPWSTR a, LPWSTR b, BOOL casless)
537 { if (casless) return strcmpiW(a,b)<0; else return strcmpW(a,b)<0;}
538 static INT comp_gt_s(LPWSTR a, LPWSTR b, BOOL casless)
539 { if (casless) return strcmpiW(a,b)>0; else return strcmpW(a,b)>0;}
540 static INT comp_le_s(LPWSTR a, LPWSTR b, BOOL casless)
541 { if (casless) return strcmpiW(a,b)<=0; else return strcmpW(a,b)<=0;}
542 static INT comp_ge_s(LPWSTR a, LPWSTR b, BOOL casless)
543 { if (casless) return strcmpiW(a,b)>=0; else return  strcmpW(a,b)>=0;}
544 static INT comp_substring(LPWSTR a, LPWSTR b, BOOL casless)
545 /* ERROR NOT WORKING REWRITE */
546 { if (casless) return strstrW(a,b)!=NULL; else return strstrW(a,b)!=NULL;}
547 static INT comp_start(LPWSTR a, LPWSTR b, BOOL casless)
548 { if (casless) return strncmpiW(a,b,strlenW(b))==0; 
549   else return strncmpW(a,b,strlenW(b))==0;}
550 static INT comp_end(LPWSTR a, LPWSTR b, BOOL casless)
551
552     int i = strlenW(a); 
553     int j = strlenW(b); 
554     if (j>i)
555         return 0;
556     if (casless) return (!strcmpiW(&a[i-j-1],b));
557     else  return (!strcmpW(&a[i-j-1],b));
558 }
559
560
561 static INT comp_eq_m1(LPWSTR a, INT b)
562 { if (COND_IsNumber(a[0])) return atoiW(a)==b; else return 0;}
563 static INT comp_ne_m1(LPWSTR a, INT b)
564 { if (COND_IsNumber(a[0])) return atoiW(a)!=b; else return 1;}
565 static INT comp_lt_m1(LPWSTR a, INT b)
566 { if (COND_IsNumber(a[0])) return atoiW(a)<b; else return 0;}
567 static INT comp_gt_m1(LPWSTR a, INT b)
568 { if (COND_IsNumber(a[0])) return atoiW(a)>b; else return 0;}
569 static INT comp_le_m1(LPWSTR a, INT b)
570 { if (COND_IsNumber(a[0])) return atoiW(a)<=b; else return 0;}
571 static INT comp_ge_m1(LPWSTR a, INT b)
572 { if (COND_IsNumber(a[0])) return atoiW(a)>=b; else return 0;}
573
574 static INT comp_eq_m2(INT a, LPWSTR b)
575 { if (COND_IsNumber(b[0])) return a == atoiW(b); else return 0;}
576 static INT comp_ne_m2(INT a, LPWSTR b)
577 { if (COND_IsNumber(b[0])) return a != atoiW(b); else return 1;}
578 static INT comp_lt_m2(INT a, LPWSTR b)
579 { if (COND_IsNumber(b[0])) return a < atoiW(b); else return 0;}
580 static INT comp_gt_m2(INT a, LPWSTR b)
581 { if (COND_IsNumber(b[0])) return a > atoiW(b); else return 0;}
582 static INT comp_le_m2(INT a, LPWSTR b)
583 { if (COND_IsNumber(b[0])) return a <= atoiW(b); else return 0;}
584 static INT comp_ge_m2(INT a, LPWSTR b)
585 { if (COND_IsNumber(b[0])) return a >= atoiW(b); else return 0;}
586
587
588
589 static int COND_IsIdent( WCHAR x )
590 {
591     return( COND_IsAlpha( x ) || COND_IsNumber( x ) || ( x == '_' ) 
592             || ( x == '#' ) || (x == '.') );
593 }
594
595 static int COND_GetOne( struct cond_str *str, COND_input *cond )
596 {
597     static const WCHAR szNot[] = {'N','O','T',0};
598     static const WCHAR szAnd[] = {'A','N','D',0};
599     static const WCHAR szOr[] = {'O','R',0};
600     WCHAR ch;
601     int rc, len = 1;
602
603     str->data = &cond->str[cond->n];
604
605     ch = str->data[0];
606     switch( ch )
607     {
608     case 0: return 0;
609     case '(': rc = COND_LPAR; break;
610     case ')': rc = COND_RPAR; break;
611     case '&': rc = COND_AMPER; break;
612     case '!': rc = COND_EXCLAM; break;
613     case '$': rc = COND_DOLLARS; break;
614     case '?': rc = COND_QUESTION; break;
615     case '%': rc = COND_PERCENT; break;
616     case ' ': rc = COND_SPACE; break;
617     case '=': rc = COND_EQ; break;
618     case '~': rc = COND_TILDA; break;
619     case '<': rc = COND_LT; break;
620     case '>': rc = COND_GT; break;
621     case '"':
622         {
623             const WCHAR *ch2 = str->data + 1;
624
625
626             while ( *ch2 && *ch2 != '"' )
627                 ++ch2;
628             if (*ch2 == '"')
629             {
630                 len = ch2 - str->data + 1;
631                 rc = COND_LITER;
632                 break;
633             }
634         }
635         ERR("Unterminated string\n");
636         rc = COND_ERROR;
637         break;
638     default: 
639         if( COND_IsAlpha( ch ) )
640         {
641             while( COND_IsIdent( str->data[len] ) )
642                 len++;
643             rc = COND_IDENT;
644             break;
645         }
646
647         if( COND_IsNumber( ch ) )
648         {
649             while( COND_IsNumber( str->data[len] ) )
650                 len++;
651             rc = COND_NUMBER;
652             break;
653         }
654
655         ERR("Got unknown character %c(%x)\n",ch,ch);
656         rc = COND_ERROR;
657         break;
658     }
659
660     /* keyword identifiers */
661     if( rc == COND_IDENT )
662     {
663         if( (len==3) && (strncmpiW(str->data,szNot,len)==0) )
664             rc = COND_NOT;
665         else if( (len==3) && (strncmpiW(str->data,szAnd,len)==0) )
666             rc = COND_AND;
667         else if( (len==2) && (strncmpiW(str->data,szOr,len)==0) )
668             rc = COND_OR;
669     }
670
671     cond->n += len;
672     str->len = len;
673
674     return rc;
675 }
676
677 static int COND_lex( void *COND_lval, COND_input *cond )
678 {
679     int rc;
680     struct cond_str *str = COND_lval;
681
682     do {
683         rc = COND_GetOne( str, cond );
684     } while (rc == COND_SPACE);
685     
686     return rc;
687 }
688
689 static LPWSTR COND_GetString( struct cond_str *str )
690 {
691     LPWSTR ret;
692
693     ret = HeapAlloc( GetProcessHeap(), 0, (str->len+1) * sizeof (WCHAR) );
694     if( ret )
695     {
696         strncpyW( ret, str->data, str->len );
697         ret[str->len]=0;
698     }
699     TRACE("Got identifier %s\n",debugstr_w(ret));
700     return ret;
701 }
702
703 static LPWSTR COND_GetLiteral( struct cond_str *str )
704 {
705     LPWSTR ret;
706
707     ret = HeapAlloc( GetProcessHeap(), 0, (str->len-1) * sizeof (WCHAR) );
708     if( ret )
709     {
710         memcpy( ret, str->data+1, (str->len-2) * sizeof(WCHAR) );
711         ret[str->len - 2]=0;
712     }
713     TRACE("Got literal %s\n",debugstr_w(ret));
714     return ret;
715 }
716
717 static int COND_error(char *str)
718 {
719     return 0;
720 }
721
722 MSICONDITION MSI_EvaluateConditionW( MSIPACKAGE *package, LPCWSTR szCondition )
723 {
724     COND_input cond;
725     MSICONDITION r;
726
727     cond.package = package;
728     cond.str   = szCondition;
729     cond.n     = 0;
730     cond.result = -1;
731     
732     TRACE("Evaluating %s\n",debugstr_w(szCondition));    
733
734     if( !COND_parse( &cond ) )
735         r = cond.result;
736     else
737         r = MSICONDITION_ERROR;
738
739     TRACE("Evaluates to %i\n",r);
740     return r;
741 }
742
743 MSICONDITION WINAPI MsiEvaluateConditionW( MSIHANDLE hInstall, LPCWSTR szCondition )
744 {
745     MSIPACKAGE *package;
746     UINT ret;
747
748     package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE);
749     if( !package)
750         return ERROR_INVALID_HANDLE;
751     ret = MSI_EvaluateConditionW( package, szCondition );
752     msiobj_release( &package->hdr );
753     return ret;
754 }
755
756 MSICONDITION WINAPI MsiEvaluateConditionA( MSIHANDLE hInstall, LPCSTR szCondition )
757 {
758     LPWSTR szwCond = NULL;
759     MSICONDITION r;
760
761     if( szCondition )
762     {
763         UINT len = MultiByteToWideChar( CP_ACP, 0, szCondition, -1, NULL, 0 );
764         szwCond = HeapAlloc( GetProcessHeap(), 0, len * sizeof (WCHAR) );
765         MultiByteToWideChar( CP_ACP, 0, szCondition, -1, szwCond, len );
766     }
767
768     r = MsiEvaluateConditionW( hInstall, szwCond );
769
770     if( szwCond )
771         HeapFree( GetProcessHeap(), 0, szwCond );
772
773     return r;
774 }