2 * config_win.c : parsing configuration data from the registry
4 * ====================================================================
5 * Copyright (c) 2000-2004 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
21 #include "svn_private_config.h"
24 #define WIN32_LEAN_AND_MEAN
33 #include <apr_file_info.h>
34 /* FIXME: We're using an internal APR header here, which means we
35 have to build Subversion with APR sources. This being Win32-only,
36 that should be fine for now, but a better solution must be found in
37 combination with issue #850. */
38 #include <arch/win32/apr_arch_utf8.h>
40 #include "svn_error.h"
42 #include "svn_pools.h"
46 svn_config__win_config_path(const char **folder
, int system_path
,
49 /* ### Adding CSIDL_FLAG_CREATE here, because those folders really
50 must exist. I'm not too sure about the SHGFP_TYPE_CURRENT
51 semancics, though; maybe we should use ..._DEFAULT instead? */
52 const int csidl
= ((system_path
? CSIDL_COMMON_APPDATA
: CSIDL_APPDATA
)
56 apr_status_t apr_err
= apr_filepath_encoding(&style
, pool
);
59 return svn_error_wrap_apr(apr_err
,
60 "Can't determine the native path encoding");
62 if (style
== APR_FILEPATH_ENCODING_UTF8
)
64 WCHAR folder_ucs2
[MAX_PATH
];
65 apr_size_t inwords
, outbytes
, outlength
;
68 if (S_OK
!= SHGetFolderPathW(NULL
, csidl
, NULL
, SHGFP_TYPE_CURRENT
,
72 /* ### When mapping from UCS-2 to UTF-8, we need at most 3 bytes
73 per wide char, plus extra space for the nul terminator. */
74 inwords
= lstrlenW(folder_ucs2
);
75 outbytes
= outlength
= 3 * (inwords
+ 1);
76 folder_utf8
= apr_palloc(pool
, outlength
);
78 apr_err
= apr_conv_ucs2_to_utf8(folder_ucs2
, &inwords
,
79 folder_utf8
, &outbytes
);
80 if (!apr_err
&& (inwords
> 0 || outbytes
== 0))
81 apr_err
= APR_INCOMPLETE
;
83 return svn_error_wrap_apr(apr_err
,
84 "Can't convert config path to UTF-8");
86 /* Note that apr_conv_ucs2_to_utf8 does _not_ terminate the
88 folder_utf8
[outlength
- outbytes
] = '\0';
89 *folder
= folder_utf8
;
91 else if (style
== APR_FILEPATH_ENCODING_LOCALE
)
93 char folder_ansi
[MAX_PATH
];
94 if (S_OK
!= SHGetFolderPathA(NULL
, csidl
, NULL
, SHGFP_TYPE_CURRENT
,
97 SVN_ERR(svn_utf_cstring_to_utf8(folder
, folder_ansi
, pool
));
101 /* There is no third option on Windows; we should never get here. */
102 return svn_error_createf(APR_EINVAL
, NULL
,
103 "Unknown native path encoding (%d)", style
);
106 *folder
= svn_path_internal_style(*folder
, pool
);
110 return svn_error_create(SVN_ERR_BAD_FILENAME
, NULL
,
112 ? "Can't determine the system config path"
113 : "Can't determine the user's config path"));
116 /* Convert UTF8, a UTF-8 encoded string, to UCS2, a UCS-2 encoded
117 string, using POOL for temporary allocations. */
119 utf8_to_ucs2(WCHAR
**ucs2
, const char *utf8
, apr_pool_t
*pool
)
121 apr_size_t inbytes
, outwords
, outlength
;
122 apr_status_t apr_err
;
124 inbytes
= lstrlenA(utf8
);
125 outwords
= outlength
= inbytes
+ 1; /* Include terminating null. */
126 *ucs2
= apr_palloc(pool
, outwords
* sizeof(WCHAR
));
127 apr_err
= apr_conv_utf8_to_ucs2(utf8
, &inbytes
, *ucs2
, &outwords
);
129 if (!apr_err
&& (inbytes
> 0 || outwords
== 0))
130 apr_err
= APR_INCOMPLETE
;
132 return svn_error_wrap_apr(apr_err
, "Can't convert config path to UCS-2");
134 /* Note that apr_conv_utf8_to_ucs2 does _not_ terminate the
136 (*ucs2
)[outlength
- outwords
] = L
'\0';
142 #include "config_impl.h"
145 svn_config__open_file(FILE **pfile
,
146 const char *filename
,
151 apr_status_t apr_err
= apr_filepath_encoding(&style
, pool
);
154 return svn_error_wrap_apr(apr_err
,
155 "Can't determine the native path encoding");
157 if (style
== APR_FILEPATH_ENCODING_UTF8
)
159 WCHAR
*filename_ucs2
;
162 SVN_ERR(utf8_to_ucs2(&filename_ucs2
, filename
, pool
));
163 SVN_ERR(utf8_to_ucs2(&mode_ucs2
, mode
, pool
));
164 *pfile
= _wfopen(filename_ucs2
, mode_ucs2
);
166 else if (style
== APR_FILEPATH_ENCODING_LOCALE
)
168 const char *filename_native
;
169 SVN_ERR(svn_utf_cstring_from_utf8(&filename_native
, filename
, pool
));
170 *pfile
= fopen(filename_native
, mode
);
174 /* There is no third option on Windows; we should never get here. */
175 return svn_error_createf(APR_EINVAL
, NULL
,
176 "Unknown native path encoding (%d)", style
);
182 /* ### These constants are insanely large, but (a) we want to avoid
183 reallocating strings if possible, and (b) the realloc logic might
184 not actually work -- you never know with Win32 ... */
185 #define SVN_REG_DEFAULT_NAME_SIZE 2048
186 #define SVN_REG_DEFAULT_VALUE_SIZE 8192
189 parse_section(svn_config_t
*cfg
, HKEY hkey
, const char *section
,
190 svn_stringbuf_t
*option
, svn_stringbuf_t
*value
)
192 DWORD option_len
, type
, index
;
195 /* Start with a reasonable size for the buffers. */
196 svn_stringbuf_ensure(option
, SVN_REG_DEFAULT_NAME_SIZE
);
197 svn_stringbuf_ensure(value
, SVN_REG_DEFAULT_VALUE_SIZE
);
198 for (index
= 0; ; ++index
)
200 option_len
= option
->blocksize
;
201 err
= RegEnumValue(hkey
, index
, option
->data
, &option_len
,
202 NULL
, &type
, NULL
, NULL
);
203 if (err
== ERROR_NO_MORE_ITEMS
)
205 if (err
== ERROR_INSUFFICIENT_BUFFER
)
207 svn_stringbuf_ensure(option
, option_len
);
208 err
= RegEnumValue(hkey
, index
, option
->data
, &option_len
,
209 NULL
, &type
, NULL
, NULL
);
211 if (err
!= ERROR_SUCCESS
)
212 return svn_error_create(SVN_ERR_MALFORMED_FILE
, NULL
,
213 "Can't enumerate registry values");
215 /* Ignore option names that start with '#', see
216 http://subversion.tigris.org/issues/show_bug.cgi?id=671 */
217 if (type
== REG_SZ
&& option
->data
[0] != '#')
219 DWORD value_len
= value
->blocksize
;
220 err
= RegQueryValueEx(hkey
, option
->data
, NULL
, NULL
,
221 value
->data
, &value_len
);
222 if (err
== ERROR_MORE_DATA
)
224 svn_stringbuf_ensure(value
, value_len
);
225 err
= RegQueryValueEx(hkey
, option
->data
, NULL
, NULL
,
226 value
->data
, &value_len
);
228 if (err
!= ERROR_SUCCESS
)
229 return svn_error_create(SVN_ERR_MALFORMED_FILE
, NULL
,
230 "Can't read registry value data");
232 svn_config_set(cfg
, section
, option
->data
, value
->data
);
241 /*** Exported interface. ***/
244 svn_config__parse_registry(svn_config_t
*cfg
, const char *file
,
245 svn_boolean_t must_exist
, apr_pool_t
*pool
)
248 svn_stringbuf_t
*section
, *option
, *value
;
249 svn_error_t
*svn_err
= SVN_NO_ERROR
;
250 HKEY base_hkey
, hkey
;
254 if (0 == strncmp(file
, SVN_REGISTRY_HKLM
, SVN_REGISTRY_HKLM_LEN
))
256 base_hkey
= HKEY_LOCAL_MACHINE
;
257 file
+= SVN_REGISTRY_HKLM_LEN
;
259 else if (0 == strncmp(file
, SVN_REGISTRY_HKCU
, SVN_REGISTRY_HKCU_LEN
))
261 base_hkey
= HKEY_CURRENT_USER
;
262 file
+= SVN_REGISTRY_HKCU_LEN
;
266 return svn_error_createf(SVN_ERR_BAD_FILENAME
, NULL
,
267 "Unrecognised registry path '%s'",
268 svn_path_local_style(file
, pool
));
271 err
= RegOpenKeyEx(base_hkey
, file
, 0,
272 KEY_ENUMERATE_SUB_KEYS
| KEY_QUERY_VALUE
,
274 if (err
!= ERROR_SUCCESS
)
276 const int is_enoent
= APR_STATUS_IS_ENOENT(APR_FROM_OS_ERROR(err
));
278 return svn_error_createf(SVN_ERR_BAD_FILENAME
, NULL
,
279 "Can't open registry key '%s'",
280 svn_path_local_style(file
, pool
));
281 else if (must_exist
&& is_enoent
)
282 return svn_error_createf(SVN_ERR_BAD_FILENAME
, NULL
,
283 "Can't find registry key '%s'",
284 svn_path_local_style(file
, pool
));
290 subpool
= svn_pool_create(pool
);
291 section
= svn_stringbuf_create("", subpool
);
292 option
= svn_stringbuf_create("", subpool
);
293 value
= svn_stringbuf_create("", subpool
);
295 /* The top-level values belong to the [DEFAULT] section */
296 svn_err
= parse_section(cfg
, hkey
, SVN_CONFIG__DEFAULT_SECTION
,
301 /* Now enumerate the rest of the keys. */
302 svn_stringbuf_ensure(section
, SVN_REG_DEFAULT_NAME_SIZE
);
303 for (index
= 0; ; ++index
)
305 DWORD section_len
= section
->blocksize
;
306 FILETIME last_write_time
;
309 err
= RegEnumKeyEx(hkey
, index
, section
->data
, §ion_len
,
310 NULL
, NULL
, NULL
, &last_write_time
);
311 if (err
== ERROR_NO_MORE_ITEMS
)
313 if (err
== ERROR_MORE_DATA
)
315 svn_stringbuf_ensure(section
, section_len
);
316 err
= RegEnumKeyEx(hkey
, index
, section
->data
, §ion_len
,
317 NULL
, NULL
, NULL
, &last_write_time
);
319 if (err
!= ERROR_SUCCESS
)
321 svn_err
= svn_error_create(SVN_ERR_MALFORMED_FILE
, NULL
,
322 "Can't enumerate registry keys");
326 err
= RegOpenKeyEx(hkey
, section
->data
, 0,
327 KEY_ENUMERATE_SUB_KEYS
| KEY_QUERY_VALUE
,
329 if (err
!= ERROR_SUCCESS
)
331 svn_err
= svn_error_create(SVN_ERR_MALFORMED_FILE
, NULL
,
332 "Can't open existing subkey");
336 svn_err
= parse_section(cfg
, sub_hkey
, section
->data
, option
, value
);
337 RegCloseKey(sub_hkey
);
344 svn_pool_destroy(subpool
);