Release 6.15.
[wine.git] / dlls / mscoree / config.c
blobb6aa647d876d4ef79a40de9105fe4de62e73b91f
1 /*
2 * Configuration file parsing
4 * Copyright 2010 Vincent Povirk
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.
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.
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
21 #define COBJMACROS
23 #include <stdarg.h>
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winreg.h"
28 #include "ole2.h"
29 #include "msxml2.h"
30 #include "mscoree.h"
31 #include "corhdr.h"
32 #include "corerror.h"
33 #include "metahost.h"
34 #include "cordebug.h"
35 #include "wine/list.h"
36 #include "mscoree_private.h"
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL( mscoree );
42 enum parse_state
44 STATE_ASSEMBLY_BINDING,
45 STATE_ROOT,
46 STATE_CONFIGURATION,
47 STATE_PROBING,
48 STATE_RUNTIME,
49 STATE_STARTUP,
50 STATE_UNKNOWN
53 typedef struct ConfigFileHandler
55 ISAXContentHandler ISAXContentHandler_iface;
56 ISAXErrorHandler ISAXErrorHandler_iface;
57 LONG ref;
58 enum parse_state states[16];
59 int statenum;
60 parsed_config_file *result;
61 } ConfigFileHandler;
63 typedef struct
65 IStream IStream_iface;
66 LONG ref;
67 HANDLE file;
68 } ConfigStream;
70 static inline ConfigStream *impl_from_IStream(IStream *iface)
72 return CONTAINING_RECORD(iface, ConfigStream, IStream_iface);
75 static HRESULT WINAPI ConfigStream_QueryInterface(IStream *iface, REFIID riid, void **ppv)
77 ConfigStream *This = impl_from_IStream(iface);
79 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
81 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IStream))
82 *ppv = &This->IStream_iface;
83 else
85 WARN("Not supported iface %s\n", debugstr_guid(riid));
86 *ppv = NULL;
87 return E_NOINTERFACE;
90 IUnknown_AddRef((IUnknown*)*ppv);
91 return S_OK;
94 static ULONG WINAPI ConfigStream_AddRef(IStream *iface)
96 ConfigStream *This = impl_from_IStream(iface);
97 ULONG ref = InterlockedIncrement(&This->ref);
99 TRACE("(%p) ref=%u\n", This, ref);
101 return ref;
104 static ULONG WINAPI ConfigStream_Release(IStream *iface)
106 ConfigStream *This = impl_from_IStream(iface);
107 ULONG ref = InterlockedDecrement(&This->ref);
109 TRACE("(%p) ref=%u\n",This, ref);
111 if (!ref)
113 CloseHandle(This->file);
114 HeapFree(GetProcessHeap(), 0, This);
117 return ref;
120 static HRESULT WINAPI ConfigStream_Read(IStream *iface, void *buf, ULONG size, ULONG *ret_read)
122 ConfigStream *This = impl_from_IStream(iface);
123 DWORD read = 0;
125 TRACE("(%p)->(%p %u %p)\n", This, buf, size, ret_read);
127 if (!ReadFile(This->file, buf, size, &read, NULL))
129 WARN("error %d reading file\n", GetLastError());
130 return HRESULT_FROM_WIN32(GetLastError());
133 if (ret_read) *ret_read = read;
134 return S_OK;
137 static HRESULT WINAPI ConfigStream_Write(IStream *iface, const void *buf, ULONG size, ULONG *written)
139 ConfigStream *This = impl_from_IStream(iface);
140 TRACE("(%p)->(%p %u %p)\n", This, buf, size, written);
141 return E_FAIL;
144 static HRESULT WINAPI ConfigStream_Seek(IStream *iface, LARGE_INTEGER dlibMove,
145 DWORD dwOrigin, ULARGE_INTEGER *pNewPos)
147 ConfigStream *This = impl_from_IStream(iface);
148 TRACE("(%p)->(%d %d %p)\n", This, dlibMove.u.LowPart, dwOrigin, pNewPos);
149 return E_NOTIMPL;
152 static HRESULT WINAPI ConfigStream_SetSize(IStream *iface, ULARGE_INTEGER libNewSize)
154 ConfigStream *This = impl_from_IStream(iface);
155 TRACE("(%p)->(%d)\n", This, libNewSize.u.LowPart);
156 return E_NOTIMPL;
159 static HRESULT WINAPI ConfigStream_CopyTo(IStream *iface, IStream *stream, ULARGE_INTEGER size,
160 ULARGE_INTEGER *read, ULARGE_INTEGER *written)
162 ConfigStream *This = impl_from_IStream(iface);
163 FIXME("(%p)->(%p %d %p %p)\n", This, stream, size.u.LowPart, read, written);
164 return E_NOTIMPL;
167 static HRESULT WINAPI ConfigStream_Commit(IStream *iface, DWORD flags)
169 ConfigStream *This = impl_from_IStream(iface);
170 FIXME("(%p,%d)\n", This, flags);
171 return E_NOTIMPL;
174 static HRESULT WINAPI ConfigStream_Revert(IStream *iface)
176 ConfigStream *This = impl_from_IStream(iface);
177 TRACE("(%p)\n", This);
178 return E_NOTIMPL;
181 static HRESULT WINAPI ConfigStream_LockUnlockRegion(IStream *iface, ULARGE_INTEGER libOffset,
182 ULARGE_INTEGER cb, DWORD dwLockType)
184 ConfigStream *This = impl_from_IStream(iface);
185 TRACE("(%p,%d,%d,%d)\n", This, libOffset.u.LowPart, cb.u.LowPart, dwLockType);
186 return E_NOTIMPL;
189 static HRESULT WINAPI ConfigStream_Stat(IStream *iface, STATSTG *lpStat, DWORD grfStatFlag)
191 ConfigStream *This = impl_from_IStream(iface);
192 FIXME("(%p,%p,%d)\n", This, lpStat, grfStatFlag);
193 return E_NOTIMPL;
196 static HRESULT WINAPI ConfigStream_Clone(IStream *iface, IStream **ppstm)
198 ConfigStream *This = impl_from_IStream(iface);
199 TRACE("(%p)\n",This);
200 return E_NOTIMPL;
203 static const IStreamVtbl ConfigStreamVtbl = {
204 ConfigStream_QueryInterface,
205 ConfigStream_AddRef,
206 ConfigStream_Release,
207 ConfigStream_Read,
208 ConfigStream_Write,
209 ConfigStream_Seek,
210 ConfigStream_SetSize,
211 ConfigStream_CopyTo,
212 ConfigStream_Commit,
213 ConfigStream_Revert,
214 ConfigStream_LockUnlockRegion,
215 ConfigStream_LockUnlockRegion,
216 ConfigStream_Stat,
217 ConfigStream_Clone
220 HRESULT WINAPI CreateConfigStream(const WCHAR *filename, IStream **stream)
222 ConfigStream *config_stream;
223 HANDLE file;
225 TRACE("(%s, %p)\n", debugstr_w(filename), stream);
227 if (!stream)
228 return COR_E_NULLREFERENCE;
230 file = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
231 if (file == INVALID_HANDLE_VALUE)
232 return GetLastError() == ERROR_FILE_NOT_FOUND ? COR_E_FILENOTFOUND : E_FAIL;
234 config_stream = HeapAlloc(GetProcessHeap(), 0, sizeof(*config_stream));
235 if (!config_stream)
237 CloseHandle(file);
238 return E_OUTOFMEMORY;
241 config_stream->IStream_iface.lpVtbl = &ConfigStreamVtbl;
242 config_stream->ref = 1;
243 config_stream->file = file;
245 *stream = &config_stream->IStream_iface;
246 return S_OK;
249 static inline ConfigFileHandler *impl_from_ISAXContentHandler(ISAXContentHandler *iface)
251 return CONTAINING_RECORD(iface, ConfigFileHandler, ISAXContentHandler_iface);
254 static inline ConfigFileHandler *impl_from_ISAXErrorHandler(ISAXErrorHandler *iface)
256 return CONTAINING_RECORD(iface, ConfigFileHandler, ISAXErrorHandler_iface);
259 static HRESULT WINAPI ConfigFileHandler_QueryInterface(ISAXContentHandler *iface,
260 REFIID riid, void **ppvObject)
262 ConfigFileHandler *This = impl_from_ISAXContentHandler(iface);
264 if (IsEqualGUID(riid, &IID_ISAXContentHandler) || IsEqualGUID(riid, &IID_IUnknown))
265 *ppvObject = &This->ISAXContentHandler_iface;
266 else if (IsEqualGUID(riid, &IID_ISAXErrorHandler))
267 *ppvObject = &This->ISAXErrorHandler_iface;
268 else
270 WARN("Unsupported interface %s\n", debugstr_guid(riid));
271 return E_NOINTERFACE;
274 IUnknown_AddRef((IUnknown*)*ppvObject);
276 return S_OK;
279 static ULONG WINAPI ConfigFileHandler_AddRef(ISAXContentHandler *iface)
281 ConfigFileHandler *This = impl_from_ISAXContentHandler(iface);
282 return InterlockedIncrement(&This->ref);
285 static ULONG WINAPI ConfigFileHandler_Release(ISAXContentHandler *iface)
287 ConfigFileHandler *This = impl_from_ISAXContentHandler(iface);
288 ULONG ref = InterlockedDecrement(&This->ref);
290 if (ref == 0)
291 HeapFree(GetProcessHeap(), 0, This);
293 return ref;
296 static HRESULT WINAPI ConfigFileHandler_putDocumentLocator(ISAXContentHandler *iface,
297 ISAXLocator *pLocator)
299 return S_OK;
302 static HRESULT WINAPI ConfigFileHandler_startDocument(ISAXContentHandler *iface)
304 return S_OK;
307 static HRESULT WINAPI ConfigFileHandler_endDocument(ISAXContentHandler *iface)
309 return S_OK;
312 static HRESULT WINAPI ConfigFileHandler_startPrefixMapping(ISAXContentHandler *iface,
313 const WCHAR *pPrefix, int nPrefix, const WCHAR *pUri, int nUri)
315 return S_OK;
318 static HRESULT WINAPI ConfigFileHandler_endPrefixMapping(ISAXContentHandler *iface,
319 const WCHAR *pPrefix, int nPrefix)
321 return S_OK;
324 static HRESULT parse_startup(ConfigFileHandler *This, ISAXAttributes *pAttr)
326 static const WCHAR legacy[] = {'u','s','e','L','e','g','a','c','y','V','2','R','u','n','t','i','m','e','A','c','t','i','v','a','t','i','o','n','P','o','l','i','c','y',0};
327 static const WCHAR empty[] = {0};
328 LPCWSTR value;
329 int value_size;
330 HRESULT hr;
332 hr = ISAXAttributes_getValueFromName(pAttr, empty, 0, legacy, lstrlenW(legacy), &value, &value_size);
333 if (SUCCEEDED(hr))
334 FIXME("useLegacyV2RuntimeActivationPolicy=%s not implemented\n", debugstr_wn(value, value_size));
335 hr = S_OK;
337 return hr;
340 static HRESULT parse_probing(ConfigFileHandler *This, ISAXAttributes *pAttr)
342 static const WCHAR privatePath[] = {'p','r','i','v','a','t','e','P','a','t','h',0};
343 static const WCHAR empty[] = {0};
344 LPCWSTR value;
345 int value_size;
346 HRESULT hr;
348 hr = ISAXAttributes_getValueFromName(pAttr, empty, 0, privatePath, lstrlenW(privatePath), &value, &value_size);
349 if (SUCCEEDED(hr))
351 TRACE("%s\n", debugstr_wn(value, value_size));
353 This->result->private_path = HeapAlloc(GetProcessHeap(), 0, (value_size + 1) * sizeof(WCHAR));
354 if (This->result->private_path)
355 wcscpy(This->result->private_path, value);
356 else
357 hr = E_OUTOFMEMORY;
360 return hr;
364 static HRESULT parse_supported_runtime(ConfigFileHandler *This, ISAXAttributes *pAttr)
366 static const WCHAR version[] = {'v','e','r','s','i','o','n',0};
367 static const WCHAR sku[] = {'s','k','u',0};
368 static const WCHAR empty[] = {0};
369 LPCWSTR value;
370 int value_size;
371 HRESULT hr;
372 supported_runtime *entry;
374 hr = ISAXAttributes_getValueFromName(pAttr, empty, 0, version, lstrlenW(version), &value, &value_size);
375 if (SUCCEEDED(hr))
377 TRACE("%s\n", debugstr_wn(value, value_size));
378 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(supported_runtime));
379 if (entry)
381 entry->version = HeapAlloc(GetProcessHeap(), 0, (value_size + 1) * sizeof(WCHAR));
382 if (entry->version)
384 lstrcpyW(entry->version, value);
385 list_add_tail(&This->result->supported_runtimes, &entry->entry);
387 else
389 HeapFree(GetProcessHeap(), 0, entry);
390 hr = E_OUTOFMEMORY;
393 else
394 hr = E_OUTOFMEMORY;
396 else
397 WARN("Missing version attribute\n");
399 if (SUCCEEDED(hr))
401 hr = ISAXAttributes_getValueFromName(pAttr, empty, 0, sku, lstrlenW(sku), &value, &value_size);
402 if (SUCCEEDED(hr))
403 FIXME("sku=%s not implemented\n", debugstr_wn(value, value_size));
404 hr = S_OK;
407 return hr;
410 static HRESULT WINAPI ConfigFileHandler_startElement(ISAXContentHandler *iface,
411 const WCHAR *pNamespaceUri, int nNamespaceUri, const WCHAR *pLocalName,
412 int nLocalName, const WCHAR *pQName, int nQName, ISAXAttributes *pAttr)
414 ConfigFileHandler *This = impl_from_ISAXContentHandler(iface);
415 static const WCHAR configuration[] = {'c','o','n','f','i','g','u','r','a','t','i','o','n',0};
416 static const WCHAR assemblyBinding[] = {'a','s','s','e','m','b','l','y','B','i','n','d','i','n','g',0};
417 static const WCHAR probing[] = {'p','r','o','b','i','n','g',0};
418 static const WCHAR runtime[] = {'r','u','n','t','i','m','e',0};
419 static const WCHAR startup[] = {'s','t','a','r','t','u','p',0};
420 static const WCHAR supportedRuntime[] = {'s','u','p','p','o','r','t','e','d','R','u','n','t','i','m','e',0};
422 HRESULT hr = S_OK;
424 TRACE("%s %s %s\n", debugstr_wn(pNamespaceUri,nNamespaceUri),
425 debugstr_wn(pLocalName,nLocalName), debugstr_wn(pQName,nQName));
427 if (This->statenum == ARRAY_SIZE(This->states) - 1)
429 ERR("file has too much nesting\n");
430 return E_FAIL;
433 switch (This->states[This->statenum])
435 case STATE_ROOT:
436 if (nLocalName == ARRAY_SIZE(configuration) - 1 && wcscmp(pLocalName, configuration) == 0)
438 This->states[++This->statenum] = STATE_CONFIGURATION;
439 break;
441 else
442 goto unknown;
443 case STATE_CONFIGURATION:
444 if (nLocalName == ARRAY_SIZE(startup) - 1 && wcscmp(pLocalName, startup) == 0)
446 hr = parse_startup(This, pAttr);
447 This->states[++This->statenum] = STATE_STARTUP;
448 break;
450 else if (nLocalName == ARRAY_SIZE(runtime) - 1 && wcscmp(pLocalName, runtime) == 0)
452 This->states[++This->statenum] = STATE_RUNTIME;
453 break;
455 else
456 goto unknown;
457 case STATE_RUNTIME:
458 if (nLocalName == ARRAY_SIZE(assemblyBinding) - 1 &&
459 wcscmp(pLocalName, assemblyBinding) == 0)
461 This->states[++This->statenum] = STATE_ASSEMBLY_BINDING;
462 break;
464 else
465 goto unknown;
466 case STATE_ASSEMBLY_BINDING:
467 if (nLocalName == ARRAY_SIZE(probing) - 1 && wcscmp(pLocalName, probing) == 0)
469 hr = parse_probing(This, pAttr);
470 This->states[++This->statenum] = STATE_PROBING;
471 break;
473 else
474 goto unknown;
475 case STATE_STARTUP:
476 if (nLocalName == ARRAY_SIZE(supportedRuntime) - 1 &&
477 wcscmp(pLocalName, supportedRuntime) == 0)
479 hr = parse_supported_runtime(This, pAttr);
480 This->states[++This->statenum] = STATE_UNKNOWN;
481 break;
483 else
484 goto unknown;
485 default:
486 goto unknown;
489 return hr;
491 unknown:
492 TRACE("Unknown element %s in state %u\n", debugstr_wn(pLocalName,nLocalName),
493 This->states[This->statenum]);
495 This->states[++This->statenum] = STATE_UNKNOWN;
497 return S_OK;
500 static HRESULT WINAPI ConfigFileHandler_endElement(ISAXContentHandler *iface,
501 const WCHAR *pNamespaceUri, int nNamespaceUri, const WCHAR *pLocalName,
502 int nLocalName, const WCHAR *pQName, int nQName)
504 ConfigFileHandler *This = impl_from_ISAXContentHandler(iface);
506 TRACE("%s %s %s\n", debugstr_wn(pNamespaceUri,nNamespaceUri),
507 debugstr_wn(pLocalName,nLocalName), debugstr_wn(pQName,nQName));
509 if (This->statenum > 0)
511 This->statenum--;
513 else
515 ERR("element end does not match a start\n");
516 return E_FAIL;
519 return S_OK;
522 static HRESULT WINAPI ConfigFileHandler_characters(ISAXContentHandler *iface,
523 const WCHAR *pChars, int nChars)
525 TRACE("%s\n", debugstr_wn(pChars,nChars));
527 return S_OK;
530 static HRESULT WINAPI ConfigFileHandler_ignorableWhitespace(ISAXContentHandler *iface,
531 const WCHAR *pChars, int nChars)
533 return S_OK;
536 static HRESULT WINAPI ConfigFileHandler_processingInstruction(ISAXContentHandler *iface,
537 const WCHAR *pTarget, int nTarget, const WCHAR *pData, int nData)
539 return S_OK;
542 static HRESULT WINAPI ConfigFileHandler_skippedEntity(ISAXContentHandler *iface,
543 const WCHAR * pName, int nName)
545 TRACE("%s\n", debugstr_wn(pName,nName));
546 return S_OK;
549 static const struct ISAXContentHandlerVtbl ConfigFileHandlerVtbl =
551 ConfigFileHandler_QueryInterface,
552 ConfigFileHandler_AddRef,
553 ConfigFileHandler_Release,
554 ConfigFileHandler_putDocumentLocator,
555 ConfigFileHandler_startDocument,
556 ConfigFileHandler_endDocument,
557 ConfigFileHandler_startPrefixMapping,
558 ConfigFileHandler_endPrefixMapping,
559 ConfigFileHandler_startElement,
560 ConfigFileHandler_endElement,
561 ConfigFileHandler_characters,
562 ConfigFileHandler_ignorableWhitespace,
563 ConfigFileHandler_processingInstruction,
564 ConfigFileHandler_skippedEntity
567 static HRESULT WINAPI ConfigFileHandler_Error_QueryInterface(ISAXErrorHandler *iface,
568 REFIID riid, void **ppvObject)
570 ConfigFileHandler *This = impl_from_ISAXErrorHandler(iface);
571 return ISAXContentHandler_QueryInterface(&This->ISAXContentHandler_iface, riid, ppvObject);
574 static ULONG WINAPI ConfigFileHandler_Error_AddRef(ISAXErrorHandler *iface)
576 ConfigFileHandler *This = impl_from_ISAXErrorHandler(iface);
577 return ISAXContentHandler_AddRef(&This->ISAXContentHandler_iface);
580 static ULONG WINAPI ConfigFileHandler_Error_Release(ISAXErrorHandler *iface)
582 ConfigFileHandler *This = impl_from_ISAXErrorHandler(iface);
583 return ISAXContentHandler_Release(&This->ISAXContentHandler_iface);
586 static HRESULT WINAPI ConfigFileHandler_error(ISAXErrorHandler *iface,
587 ISAXLocator * pLocator, const WCHAR * pErrorMessage, HRESULT hrErrorCode)
589 WARN("%s,%x\n", debugstr_w(pErrorMessage), hrErrorCode);
590 return S_OK;
593 static HRESULT WINAPI ConfigFileHandler_fatalError(ISAXErrorHandler *iface,
594 ISAXLocator * pLocator, const WCHAR * pErrorMessage, HRESULT hrErrorCode)
596 WARN("%s,%x\n", debugstr_w(pErrorMessage), hrErrorCode);
597 return S_OK;
600 static HRESULT WINAPI ConfigFileHandler_ignorableWarning(ISAXErrorHandler *iface,
601 ISAXLocator * pLocator, const WCHAR * pErrorMessage, HRESULT hrErrorCode)
603 WARN("%s,%x\n", debugstr_w(pErrorMessage), hrErrorCode);
604 return S_OK;
607 static const struct ISAXErrorHandlerVtbl ConfigFileHandlerErrorVtbl =
609 ConfigFileHandler_Error_QueryInterface,
610 ConfigFileHandler_Error_AddRef,
611 ConfigFileHandler_Error_Release,
612 ConfigFileHandler_error,
613 ConfigFileHandler_fatalError,
614 ConfigFileHandler_ignorableWarning
617 static void init_config(parsed_config_file *config)
619 list_init(&config->supported_runtimes);
620 config->private_path = NULL;
623 static HRESULT parse_config(VARIANT input, parsed_config_file *result)
625 ISAXXMLReader *reader;
626 ConfigFileHandler *handler;
627 HRESULT hr;
629 handler = HeapAlloc(GetProcessHeap(), 0, sizeof(ConfigFileHandler));
630 if (!handler)
631 return E_OUTOFMEMORY;
633 handler->ISAXContentHandler_iface.lpVtbl = &ConfigFileHandlerVtbl;
634 handler->ISAXErrorHandler_iface.lpVtbl = &ConfigFileHandlerErrorVtbl;
635 handler->ref = 1;
636 handler->states[0] = STATE_ROOT;
637 handler->statenum = 0;
638 handler->result = result;
640 hr = CoCreateInstance(&CLSID_SAXXMLReader, NULL, CLSCTX_INPROC_SERVER,
641 &IID_ISAXXMLReader, (LPVOID*)&reader);
643 if (SUCCEEDED(hr))
645 hr = ISAXXMLReader_putContentHandler(reader, &handler->ISAXContentHandler_iface);
647 if (SUCCEEDED(hr))
648 hr = ISAXXMLReader_putErrorHandler(reader, &handler->ISAXErrorHandler_iface);
650 if (SUCCEEDED(hr))
651 hr = ISAXXMLReader_parse(reader, input);
653 ISAXXMLReader_Release(reader);
656 ISAXContentHandler_Release(&handler->ISAXContentHandler_iface);
658 return S_OK;
661 HRESULT parse_config_stream(IStream *stream, parsed_config_file *result)
663 VARIANT var;
664 HRESULT hr;
665 HRESULT initresult;
667 init_config(result);
669 initresult = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
670 V_VT(&var) = VT_UNKNOWN;
671 V_UNKNOWN(&var) = (IUnknown*)stream;
673 hr = parse_config(var, result);
675 if (SUCCEEDED(initresult))
676 CoUninitialize();
678 return hr;
681 HRESULT parse_config_file(LPCWSTR filename, parsed_config_file *result)
683 HRESULT hr;
684 IStream *stream;
686 init_config(result);
688 hr = CreateConfigStream(filename, &stream);
689 if (FAILED(hr))
690 return hr;
692 hr = parse_config_stream(stream, result);
694 IStream_Release(stream);
696 return hr;
699 void free_parsed_config_file(parsed_config_file *file)
701 supported_runtime *cursor, *cursor2;
703 LIST_FOR_EACH_ENTRY_SAFE(cursor, cursor2, &file->supported_runtimes, supported_runtime, entry)
705 HeapFree(GetProcessHeap(), 0, cursor->version);
706 list_remove(&cursor->entry);
707 HeapFree(GetProcessHeap(), 0, cursor);
710 HeapFree(GetProcessHeap(), 0, file->private_path);