WIN_ListParents no longer needs to be exported, make it static.
[wine] / dlls / msi / format.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2005 Mike McCormack for CodeWeavers
5  * Copyright 2005 Aric Stewart for CodeWeavers
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 /*
23 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msiformatrecord.asp 
24  */
25
26 #include <stdarg.h>
27 #include <stdio.h>
28
29 #define COBJMACROS
30
31 #include "windef.h"
32 #include "winbase.h"
33 #include "winerror.h"
34 #include "winreg.h"
35 #include "wine/debug.h"
36 #include "fdi.h"
37 #include "msi.h"
38 #include "msiquery.h"
39 #include "msvcrt/fcntl.h"
40 #include "objbase.h"
41 #include "objidl.h"
42 #include "msipriv.h"
43 #include "winnls.h"
44 #include "winuser.h"
45 #include "shlobj.h"
46 #include "wine/unicode.h"
47 #include "ver.h"
48 #include "action.h"
49
50 WINE_DEFAULT_DEBUG_CHANNEL(msi);
51
52 static const WCHAR* scanW(LPCWSTR buf, WCHAR token, DWORD len)
53 {
54     DWORD i;
55     for (i = 0; i < len; i++)
56         if (buf[i] == token)
57             return &buf[i];
58     return NULL;
59 }
60
61 /*
62  * This helper function should probably go a lot of places
63  *
64  * Thinking about this, maybe this should become yet another Bison file
65  *
66  * len is in WCHARs
67  * return is also in WCHARs
68  */
69 static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr, 
70                                      WCHAR** data, DWORD len, MSIRECORD* record)
71 {
72     const WCHAR* mark=NULL;
73     WCHAR* mark2;
74     DWORD size=0;
75     DWORD chunk=0;
76     WCHAR key[0x100];
77     LPWSTR value = NULL;
78     DWORD sz;
79     UINT rc;
80     INT index;
81     LPWSTR newdata = NULL;
82
83     if (ptr==NULL)
84     {
85         TRACE("Deformatting NULL string\n");
86         *data = NULL;
87         return 0;
88     }
89
90     TRACE("Starting with %s\n",debugstr_w(ptr));
91
92     /* scan for special characters */
93     if (!scanW(ptr,'[',len) || (scanW(ptr,'[',len) && !scanW(ptr,']',len)))
94     {
95         /* not formatted */
96         *data = HeapAlloc(GetProcessHeap(),0,(len*sizeof(WCHAR)));
97         memcpy(*data,ptr,len*sizeof(WCHAR));
98         TRACE("Returning %s\n",debugstr_w(*data));
99         return len;
100     }
101    
102     /* formatted string located */ 
103     mark = scanW(ptr,'[',len);
104     if (mark != ptr)
105     {
106         INT cnt = (mark - ptr);
107         TRACE("%i  (%i) characters before marker\n",cnt,(mark-ptr));
108         size = cnt * sizeof(WCHAR);
109         newdata = HeapAlloc(GetProcessHeap(),0,size);
110         memcpy(newdata,ptr,(cnt * sizeof(WCHAR)));
111     }
112     else
113     {
114         size = 0;
115         newdata = HeapAlloc(GetProcessHeap(),0,size);
116         newdata[0]=0;
117     }
118     mark++;
119     /* there should be no null characters in a key so strchrW is ok */
120     mark2 = strchrW(mark,']');
121     strncpyW(key,mark,mark2-mark);
122     key[mark2-mark] = 0;
123     mark = strchrW(mark,']');
124     mark++;
125     TRACE("Current %s .. %s\n",debugstr_w(newdata),debugstr_w(key));
126     sz = 0;
127     /* expand what we can deformat... Again, this should become a bison file */
128     switch (key[0])
129     {
130         case '~':
131             value = HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR)*2);
132             value[0] =  0;
133             chunk = sizeof(WCHAR);
134             rc = ERROR_SUCCESS;
135             break;
136         case '$':
137             ERR("POORLY HANDLED DEFORMAT.. [$componentkey] \n");
138             index = get_loaded_component(package,&key[1]);
139             if (index >= 0)
140             {
141                 value = resolve_folder(package, 
142                                        package->components[index].Directory, 
143                                        FALSE, FALSE, NULL);
144                 chunk = (strlenW(value)) * sizeof(WCHAR);
145                 rc = 0;
146             }
147             else
148                 rc = ERROR_FUNCTION_FAILED;
149             break;
150         case '#':
151         case '!': /* should be short path */
152             index = get_loaded_file(package,&key[1]);
153             if (index >=0)
154             {
155                 sz = strlenW(package->files[index].TargetPath);
156                 value = dupstrW(package->files[index].TargetPath);
157                 chunk = (strlenW(value)) * sizeof(WCHAR);
158                 rc= ERROR_SUCCESS;
159             }
160             else
161                 rc = ERROR_FUNCTION_FAILED;
162             break;
163         case '\\':
164             value = HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR)*2);
165             value[0] =  key[1];
166             chunk = sizeof(WCHAR);
167             rc = ERROR_SUCCESS;
168             break;
169         case '%':
170             sz  = GetEnvironmentVariableW(&key[1],NULL,0);
171             if (sz > 0)
172             {
173                 sz++;
174                 value = HeapAlloc(GetProcessHeap(),0,sz * sizeof(WCHAR));
175                 GetEnvironmentVariableW(&key[1],value,sz);
176                 chunk = (strlenW(value)) * sizeof(WCHAR);
177                 rc = ERROR_SUCCESS;
178             }
179             else
180             {
181                 ERR("Unknown environment variable\n");
182                 chunk = 0;
183                 value = NULL;
184                 rc = ERROR_FUNCTION_FAILED;
185             }
186             break;
187         default:
188             /* check for numeric values */
189             index = 0;
190             while (isdigitW(key[index])) index++;
191             if (key[index] == 0)
192             {
193                 index = atoiW(key);
194                 TRACE("record index %i\n",index);
195                 value = load_dynamic_stringW(record,index);
196                 if (value)
197                 {
198                     chunk = strlenW(value) * sizeof(WCHAR);
199                     rc = ERROR_SUCCESS;
200                 }
201                 else
202                 {
203                     value = NULL;
204                     rc = ERROR_FUNCTION_FAILED;
205                 }
206             }
207             else
208             {
209                 value = load_dynamic_property(package,key, &rc);
210                 if (rc == ERROR_SUCCESS)
211                     chunk = (strlenW(value)) * sizeof(WCHAR);
212             }
213             break;      
214     }
215     if (((rc == ERROR_SUCCESS) || (rc == ERROR_MORE_DATA)) && value!=NULL)
216     {
217         LPWSTR nd2;
218         TRACE("value %s, chunk %li size %li\n",debugstr_w(value),chunk,size);
219
220         nd2= HeapReAlloc(GetProcessHeap(),0,newdata,(size + chunk));
221         newdata = nd2;
222         memcpy(&newdata[(size/sizeof(WCHAR))],value,chunk);
223         size+=chunk;   
224         HeapFree(GetProcessHeap(),0,value);
225     }
226     TRACE("after value %s .. %s\n",debugstr_w(newdata),debugstr_w(mark));
227     if (mark - ptr < len)
228     {
229         LPWSTR nd2;
230         chunk = (len - (mark - ptr)) * sizeof(WCHAR);
231         TRACE("after chunk is %li\n",chunk);
232         nd2 = HeapReAlloc(GetProcessHeap(),0,newdata,(size+chunk));
233         newdata = nd2;
234         memcpy(&newdata[(size/sizeof(WCHAR))],mark,chunk);
235         size+=chunk;
236     }
237     TRACE("after trailing %s .. %s\n",debugstr_w(newdata),debugstr_w(mark));
238
239     /* recursively do this to clean up */
240     size = deformat_string_internal(package,newdata,data,(size/sizeof(WCHAR)),
241                                     record);
242     HeapFree(GetProcessHeap(),0,newdata);
243     return size;
244 }
245
246
247 UINT MSI_FormatRecordW(MSIPACKAGE* package, MSIRECORD* record, LPWSTR buffer,
248                         DWORD *size)
249 {
250     LPWSTR deformated;
251     LPWSTR rec;
252     DWORD len;
253     UINT rc = ERROR_INVALID_PARAMETER;
254
255     TRACE("%p %p %p %li\n",package, record ,buffer, *size);
256
257     rec = load_dynamic_stringW(record,0);
258     if (!rec)
259         return rc;
260
261     TRACE("(%s)\n",debugstr_w(rec));
262
263     len = deformat_string_internal(package,rec,&deformated,(strlenW(rec)+1),
264                                    record);
265     if (len <= *size)
266     {
267         *size = len;
268         memcpy(buffer,deformated,len*sizeof(WCHAR));
269         rc = ERROR_SUCCESS;
270     }
271     else
272     {
273         *size = len;
274         rc = ERROR_MORE_DATA;
275     }
276
277     HeapFree(GetProcessHeap(),0,rec);
278     HeapFree(GetProcessHeap(),0,deformated);
279     return rc;
280 }
281
282 UINT WINAPI MsiFormatRecordA(MSIHANDLE hInstall, MSIHANDLE hRecord, LPSTR
283 szResult, DWORD *sz)
284 {
285     UINT rc;
286     MSIPACKAGE* package;
287     MSIRECORD* record;
288     LPWSTR szwResult;
289     DWORD original_len;
290
291     TRACE("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
292
293     package = msihandle2msiinfo(hInstall,MSIHANDLETYPE_PACKAGE);
294     record = msihandle2msiinfo(hRecord,MSIHANDLETYPE_RECORD);
295
296     if (!package || !record)
297         return ERROR_INVALID_HANDLE;
298
299     original_len = *sz;
300     /* +1 just to make sure we have a buffer in case the len is 0 */
301     szwResult = HeapAlloc(GetProcessHeap(),0,(original_len+1) * sizeof(WCHAR));
302
303     rc = MSI_FormatRecordW(package, record, szwResult, sz);
304     
305     WideCharToMultiByte(CP_ACP,0,szwResult,original_len, szResult, original_len,
306                         NULL,NULL);
307
308     HeapFree(GetProcessHeap(),0,szwResult);
309
310     return rc;
311
312 }
313
314 UINT WINAPI MsiFormatRecordW(MSIHANDLE hInstall, MSIHANDLE hRecord, 
315                              LPWSTR szResult, DWORD *sz)
316 {
317     UINT rc;
318     MSIPACKAGE* package;
319     MSIRECORD* record;
320
321     TRACE("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
322
323     package = msihandle2msiinfo(hInstall,MSIHANDLETYPE_PACKAGE);
324     record = msihandle2msiinfo(hRecord,MSIHANDLETYPE_RECORD);
325
326     if (!package || !record)
327         return ERROR_INVALID_HANDLE;
328
329     rc = MSI_FormatRecordW(package, record, szResult, sz);
330
331     return rc;
332 }