advapi32: Fix GetServiceKeyNameW behavior to pass the tests.
[wine] / dlls / uxtheme / uxini.c
1 /*
2  * Win32 5.1 uxtheme ini file processing
3  *
4  * Copyright (C) 2004 Kevin Koltzau
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 "config.h"
22
23 #include <stdarg.h>
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winnls.h"
28
29 #include "wine/debug.h"
30
31 WINE_DEFAULT_DEBUG_CHANNEL(uxtheme);
32
33 /***********************************************************************
34  * Defines and global variables
35  */
36
37 static const WCHAR szTextFileResource[] = {
38     'T','E','X','T','F','I','L','E','\0'
39 };
40
41 typedef struct _UXINI_FILE {
42     LPCWSTR lpIni;
43     LPCWSTR lpCurLoc;
44     LPCWSTR lpEnd;
45 } UXINI_FILE, *PUXINI_FILE;
46
47 /***********************************************************************/
48
49 /**********************************************************************
50  *      UXINI_LoadINI
51  *
52  * Load a theme INI file out of resources from the specified
53  * theme
54  *
55  * PARAMS
56  *     tf                  Theme to load INI file out of resources
57  *     lpName              Resource name of the INI file
58  *
59  * RETURNS
60  *     INI file, or NULL if not found
61  */
62 PUXINI_FILE UXINI_LoadINI(HMODULE hTheme, LPCWSTR lpName) {
63     HRSRC hrsc;
64     LPCWSTR lpThemesIni = NULL;
65     PUXINI_FILE uf;
66     DWORD dwIniSize;
67
68     TRACE("Loading resource INI %s\n", debugstr_w(lpName));
69
70     if((hrsc = FindResourceW(hTheme, lpName, szTextFileResource))) {
71         if(!(lpThemesIni = LoadResource(hTheme, hrsc))) {
72             TRACE("%s resource not found\n", debugstr_w(lpName));
73             return NULL;
74         }
75     }
76
77     dwIniSize = SizeofResource(hTheme, hrsc) / sizeof(WCHAR);
78     uf = HeapAlloc(GetProcessHeap(), 0, sizeof(UXINI_FILE));
79     uf->lpIni = lpThemesIni;
80     uf->lpCurLoc = lpThemesIni;
81     uf->lpEnd = lpThemesIni + dwIniSize;
82     return uf;
83 }
84
85 /**********************************************************************
86  *      UXINI_CloseINI
87  *
88  * Close an open theme INI file
89  *
90  * PARAMS
91  *     uf                  Theme INI file to close
92  */
93 void UXINI_CloseINI(PUXINI_FILE uf)
94 {
95     HeapFree(GetProcessHeap(), 0, uf);
96 }
97
98 /**********************************************************************
99  *      UXINI_ResetINI
100  *
101  * Reset the current pointer into INI file to the beginning of the file
102  *
103  * PARAMS
104  *     uf                  Theme INI file to reset
105  */
106 void UXINI_ResetINI(PUXINI_FILE uf)
107 {
108     uf->lpCurLoc = uf->lpIni;
109 }
110
111 /**********************************************************************
112  *      UXINI_eof
113  *
114  * Determines if we are at the end of the INI file
115  *
116  * PARAMS
117  *     uf                  Theme INI file to test
118  */
119 static inline BOOL UXINI_eof(PUXINI_FILE uf)
120 {
121     return uf->lpCurLoc >= uf->lpEnd;
122 }
123
124 /**********************************************************************
125  *      UXINI_isspace
126  *
127  * Check if a character is a space character
128  *
129  * PARAMS
130  *     c                   Character to test
131  */
132 static inline BOOL UXINI_isspace(WCHAR c)
133 {
134     if (isspace(c)) return TRUE;
135     if (c=='\r') return TRUE;
136     return FALSE;
137 }
138
139 /**********************************************************************
140  *      UXINI_GetNextLine
141  *
142  * Get the next line in the INI file, non NULL terminated
143  * removes whitespace at beginning and end of line, and removes comments
144  *
145  * PARAMS
146  *     uf                  INI file to retrieve next line
147  *     dwLen               Location to store pointer to line length
148  *
149  * RETURNS
150  *     The section name, non NULL terminated
151  */
152 static LPCWSTR UXINI_GetNextLine(PUXINI_FILE uf, DWORD *dwLen)
153 {
154     LPCWSTR lpLineEnd;
155     LPCWSTR lpLineStart;
156     DWORD len;
157     do {
158         if(UXINI_eof(uf)) return NULL;
159         /* Skip whitespace and empty lines */
160         while(!UXINI_eof(uf) && (UXINI_isspace(*uf->lpCurLoc) || *uf->lpCurLoc == '\n')) uf->lpCurLoc++;
161         lpLineStart = uf->lpCurLoc;
162         lpLineEnd = uf->lpCurLoc;
163         while(!UXINI_eof(uf) && *uf->lpCurLoc != '\n' && *uf->lpCurLoc != ';') lpLineEnd = ++uf->lpCurLoc;
164         /* If comment was found, skip the rest of the line */
165         if(*uf->lpCurLoc == ';')
166             while(!UXINI_eof(uf) && *uf->lpCurLoc != '\n') uf->lpCurLoc++;
167         len = (lpLineEnd - lpLineStart);
168         if(*lpLineStart != ';' && len == 0)
169             return NULL;
170     } while(*lpLineStart == ';');
171     /* Remove whitespace from end of line */
172     while(UXINI_isspace(lpLineStart[len-1])) len--;
173     *dwLen = len;
174
175     return lpLineStart;
176 }
177
178 static inline void UXINI_UnGetToLine(PUXINI_FILE uf, LPCWSTR lpLine)
179 {
180     uf->lpCurLoc = lpLine;
181 }
182
183 /**********************************************************************
184  *      UXINI_GetNextSection
185  *
186  * Locate the next section in the ini file, and return pointer to
187  * section name, non NULL terminated. Use dwLen to determine length
188  *
189  * PARAMS
190  *     uf                  INI file to search, search starts at current location
191  *     dwLen               Location to store pointer to section name length
192  *
193  * RETURNS
194  *     The section name, non NULL terminated
195  */
196 LPCWSTR UXINI_GetNextSection(PUXINI_FILE uf, DWORD *dwLen)
197 {
198     LPCWSTR lpLine;
199     while((lpLine = UXINI_GetNextLine(uf, dwLen))) {
200         /* Assuming a ']' ending to the section name */
201         if(lpLine[0] == '[') {
202             lpLine++;
203             *dwLen -= 2;
204             break;
205         }
206     }
207     return lpLine;
208 }
209
210 /**********************************************************************
211  *      UXINI_FindSection
212  *
213  * Locate a section with the specified name, search starts
214  * at current location in ini file
215  * to start search from start, call UXINI_ResetINI
216  *
217  * PARAMS
218  *     uf                  INI file to search, search starts at current location
219  *     lpName              Name of the section to locate
220  *
221  * RETURNS
222  *     TRUE if section was found, FALSE otherwise
223  */
224 BOOL UXINI_FindSection(PUXINI_FILE uf, LPCWSTR lpName)
225 {
226     LPCWSTR lpSection;
227     DWORD dwLen;
228     while((lpSection = UXINI_GetNextSection(uf, &dwLen))) {
229         if(CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, lpSection, dwLen, lpName, -1) == CSTR_EQUAL) {
230             return TRUE;
231         }
232     }
233     return FALSE;
234 }
235
236 /**********************************************************************
237  *      UXINI_GetNextValue
238  *
239  * Locate the next value in the current section
240  *
241  * PARAMS
242  *     uf                  INI file to search, search starts at current location
243  *     dwNameLen            Location to store pointer to value name length
244  *     lpValue              Location to store pointer to the value
245  *     dwValueLen           Location to store pointer to value length
246  *
247  * RETURNS
248  *     The value name, non NULL terminated
249  */
250 LPCWSTR UXINI_GetNextValue(PUXINI_FILE uf, DWORD *dwNameLen, LPCWSTR *lpValue, DWORD *dwValueLen)
251 {
252     LPCWSTR lpLine;
253     LPCWSTR lpLineEnd;
254     LPCWSTR name = NULL;
255     LPCWSTR value = NULL;
256     DWORD vallen = 0;
257     DWORD namelen = 0;
258     DWORD dwLen;
259     lpLine = UXINI_GetNextLine(uf, &dwLen);
260     if(!lpLine)
261         return NULL;
262     if(lpLine[0] == '[') {
263         UXINI_UnGetToLine(uf, lpLine);
264         return NULL;
265     }
266     lpLineEnd = lpLine + dwLen;
267
268     name = lpLine;
269     while(namelen < dwLen && *lpLine != '=') {
270         lpLine++;
271         namelen++;
272     }
273     if(*lpLine != '=') return NULL;
274     lpLine++;
275
276     /* Remove whitespace from end of name */
277     while(UXINI_isspace(name[namelen-1])) namelen--;
278     /* Remove whitespace from beginning of value */
279     while(UXINI_isspace(*lpLine) && lpLine < lpLineEnd) lpLine++;
280     value = lpLine;
281     vallen = dwLen-(value-name);
282
283     *dwNameLen = namelen;
284     *dwValueLen = vallen;
285     *lpValue = value;
286
287     return name;
288 }
289
290 /**********************************************************************
291  *      UXINI_FindValue
292  *
293  * Locate a value by name
294  *
295  * PARAMS
296  *     uf                   INI file to search, search starts at current location
297  *     lpName               Value name to locate
298  *     lpValue              Location to store pointer to the value
299  *     dwValueLen           Location to store pointer to value length
300  *
301  * RETURNS
302  *     The value name, non NULL terminated
303  */
304 BOOL UXINI_FindValue(PUXINI_FILE uf, LPCWSTR lpName, LPCWSTR *lpValue, DWORD *dwValueLen)
305 {
306     LPCWSTR name;
307     DWORD namelen;
308
309     while((name = UXINI_GetNextValue(uf, &namelen, lpValue, dwValueLen))) {
310         if(CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, name, namelen, lpName, -1) == CSTR_EQUAL) {
311             return TRUE;
312         }
313     }
314     return FALSE;
315 }