- action.c is getting too big, so split out all the handling of
[wine.git] / dlls / msi / format.c
blob012a2d75d87be049972b1f9d9565fb7505f9b155
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2005 Mike McCormack for CodeWeavers
5 * Copyright 2005 Aric Stewart for CodeWeavers
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.
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.
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
23 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msiformatrecord.asp
26 #include <stdarg.h>
27 #include <stdio.h>
29 #define COBJMACROS
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"
50 WINE_DEFAULT_DEBUG_CHANNEL(msi);
52 static const WCHAR* scanW(LPCWSTR buf, WCHAR token, DWORD len)
54 DWORD i;
55 for (i = 0; i < len; i++)
56 if (buf[i] == token)
57 return &buf[i];
58 return NULL;
62 * This helper function should probably go alot of places
64 * Thinking about this, maybe this should become yet another Bison file
66 * len is in WCHARs
67 * return is also in WCHARs
69 static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr,
70 WCHAR** data, DWORD len, MSIRECORD* record)
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;
83 if (ptr==NULL)
85 TRACE("Deformatting NULL string\n");
86 *data = NULL;
87 return 0;
90 TRACE("Starting with %s\n",debugstr_w(ptr));
92 /* scan for special characters */
93 if (!scanW(ptr,'[',len) || (scanW(ptr,'[',len) && !scanW(ptr,']',len)))
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;
102 /* formatted string located */
103 mark = scanW(ptr,'[',len);
104 if (mark != ptr)
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)));
112 else
114 size = 0;
115 newdata = HeapAlloc(GetProcessHeap(),0,size);
116 newdata[0]=0;
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])
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)
141 value = resolve_folder(package,
142 package->components[index].Directory,
143 FALSE, FALSE, NULL);
144 chunk = (strlenW(value)) * sizeof(WCHAR);
145 rc = 0;
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)
155 sz = strlenW(package->files[index].TargetPath);
156 value = dupstrW(package->files[index].TargetPath);
157 chunk = (strlenW(value)) * sizeof(WCHAR);
158 rc= ERROR_SUCCESS;
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)
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;
179 else
181 ERR("Unknown enviroment variable\n");
182 chunk = 0;
183 value = NULL;
184 rc = ERROR_FUNCTION_FAILED;
186 break;
187 default:
188 /* check for numaric values */
189 index = 0;
190 while (isdigitW(key[index])) index++;
191 if (key[index] == 0)
193 index = atoiW(key);
194 TRACE("record index %i\n",index);
195 value = load_dynamic_stringW(record,index);
196 if (value)
198 chunk = strlenW(value) * sizeof(WCHAR);
199 rc = ERROR_SUCCESS;
201 else
203 value = NULL;
204 rc = ERROR_FUNCTION_FAILED;
207 else
209 value = load_dynamic_property(package,key, &rc);
210 if (rc == ERROR_SUCCESS)
211 chunk = (strlenW(value)) * sizeof(WCHAR);
213 break;
215 if (((rc == ERROR_SUCCESS) || (rc == ERROR_MORE_DATA)) && value!=NULL)
217 LPWSTR nd2;
218 TRACE("value %s, chunk %li size %li\n",debugstr_w(value),chunk,size);
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);
226 TRACE("after value %s .. %s\n",debugstr_w(newdata),debugstr_w(mark));
227 if (mark - ptr < len)
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;
237 TRACE("after trailing %s .. %s\n",debugstr_w(newdata),debugstr_w(mark));
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;
247 UINT MSI_FormatRecordW(MSIPACKAGE* package, MSIRECORD* record, LPWSTR buffer,
248 DWORD *size)
250 LPWSTR deformated;
251 LPWSTR rec;
252 DWORD len;
253 UINT rc = ERROR_INVALID_PARAMETER;
255 TRACE("%p %p %p %li\n",package, record ,buffer, *size);
257 rec = load_dynamic_stringW(record,0);
258 if (!rec)
259 return rc;
261 TRACE("(%s)\n",debugstr_w(rec));
263 len = deformat_string_internal(package,rec,&deformated,(strlenW(rec)+1),
264 record);
265 if (len <= *size)
267 *size = len;
268 memcpy(buffer,deformated,len*sizeof(WCHAR));
269 rc = ERROR_SUCCESS;
271 else
273 *size = len;
274 rc = ERROR_MORE_DATA;
277 HeapFree(GetProcessHeap(),0,rec);
278 HeapFree(GetProcessHeap(),0,deformated);
279 return rc;
282 UINT WINAPI MsiFormatRecordA(MSIHANDLE hInstall, MSIHANDLE hRecord, LPSTR
283 szResult, DWORD *sz)
285 UINT rc;
286 MSIPACKAGE* package;
287 MSIRECORD* record;
288 LPWSTR szwResult;
289 DWORD original_len;
291 TRACE("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
293 package = msihandle2msiinfo(hInstall,MSIHANDLETYPE_PACKAGE);
294 record = msihandle2msiinfo(hRecord,MSIHANDLETYPE_RECORD);
296 if (!package || !record)
297 return ERROR_INVALID_HANDLE;
299 original_len = *sz;
300 /* +1 just to make sure we have a buffer incase the len is 0 */
301 szwResult = HeapAlloc(GetProcessHeap(),0,(original_len+1) * sizeof(WCHAR));
303 rc = MSI_FormatRecordW(package, record, szwResult, sz);
305 WideCharToMultiByte(CP_ACP,0,szwResult,original_len, szResult, original_len,
306 NULL,NULL);
308 HeapFree(GetProcessHeap(),0,szwResult);
310 return rc;
314 UINT WINAPI MsiFormatRecordW(MSIHANDLE hInstall, MSIHANDLE hRecord,
315 LPWSTR szResult, DWORD *sz)
317 UINT rc;
318 MSIPACKAGE* package;
319 MSIRECORD* record;
321 TRACE("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
323 package = msihandle2msiinfo(hInstall,MSIHANDLETYPE_PACKAGE);
324 record = msihandle2msiinfo(hRecord,MSIHANDLETYPE_RECORD);
326 if (!package || !record)
327 return ERROR_INVALID_HANDLE;
329 rc = MSI_FormatRecordW(package, record, szResult, sz);
331 return rc;