Change the format of the revprops block sent in svnserve for
[svn.git] / subversion / libsvn_subr / config_win.c
blob1b0e44033afac5d7db9eca7a8661aa8417fe485c
1 /*
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"
23 #ifdef WIN32
24 #define WIN32_LEAN_AND_MEAN
25 #ifdef APR_HAVE_IPV6
26 #include <winsock2.h>
27 #include <Ws2tcpip.h>
28 #include <Wspiapi.h>
29 #endif
30 #include <windows.h>
31 #include <shlobj.h>
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"
41 #include "svn_path.h"
42 #include "svn_pools.h"
43 #include "svn_utf.h"
45 svn_error_t *
46 svn_config__win_config_path(const char **folder, int system_path,
47 apr_pool_t *pool)
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)
53 | CSIDL_FLAG_CREATE);
55 int style;
56 apr_status_t apr_err = apr_filepath_encoding(&style, pool);
58 if (apr_err)
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;
66 char *folder_utf8;
68 if (S_OK != SHGetFolderPathW(NULL, csidl, NULL, SHGFP_TYPE_CURRENT,
69 folder_ucs2))
70 goto no_folder_path;
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;
82 if (apr_err)
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
87 outgoing buffer. */
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,
95 folder_ansi))
96 goto no_folder_path;
97 SVN_ERR(svn_utf_cstring_to_utf8(folder, folder_ansi, pool));
99 else
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);
107 return SVN_NO_ERROR;
109 no_folder_path:
110 return svn_error_create(SVN_ERR_BAD_FILENAME, NULL,
111 (system_path
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. */
118 static svn_error_t *
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;
131 if (apr_err)
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
135 outgoing buffer. */
136 (*ucs2)[outlength - outwords] = L'\0';
137 return SVN_NO_ERROR;
142 #include "config_impl.h"
144 svn_error_t *
145 svn_config__open_file(FILE **pfile,
146 const char *filename,
147 const char *mode,
148 apr_pool_t *pool)
150 int style;
151 apr_status_t apr_err = apr_filepath_encoding(&style, pool);
153 if (apr_err)
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;
160 WCHAR *mode_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);
172 else
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);
179 return SVN_NO_ERROR;
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
188 static svn_error_t *
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;
193 LONG err;
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)
204 break;
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);
236 return SVN_NO_ERROR;
241 /*** Exported interface. ***/
243 svn_error_t *
244 svn_config__parse_registry(svn_config_t *cfg, const char *file,
245 svn_boolean_t must_exist, apr_pool_t *pool)
247 apr_pool_t *subpool;
248 svn_stringbuf_t *section, *option, *value;
249 svn_error_t *svn_err = SVN_NO_ERROR;
250 HKEY base_hkey, hkey;
251 DWORD index;
252 LONG err;
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;
264 else
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,
273 &hkey);
274 if (err != ERROR_SUCCESS)
276 const int is_enoent = APR_STATUS_IS_ENOENT(APR_FROM_OS_ERROR(err));
277 if (!is_enoent)
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));
285 else
286 return SVN_NO_ERROR;
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,
297 option, value);
298 if (svn_err)
299 goto cleanup;
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;
307 HKEY sub_hkey;
309 err = RegEnumKeyEx(hkey, index, section->data, &section_len,
310 NULL, NULL, NULL, &last_write_time);
311 if (err == ERROR_NO_MORE_ITEMS)
312 break;
313 if (err == ERROR_MORE_DATA)
315 svn_stringbuf_ensure(section, section_len);
316 err = RegEnumKeyEx(hkey, index, section->data, &section_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");
323 goto cleanup;
326 err = RegOpenKeyEx(hkey, section->data, 0,
327 KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE,
328 &sub_hkey);
329 if (err != ERROR_SUCCESS)
331 svn_err = svn_error_create(SVN_ERR_MALFORMED_FILE, NULL,
332 "Can't open existing subkey");
333 goto cleanup;
336 svn_err = parse_section(cfg, sub_hkey, section->data, option, value);
337 RegCloseKey(sub_hkey);
338 if (svn_err)
339 goto cleanup;
342 cleanup:
343 RegCloseKey(hkey);
344 svn_pool_destroy(subpool);
345 return svn_err;
348 #endif /* WIN32 */