user32/tests: Fix a test failure on Win95.
[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_eof
100  *
101  * Determines if we are at the end of the INI file
102  *
103  * PARAMS
104  *     uf                  Theme INI file to test
105  */
106 static inline BOOL UXINI_eof(PUXINI_FILE uf)
107 {
108     return uf->lpCurLoc >= uf->lpEnd;
109 }
110
111 /**********************************************************************
112  *      UXINI_isspace
113  *
114  * Check if a character is a space character
115  *
116  * PARAMS
117  *     c                   Character to test
118  */
119 static inline BOOL UXINI_isspace(WCHAR c)
120 {
121     if (isspace(c)) return TRUE;
122     if (c=='\r') return TRUE;
123     return FALSE;
124 }
125
126 /**********************************************************************
127  *      UXINI_GetNextLine
128  *
129  * Get the next line in the INI file, non NULL terminated
130  * removes whitespace at beginning and end of line, and removes comments
131  *
132  * PARAMS
133  *     uf                  INI file to retrieve next line
134  *     dwLen               Location to store pointer to line length
135  *
136  * RETURNS
137  *     The section name, non NULL terminated
138  */
139 static LPCWSTR UXINI_GetNextLine(PUXINI_FILE uf, DWORD *dwLen)
140 {
141     LPCWSTR lpLineEnd;
142     LPCWSTR lpLineStart;
143     DWORD len;
144     do {
145         if(UXINI_eof(uf)) return NULL;
146         /* Skip whitespace and empty lines */
147         while(!UXINI_eof(uf) && (UXINI_isspace(*uf->lpCurLoc) || *uf->lpCurLoc == '\n')) uf->lpCurLoc++;
148         lpLineStart = uf->lpCurLoc;
149         lpLineEnd = uf->lpCurLoc;
150         while(!UXINI_eof(uf) && *uf->lpCurLoc != '\n' && *uf->lpCurLoc != ';') lpLineEnd = ++uf->lpCurLoc;
151         /* If comment was found, skip the rest of the line */
152         if(*uf->lpCurLoc == ';')
153             while(!UXINI_eof(uf) && *uf->lpCurLoc != '\n') uf->lpCurLoc++;
154         len = (lpLineEnd - lpLineStart);
155         if(*lpLineStart != ';' && len == 0)
156             return NULL;
157     } while(*lpLineStart == ';');
158     /* Remove whitespace from end of line */
159     while(UXINI_isspace(lpLineStart[len-1])) len--;
160     *dwLen = len;
161
162     return lpLineStart;
163 }
164
165 static inline void UXINI_UnGetToLine(PUXINI_FILE uf, LPCWSTR lpLine)
166 {
167     uf->lpCurLoc = lpLine;
168 }
169
170 /**********************************************************************
171  *      UXINI_GetNextSection
172  *
173  * Locate the next section in the ini file, and return pointer to
174  * section name, non NULL terminated. Use dwLen to determine length
175  *
176  * PARAMS
177  *     uf                  INI file to search, search starts at current location
178  *     dwLen               Location to store pointer to section name length
179  *
180  * RETURNS
181  *     The section name, non NULL terminated
182  */
183 LPCWSTR UXINI_GetNextSection(PUXINI_FILE uf, DWORD *dwLen)
184 {
185     LPCWSTR lpLine;
186     while((lpLine = UXINI_GetNextLine(uf, dwLen))) {
187         /* Assuming a ']' ending to the section name */
188         if(lpLine[0] == '[') {
189             lpLine++;
190             *dwLen -= 2;
191             break;
192         }
193     }
194     return lpLine;
195 }
196
197 /**********************************************************************
198  *      UXINI_FindSection
199  *
200  * Locate a section with the specified name, search starts
201  * at current location in ini file
202  *
203  * PARAMS
204  *     uf                  INI file to search, search starts at current location
205  *     lpName              Name of the section to locate
206  *
207  * RETURNS
208  *     TRUE if section was found, FALSE otherwise
209  */
210 BOOL UXINI_FindSection(PUXINI_FILE uf, LPCWSTR lpName)
211 {
212     LPCWSTR lpSection;
213     DWORD dwLen;
214     while((lpSection = UXINI_GetNextSection(uf, &dwLen))) {
215         if(CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, lpSection, dwLen, lpName, -1) == CSTR_EQUAL) {
216             return TRUE;
217         }
218     }
219     return FALSE;
220 }
221
222 /**********************************************************************
223  *      UXINI_GetNextValue
224  *
225  * Locate the next value in the current section
226  *
227  * PARAMS
228  *     uf                  INI file to search, search starts at current location
229  *     dwNameLen            Location to store pointer to value name length
230  *     lpValue              Location to store pointer to the value
231  *     dwValueLen           Location to store pointer to value length
232  *
233  * RETURNS
234  *     The value name, non NULL terminated
235  */
236 LPCWSTR UXINI_GetNextValue(PUXINI_FILE uf, DWORD *dwNameLen, LPCWSTR *lpValue, DWORD *dwValueLen)
237 {
238     LPCWSTR lpLine;
239     LPCWSTR lpLineEnd;
240     LPCWSTR name = NULL;
241     LPCWSTR value = NULL;
242     DWORD vallen = 0;
243     DWORD namelen = 0;
244     DWORD dwLen;
245     lpLine = UXINI_GetNextLine(uf, &dwLen);
246     if(!lpLine)
247         return NULL;
248     if(lpLine[0] == '[') {
249         UXINI_UnGetToLine(uf, lpLine);
250         return NULL;
251     }
252     lpLineEnd = lpLine + dwLen;
253
254     name = lpLine;
255     while(namelen < dwLen && *lpLine != '=') {
256         lpLine++;
257         namelen++;
258     }
259     if(*lpLine != '=') return NULL;
260     lpLine++;
261
262     /* Remove whitespace from end of name */
263     while(UXINI_isspace(name[namelen-1])) namelen--;
264     /* Remove whitespace from beginning of value */
265     while(UXINI_isspace(*lpLine) && lpLine < lpLineEnd) lpLine++;
266     value = lpLine;
267     vallen = dwLen-(value-name);
268
269     *dwNameLen = namelen;
270     *dwValueLen = vallen;
271     *lpValue = value;
272
273     return name;
274 }
275
276 /**********************************************************************
277  *      UXINI_FindValue
278  *
279  * Locate a value by name
280  *
281  * PARAMS
282  *     uf                   INI file to search, search starts at current location
283  *     lpName               Value name to locate
284  *     lpValue              Location to store pointer to the value
285  *     dwValueLen           Location to store pointer to value length
286  *
287  * RETURNS
288  *     The value name, non NULL terminated
289  */
290 BOOL UXINI_FindValue(PUXINI_FILE uf, LPCWSTR lpName, LPCWSTR *lpValue, DWORD *dwValueLen)
291 {
292     LPCWSTR name;
293     DWORD namelen;
294
295     while((name = UXINI_GetNextValue(uf, &namelen, lpValue, dwValueLen))) {
296         if(CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, name, namelen, lpName, -1) == CSTR_EQUAL) {
297             return TRUE;
298         }
299     }
300     return FALSE;
301 }