propsys: Use integer conversion functions in PropVariantCompareEx.
[wine] / dlls / propsys / propvar.c
1 /*
2  * PropVariant implementation
3  *
4  * Copyright 2008 James Hawkins for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22 #include <stdio.h>
23
24 #define NONAMELESSUNION
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winerror.h"
29 #include "winreg.h"
30 #include "winuser.h"
31 #include "shlobj.h"
32 #include "propvarutil.h"
33
34 #include "wine/debug.h"
35 #include "wine/unicode.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(propsys);
38
39 static HRESULT PROPVAR_ConvertFILETIME(PROPVARIANT *ppropvarDest,
40                                        REFPROPVARIANT propvarSrc, VARTYPE vt)
41 {
42     SYSTEMTIME time;
43
44     FileTimeToSystemTime(&propvarSrc->u.filetime, &time);
45
46     switch (vt)
47     {
48         case VT_LPSTR:
49         {
50             static const char format[] = "%04d/%02d/%02d:%02d:%02d:%02d.%03d";
51
52             ppropvarDest->u.pszVal = HeapAlloc(GetProcessHeap(), 0,
53                                              lstrlenA(format) + 1);
54             if (!ppropvarDest->u.pszVal)
55                 return E_OUTOFMEMORY;
56
57             sprintf(ppropvarDest->u.pszVal, format, time.wYear, time.wMonth,
58                     time.wDay, time.wHour, time.wMinute,
59                     time.wSecond, time.wMilliseconds);
60
61             return S_OK;
62         }
63
64         default:
65             FIXME("Unhandled target type: %d\n", vt);
66     }
67
68     return E_FAIL;
69 }
70
71 static HRESULT PROPVAR_ConvertNumber(REFPROPVARIANT pv, int dest_bits,
72     int dest_signed, LONGLONG *res)
73 {
74     int src_signed;
75
76     switch (pv->vt)
77     {
78     case VT_I1:
79         src_signed = 1;
80         *res = pv->u.cVal;
81         break;
82     case VT_UI1:
83         src_signed = 0;
84         *res = pv->u.bVal;
85         break;
86     case VT_I2:
87         src_signed = 1;
88         *res = pv->u.iVal;
89         break;
90     case VT_UI2:
91         src_signed = 0;
92         *res = pv->u.uiVal;
93         break;
94     case VT_I4:
95         src_signed = 1;
96         *res = pv->u.lVal;
97         break;
98     case VT_UI4:
99         src_signed = 0;
100         *res = pv->u.ulVal;
101         break;
102     case VT_I8:
103         src_signed = 1;
104         *res = pv->u.hVal.QuadPart;
105         break;
106     case VT_UI8:
107         src_signed = 0;
108         *res = pv->u.uhVal.QuadPart;
109         break;
110     case VT_EMPTY:
111         src_signed = 0;
112         *res = 0;
113         break;
114     default:
115         FIXME("unhandled vt %d\n", pv->vt);
116         return E_NOTIMPL;
117     }
118
119     if (*res < 0 && src_signed != dest_signed)
120         return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
121
122     if (dest_bits < 64)
123     {
124         if (dest_signed)
125         {
126             if (*res >= ((LONGLONG)1 << (dest_bits-1)) ||
127                 *res < ((LONGLONG)-1 << (dest_bits-1)))
128                 return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
129         }
130         else
131         {
132             if ((ULONGLONG)(*res) >= ((ULONGLONG)1 << dest_bits))
133                 return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
134         }
135     }
136
137     return S_OK;
138 }
139
140 HRESULT WINAPI PropVariantToInt16(REFPROPVARIANT propvarIn, SHORT *ret)
141 {
142     LONGLONG res;
143     HRESULT hr;
144
145     TRACE("%p,%p\n", propvarIn, ret);
146
147     hr = PROPVAR_ConvertNumber(propvarIn, 16, 1, &res);
148     if (SUCCEEDED(hr)) *ret = (SHORT)res;
149     return hr;
150 }
151
152 HRESULT WINAPI PropVariantToInt32(REFPROPVARIANT propvarIn, LONG *ret)
153 {
154     LONGLONG res;
155     HRESULT hr;
156
157     TRACE("%p,%p\n", propvarIn, ret);
158
159     hr = PROPVAR_ConvertNumber(propvarIn, 32, 1, &res);
160     if (SUCCEEDED(hr)) *ret = (LONG)res;
161     return hr;
162 }
163
164 HRESULT WINAPI PropVariantToInt64(REFPROPVARIANT propvarIn, LONGLONG *ret)
165 {
166     LONGLONG res;
167     HRESULT hr;
168
169     TRACE("%p,%p\n", propvarIn, ret);
170
171     hr = PROPVAR_ConvertNumber(propvarIn, 64, 1, &res);
172     if (SUCCEEDED(hr)) *ret = (LONGLONG)res;
173     return hr;
174 }
175
176 HRESULT WINAPI PropVariantToUInt16(REFPROPVARIANT propvarIn, USHORT *ret)
177 {
178     LONGLONG res;
179     HRESULT hr;
180
181     TRACE("%p,%p\n", propvarIn, ret);
182
183     hr = PROPVAR_ConvertNumber(propvarIn, 16, 0, &res);
184     if (SUCCEEDED(hr)) *ret = (USHORT)res;
185     return hr;
186 }
187
188 HRESULT WINAPI PropVariantToUInt32(REFPROPVARIANT propvarIn, ULONG *ret)
189 {
190     LONGLONG res;
191     HRESULT hr;
192
193     TRACE("%p,%p\n", propvarIn, ret);
194
195     hr = PROPVAR_ConvertNumber(propvarIn, 32, 0, &res);
196     if (SUCCEEDED(hr)) *ret = (ULONG)res;
197     return hr;
198 }
199
200 HRESULT WINAPI PropVariantToUInt64(REFPROPVARIANT propvarIn, ULONGLONG *ret)
201 {
202     LONGLONG res;
203     HRESULT hr;
204
205     TRACE("%p,%p\n", propvarIn, ret);
206
207     hr = PROPVAR_ConvertNumber(propvarIn, 64, 0, &res);
208     if (SUCCEEDED(hr)) *ret = (ULONGLONG)res;
209     return hr;
210 }
211
212 /******************************************************************
213  *  PropVariantChangeType   (PROPSYS.@)
214  */
215 HRESULT WINAPI PropVariantChangeType(PROPVARIANT *ppropvarDest, REFPROPVARIANT propvarSrc,
216                                      PROPVAR_CHANGE_FLAGS flags, VARTYPE vt)
217 {
218     HRESULT hr;
219
220     FIXME("(%p, %p, %d, %d, %d): semi-stub!\n", ppropvarDest, propvarSrc,
221           propvarSrc->vt, flags, vt);
222
223     switch (vt)
224     {
225     case VT_I2:
226     {
227         SHORT res;
228         hr = PropVariantToInt16(propvarSrc, &res);
229         if (SUCCEEDED(hr))
230         {
231             ppropvarDest->vt = VT_I2;
232             ppropvarDest->u.iVal = res;
233         }
234         return hr;
235     }
236     case VT_UI2:
237     {
238         USHORT res;
239         hr = PropVariantToUInt16(propvarSrc, &res);
240         if (SUCCEEDED(hr))
241         {
242             ppropvarDest->vt = VT_UI2;
243             ppropvarDest->u.uiVal = res;
244         }
245         return hr;
246     }
247     case VT_I4:
248     {
249         LONG res;
250         hr = PropVariantToInt32(propvarSrc, &res);
251         if (SUCCEEDED(hr))
252         {
253             ppropvarDest->vt = VT_I4;
254             ppropvarDest->u.lVal = res;
255         }
256         return hr;
257     }
258     case VT_UI4:
259     {
260         ULONG res;
261         hr = PropVariantToUInt32(propvarSrc, &res);
262         if (SUCCEEDED(hr))
263         {
264             ppropvarDest->vt = VT_UI4;
265             ppropvarDest->u.ulVal = res;
266         }
267         return hr;
268     }
269     case VT_I8:
270     {
271         LONGLONG res;
272         hr = PropVariantToInt64(propvarSrc, &res);
273         if (SUCCEEDED(hr))
274         {
275             ppropvarDest->vt = VT_I8;
276             ppropvarDest->u.hVal.QuadPart = res;
277         }
278         return hr;
279     }
280     case VT_UI8:
281     {
282         ULONGLONG res;
283         hr = PropVariantToUInt64(propvarSrc, &res);
284         if (SUCCEEDED(hr))
285         {
286             ppropvarDest->vt = VT_UI8;
287             ppropvarDest->u.uhVal.QuadPart = res;
288         }
289         return hr;
290     }
291     }
292
293     switch (propvarSrc->vt)
294     {
295         case VT_FILETIME:
296             return PROPVAR_ConvertFILETIME(ppropvarDest, propvarSrc, vt);
297         default:
298             FIXME("Unhandled source type: %d\n", propvarSrc->vt);
299     }
300
301     return E_FAIL;
302 }
303
304 static void PROPVAR_GUIDToWSTR(REFGUID guid, WCHAR *str)
305 {
306     static const WCHAR format[] = {'{','%','0','8','X','-','%','0','4','X','-','%','0','4','X',
307         '-','%','0','2','X','%','0','2','X','-','%','0','2','X','%','0','2','X','%','0','2','X',
308         '%','0','2','X','%','0','2','X','%','0','2','X','}',0};
309
310     sprintfW(str, format, guid->Data1, guid->Data2, guid->Data3,
311             guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3],
312             guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);
313 }
314
315 HRESULT WINAPI InitPropVariantFromGUIDAsString(REFGUID guid, PROPVARIANT *ppropvar)
316 {
317     TRACE("(%p %p)\n", guid, ppropvar);
318
319     if(!guid)
320         return E_FAIL;
321
322     ppropvar->vt = VT_LPWSTR;
323     ppropvar->u.pwszVal = CoTaskMemAlloc(39*sizeof(WCHAR));
324     if(!ppropvar->u.pwszVal)
325         return E_OUTOFMEMORY;
326
327     PROPVAR_GUIDToWSTR(guid, ppropvar->u.pwszVal);
328     return S_OK;
329 }
330
331 HRESULT WINAPI InitVariantFromGUIDAsString(REFGUID guid, VARIANT *pvar)
332 {
333     TRACE("(%p %p)\n", guid, pvar);
334
335     if(!guid) {
336         FIXME("guid == NULL\n");
337         return E_FAIL;
338     }
339
340     V_VT(pvar) = VT_BSTR;
341     V_BSTR(pvar) = SysAllocStringLen(NULL, 38);
342     if(!V_BSTR(pvar))
343         return E_OUTOFMEMORY;
344
345     PROPVAR_GUIDToWSTR(guid, V_BSTR(pvar));
346     return S_OK;
347 }
348
349 HRESULT WINAPI InitPropVariantFromBuffer(const VOID *pv, UINT cb, PROPVARIANT *ppropvar)
350 {
351     TRACE("(%p %u %p)\n", pv, cb, ppropvar);
352
353     ppropvar->u.caub.pElems = CoTaskMemAlloc(cb);
354     if(!ppropvar->u.caub.pElems)
355         return E_OUTOFMEMORY;
356
357     ppropvar->vt = VT_VECTOR|VT_UI1;
358     ppropvar->u.caub.cElems = cb;
359     memcpy(ppropvar->u.caub.pElems, pv, cb);
360     return S_OK;
361 }
362
363 HRESULT WINAPI InitVariantFromBuffer(const VOID *pv, UINT cb, VARIANT *pvar)
364 {
365     SAFEARRAY *arr;
366     void *data;
367     HRESULT hres;
368
369     TRACE("(%p %u %p)\n", pv, cb, pvar);
370
371     arr = SafeArrayCreateVector(VT_UI1, 0, cb);
372     if(!arr)
373         return E_OUTOFMEMORY;
374
375     hres = SafeArrayAccessData(arr, &data);
376     if(FAILED(hres)) {
377         SafeArrayDestroy(arr);
378         return hres;
379     }
380
381     memcpy(data, pv, cb);
382
383     hres = SafeArrayUnaccessData(arr);
384     if(FAILED(hres)) {
385         SafeArrayDestroy(arr);
386         return hres;
387     }
388
389     V_VT(pvar) = VT_ARRAY|VT_UI1;
390     V_ARRAY(pvar) = arr;
391     return S_OK;
392 }
393
394 static inline DWORD PROPVAR_HexToNum(const WCHAR *hex)
395 {
396     DWORD ret;
397
398     if(hex[0]>='0' && hex[0]<='9')
399         ret = hex[0]-'0';
400     else if(hex[0]>='a' && hex[0]<='f')
401         ret = hex[0]-'a'+10;
402     else if(hex[0]>='A' && hex[0]<='F')
403         ret = hex[0]-'A'+10;
404     else
405         return -1;
406
407     ret <<= 4;
408     if(hex[1]>='0' && hex[1]<='9')
409         return ret + hex[1]-'0';
410     else if(hex[1]>='a' && hex[1]<='f')
411         return ret + hex[1]-'a'+10;
412     else if(hex[1]>='A' && hex[1]<='F')
413         return ret + hex[1]-'A'+10;
414     else
415         return -1;
416 }
417
418 static inline HRESULT PROPVAR_WCHARToGUID(const WCHAR *str, int len, GUID *guid)
419 {
420     DWORD i, val=0;
421     const WCHAR *p;
422
423     memset(guid, 0, sizeof(GUID));
424
425     if(len!=38 || str[0]!='{' || str[9]!='-' || str[14]!='-'
426             || str[19]!='-' || str[24]!='-' || str[37]!='}') {
427         WARN("Error parsing %s\n", debugstr_w(str));
428         return E_INVALIDARG;
429     }
430
431     p = str+1;
432     for(i=0; i<4 && val!=-1; i++) {
433         val = PROPVAR_HexToNum(p);
434         guid->Data1 = (guid->Data1<<8) + val;
435         p += 2;
436     }
437     p++;
438     for(i=0; i<2 && val!=-1; i++) {
439         val = PROPVAR_HexToNum(p);
440         guid->Data2 = (guid->Data2<<8) + val;
441         p += 2;
442     }
443     p++;
444     for(i=0; i<2 && val!=-1; i++) {
445         val = PROPVAR_HexToNum(p);
446         guid->Data3 = (guid->Data3<<8) + val;
447         p += 2;
448     }
449     p++;
450     for(i=0; i<8 && val!=-1; i++) {
451         if(i == 2)
452             p++;
453
454         val = guid->Data4[i] = PROPVAR_HexToNum(p);
455         p += 2;
456     }
457
458     if(val == -1) {
459         WARN("Error parsing %s\n", debugstr_w(str));
460         memset(guid, 0, sizeof(GUID));
461         return E_INVALIDARG;
462     }
463     return S_OK;
464 }
465
466 HRESULT WINAPI PropVariantToGUID(const PROPVARIANT *ppropvar, GUID *guid)
467 {
468     TRACE("%p %p)\n", ppropvar, guid);
469
470     switch(ppropvar->vt) {
471     case VT_BSTR:
472         return PROPVAR_WCHARToGUID(ppropvar->u.bstrVal, SysStringLen(ppropvar->u.bstrVal), guid);
473     case VT_LPWSTR:
474         return PROPVAR_WCHARToGUID(ppropvar->u.pwszVal, strlenW(ppropvar->u.pwszVal), guid);
475
476     default:
477         FIXME("unsupported vt: %d\n", ppropvar->vt);
478         return E_NOTIMPL;
479     }
480 }
481
482 HRESULT WINAPI VariantToGUID(const VARIANT *pvar, GUID *guid)
483 {
484     TRACE("(%p %p)\n", pvar, guid);
485
486     switch(V_VT(pvar)) {
487     case VT_BSTR: {
488         HRESULT hres = PROPVAR_WCHARToGUID(V_BSTR(pvar), SysStringLen(V_BSTR(pvar)), guid);
489         if(hres == E_INVALIDARG)
490             return E_FAIL;
491         return hres;
492     }
493
494     default:
495         FIXME("unsupported vt: %d\n", V_VT(pvar));
496         return E_NOTIMPL;
497     }
498 }
499
500 static int isemptyornull(const PROPVARIANT *propvar)
501 {
502     if (propvar->vt == VT_EMPTY || propvar->vt == VT_NULL)
503         return 1;
504     if ((propvar->vt & VT_ARRAY) == VT_ARRAY)
505     {
506         int i;
507         for (i=0; i<propvar->u.parray->cDims; i++)
508         {
509             if (propvar->u.parray->rgsabound[i].cElements != 0)
510                 break;
511         }
512         return i == propvar->u.parray->cDims;
513     }
514     /* FIXME: vectors, byrefs, errors? */
515     return 0;
516 }
517
518 INT WINAPI PropVariantCompareEx(REFPROPVARIANT propvar1, REFPROPVARIANT propvar2,
519     PROPVAR_COMPARE_UNIT unit, PROPVAR_COMPARE_FLAGS flags)
520 {
521     const PROPVARIANT *propvar2_converted;
522     PROPVARIANT propvar2_static;
523     HRESULT hr;
524     INT res=-1;
525
526     TRACE("%p,%p,%x,%x\n", propvar1, propvar2, unit, flags);
527
528     if (isemptyornull(propvar1))
529     {
530         if (isemptyornull(propvar2))
531             return 0;
532         return (flags & PVCF_TREATEMPTYASGREATERTHAN) ? 1 : -1;
533     }
534
535     if (isemptyornull(propvar2))
536         return (flags & PVCF_TREATEMPTYASGREATERTHAN) ? -1 : 1;
537
538     if (propvar1->vt != propvar2->vt)
539     {
540         hr = PropVariantChangeType(&propvar2_static, propvar2, 0, propvar1->vt);
541
542         if (FAILED(hr))
543             return -1;
544
545         propvar2_converted = &propvar2_static;
546     }
547     else
548         propvar2_converted = propvar2;
549
550 #define CMP_INT_VALUE(var) do { \
551     if (propvar1->u.var > propvar2_converted->u.var) \
552         res = 1; \
553     else if (propvar1->u.var < propvar2_converted->u.var) \
554         res = -1; \
555     else \
556         res = 0; \
557     } while (0)
558
559     switch (propvar1->vt)
560     {
561     case VT_I1:
562         CMP_INT_VALUE(cVal);
563         break;
564     case VT_UI1:
565         CMP_INT_VALUE(bVal);
566         break;
567     case VT_I2:
568         CMP_INT_VALUE(iVal);
569         break;
570     case VT_UI2:
571         CMP_INT_VALUE(uiVal);
572         break;
573     case VT_I4:
574         CMP_INT_VALUE(lVal);
575         break;
576     case VT_UI4:
577         CMP_INT_VALUE(uiVal);
578         break;
579     case VT_I8:
580         CMP_INT_VALUE(hVal.QuadPart);
581         break;
582     case VT_UI8:
583         CMP_INT_VALUE(uhVal.QuadPart);
584         break;
585     case VT_BSTR:
586         /* FIXME: Use string flags. */
587         res = lstrcmpW(propvar1->u.bstrVal, propvar2->u.bstrVal);
588         break;
589     default:
590         FIXME("vartype %d not handled\n", propvar1->vt);
591         res = -1;
592         break;
593     }
594
595     if (propvar2_converted == &propvar2_static)
596         PropVariantClear(&propvar2_static);
597
598     return res;
599 }