2 * Copyright (C) the libgit2 contributors. All rights reserved.
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
15 #include "win32/findfile.h"
21 static int git_sysdir_guess_programdata_dirs(git_buf
*out
)
24 return git_win32__find_programdata_dirs(out
);
31 static int git_sysdir_guess_system_dirs(git_buf
*out
)
34 return git_win32__find_system_dirs(out
, L
"etc\\");
36 return git_buf_sets(out
, "/etc");
41 static int get_passwd_home(git_buf
*out
, uid_t uid
)
43 struct passwd pwd
, *pwdptr
;
50 if ((buflen
= sysconf(_SC_GETPW_R_SIZE_MAX
)) == -1)
54 buf
= git__realloc(buf
, buflen
);
55 error
= getpwuid_r(uid
, &pwd
, buf
, buflen
, &pwdptr
);
57 } while (error
== ERANGE
&& buflen
<= 8192);
60 git_error_set(GIT_ERROR_OS
, "failed to get passwd entry");
65 git_error_set(GIT_ERROR_OS
, "no passwd entry found for user");
69 if ((error
= git_buf_puts(out
, pwdptr
->pw_dir
)) < 0)
78 static int git_sysdir_guess_global_dirs(git_buf
*out
)
81 return git_win32__find_global_dirs(out
);
90 * In case we are running setuid, use the configuration
91 * of the effective user.
94 error
= git__getenv(out
, "HOME");
96 error
= get_passwd_home(out
, euid
);
98 if (error
== GIT_ENOTFOUND
) {
107 static int git_sysdir_guess_xdg_dirs(git_buf
*out
)
110 return git_win32__find_xdg_dirs(out
);
112 git_buf env
= GIT_BUF_INIT
;
120 * In case we are running setuid, only look up passwd
121 * directory of the effective user.
124 if ((error
= git__getenv(&env
, "XDG_CONFIG_HOME")) == 0)
125 error
= git_buf_joinpath(out
, env
.ptr
, "git");
127 if (error
== GIT_ENOTFOUND
&& (error
= git__getenv(&env
, "HOME")) == 0)
128 error
= git_buf_joinpath(out
, env
.ptr
, ".config/git");
130 if ((error
= get_passwd_home(&env
, euid
)) == 0)
131 error
= git_buf_joinpath(out
, env
.ptr
, ".config/git");
134 if (error
== GIT_ENOTFOUND
) {
139 git_buf_dispose(&env
);
144 static int git_sysdir_guess_template_dirs(git_buf
*out
)
147 return git_win32__find_system_dirs(out
, L
"share\\git-core\\templates");
149 return git_buf_sets(out
, "/usr/share/git-core/templates");
153 struct git_sysdir__dir
{
155 int (*guess
)(git_buf
*out
);
158 static struct git_sysdir__dir git_sysdir__dirs
[] = {
159 { GIT_BUF_INIT
, git_sysdir_guess_system_dirs
},
160 { GIT_BUF_INIT
, git_sysdir_guess_global_dirs
},
161 { GIT_BUF_INIT
, git_sysdir_guess_xdg_dirs
},
162 { GIT_BUF_INIT
, git_sysdir_guess_programdata_dirs
},
163 { GIT_BUF_INIT
, git_sysdir_guess_template_dirs
},
166 static void git_sysdir_global_shutdown(void)
170 for (i
= 0; i
< ARRAY_SIZE(git_sysdir__dirs
); ++i
)
171 git_buf_dispose(&git_sysdir__dirs
[i
].buf
);
174 int git_sysdir_global_init(void)
179 for (i
= 0; !error
&& i
< ARRAY_SIZE(git_sysdir__dirs
); i
++)
180 error
= git_sysdir__dirs
[i
].guess(&git_sysdir__dirs
[i
].buf
);
182 git__on_shutdown(git_sysdir_global_shutdown
);
187 static int git_sysdir_check_selector(git_sysdir_t which
)
189 if (which
< ARRAY_SIZE(git_sysdir__dirs
))
192 git_error_set(GIT_ERROR_INVALID
, "config directory selector out of range");
197 int git_sysdir_get(const git_buf
**out
, git_sysdir_t which
)
203 GIT_ERROR_CHECK_ERROR(git_sysdir_check_selector(which
));
205 *out
= &git_sysdir__dirs
[which
].buf
;
209 int git_sysdir_get_str(
214 const git_buf
*path
= NULL
;
216 GIT_ERROR_CHECK_ERROR(git_sysdir_check_selector(which
));
217 GIT_ERROR_CHECK_ERROR(git_sysdir_get(&path
, which
));
219 if (!out
|| path
->size
>= outlen
) {
220 git_error_set(GIT_ERROR_NOMEMORY
, "buffer is too short for the path");
224 git_buf_copy_cstr(out
, outlen
, path
);
228 #define PATH_MAGIC "$PATH"
230 int git_sysdir_set(git_sysdir_t which
, const char *search_path
)
232 const char *expand_path
= NULL
;
233 git_buf merge
= GIT_BUF_INIT
;
235 GIT_ERROR_CHECK_ERROR(git_sysdir_check_selector(which
));
237 if (search_path
!= NULL
)
238 expand_path
= strstr(search_path
, PATH_MAGIC
);
240 /* reset the default if this path has been cleared */
242 git_sysdir__dirs
[which
].guess(&git_sysdir__dirs
[which
].buf
);
244 /* if $PATH is not referenced, then just set the path */
247 git_buf_sets(&git_sysdir__dirs
[which
].buf
, search_path
);
252 /* otherwise set to join(before $PATH, old value, after $PATH) */
253 if (expand_path
> search_path
)
254 git_buf_set(&merge
, search_path
, expand_path
- search_path
);
256 if (git_buf_len(&git_sysdir__dirs
[which
].buf
))
257 git_buf_join(&merge
, GIT_PATH_LIST_SEPARATOR
,
258 merge
.ptr
, git_sysdir__dirs
[which
].buf
.ptr
);
260 expand_path
+= strlen(PATH_MAGIC
);
262 git_buf_join(&merge
, GIT_PATH_LIST_SEPARATOR
, merge
.ptr
, expand_path
);
264 git_buf_swap(&git_sysdir__dirs
[which
].buf
, &merge
);
265 git_buf_dispose(&merge
);
268 if (git_buf_oom(&git_sysdir__dirs
[which
].buf
))
274 static int git_sysdir_find_in_dirlist(
281 const char *scan
, *next
= NULL
;
282 const git_buf
*syspath
;
284 GIT_ERROR_CHECK_ERROR(git_sysdir_get(&syspath
, which
));
285 if (!syspath
|| !git_buf_len(syspath
))
288 for (scan
= git_buf_cstr(syspath
); scan
; scan
= next
) {
289 /* find unescaped separator or end of string */
290 for (next
= scan
; *next
; ++next
) {
291 if (*next
== GIT_PATH_LIST_SEPARATOR
&&
292 (next
<= scan
|| next
[-1] != '\\'))
296 len
= (size_t)(next
- scan
);
297 next
= (*next
? next
+ 1 : NULL
);
301 GIT_ERROR_CHECK_ERROR(git_buf_set(path
, scan
, len
));
303 GIT_ERROR_CHECK_ERROR(git_buf_joinpath(path
, path
->ptr
, name
));
305 if (git_path_exists(path
->ptr
))
310 git_buf_dispose(path
);
311 git_error_set(GIT_ERROR_OS
, "the %s file '%s' doesn't exist", label
, name
);
312 return GIT_ENOTFOUND
;
315 int git_sysdir_find_system_file(git_buf
*path
, const char *filename
)
317 return git_sysdir_find_in_dirlist(
318 path
, filename
, GIT_SYSDIR_SYSTEM
, "system");
321 int git_sysdir_find_global_file(git_buf
*path
, const char *filename
)
323 return git_sysdir_find_in_dirlist(
324 path
, filename
, GIT_SYSDIR_GLOBAL
, "global");
327 int git_sysdir_find_xdg_file(git_buf
*path
, const char *filename
)
329 return git_sysdir_find_in_dirlist(
330 path
, filename
, GIT_SYSDIR_XDG
, "global/xdg");
333 int git_sysdir_find_programdata_file(git_buf
*path
, const char *filename
)
335 return git_sysdir_find_in_dirlist(
336 path
, filename
, GIT_SYSDIR_PROGRAMDATA
, "ProgramData");
339 int git_sysdir_find_template_dir(git_buf
*path
)
341 return git_sysdir_find_in_dirlist(
342 path
, NULL
, GIT_SYSDIR_TEMPLATE
, "template");
345 int git_sysdir_expand_global_file(git_buf
*path
, const char *filename
)
349 if ((error
= git_sysdir_find_global_file(path
, NULL
)) == 0) {
351 error
= git_buf_joinpath(path
, path
->ptr
, filename
);