]> git.proxmox.com Git - libgit2.git/blob - src/sysdir.c
Include stacktrace summary in memory leak output.
[libgit2.git] / src / sysdir.c
1 /*
2 * Copyright (C) the libgit2 contributors. All rights reserved.
3 *
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.
6 */
7
8 #include "common.h"
9 #include "sysdir.h"
10 #include "global.h"
11 #include "buffer.h"
12 #include "path.h"
13 #include <ctype.h>
14 #if GIT_WIN32
15 #include "win32/findfile.h"
16 #endif
17
18 static int git_sysdir_guess_system_dirs(git_buf *out)
19 {
20 #ifdef GIT_WIN32
21 return git_win32__find_system_dirs(out, L"etc\\");
22 #else
23 return git_buf_sets(out, "/etc");
24 #endif
25 }
26
27 static int git_sysdir_guess_global_dirs(git_buf *out)
28 {
29 #ifdef GIT_WIN32
30 return git_win32__find_global_dirs(out);
31 #else
32 return git_buf_sets(out, getenv("HOME"));
33 #endif
34 }
35
36 static int git_sysdir_guess_xdg_dirs(git_buf *out)
37 {
38 #ifdef GIT_WIN32
39 return git_win32__find_xdg_dirs(out);
40 #else
41 const char *env = NULL;
42
43 if ((env = getenv("XDG_CONFIG_HOME")) != NULL)
44 return git_buf_joinpath(out, env, "git");
45 else if ((env = getenv("HOME")) != NULL)
46 return git_buf_joinpath(out, env, ".config/git");
47
48 git_buf_clear(out);
49 return 0;
50 #endif
51 }
52
53 static int git_sysdir_guess_template_dirs(git_buf *out)
54 {
55 #ifdef GIT_WIN32
56 return git_win32__find_system_dirs(out, L"share\\git-core\\templates");
57 #else
58 return git_buf_sets(out, "/usr/share/git-core/templates");
59 #endif
60 }
61
62 typedef int (*git_sysdir_guess_cb)(git_buf *out);
63
64 static git_buf git_sysdir__dirs[GIT_SYSDIR__MAX] =
65 { GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT };
66
67 static git_sysdir_guess_cb git_sysdir__dir_guess[GIT_SYSDIR__MAX] = {
68 git_sysdir_guess_system_dirs,
69 git_sysdir_guess_global_dirs,
70 git_sysdir_guess_xdg_dirs,
71 git_sysdir_guess_template_dirs,
72 };
73
74 static int git_sysdir__dirs_shutdown_set = 0;
75
76 int git_sysdir_global_init(void)
77 {
78 git_sysdir_t i;
79 const git_buf *path;
80 int error = 0;
81
82 for (i = 0; !error && i < GIT_SYSDIR__MAX; i++)
83 error = git_sysdir_get(&path, i);
84
85 return error;
86 }
87
88 void git_sysdir_global_shutdown(void)
89 {
90 int i;
91 for (i = 0; i < GIT_SYSDIR__MAX; ++i)
92 git_buf_free(&git_sysdir__dirs[i]);
93
94 git_sysdir__dirs_shutdown_set = 0;
95 }
96
97 static int git_sysdir_check_selector(git_sysdir_t which)
98 {
99 if (which < GIT_SYSDIR__MAX)
100 return 0;
101
102 giterr_set(GITERR_INVALID, "config directory selector out of range");
103 return -1;
104 }
105
106
107 int git_sysdir_get(const git_buf **out, git_sysdir_t which)
108 {
109 assert(out);
110
111 *out = NULL;
112
113 GITERR_CHECK_ERROR(git_sysdir_check_selector(which));
114
115 if (!git_buf_len(&git_sysdir__dirs[which])) {
116 /* prepare shutdown if we're going to need it */
117 if (!git_sysdir__dirs_shutdown_set) {
118 git__on_shutdown(git_sysdir_global_shutdown);
119 git_sysdir__dirs_shutdown_set = 1;
120 }
121
122 GITERR_CHECK_ERROR(
123 git_sysdir__dir_guess[which](&git_sysdir__dirs[which]));
124 }
125
126 *out = &git_sysdir__dirs[which];
127 return 0;
128 }
129
130 int git_sysdir_get_str(
131 char *out,
132 size_t outlen,
133 git_sysdir_t which)
134 {
135 const git_buf *path = NULL;
136
137 GITERR_CHECK_ERROR(git_sysdir_check_selector(which));
138 GITERR_CHECK_ERROR(git_sysdir_get(&path, which));
139
140 if (!out || path->size >= outlen) {
141 giterr_set(GITERR_NOMEMORY, "Buffer is too short for the path");
142 return GIT_EBUFS;
143 }
144
145 git_buf_copy_cstr(out, outlen, path);
146 return 0;
147 }
148
149 #define PATH_MAGIC "$PATH"
150
151 int git_sysdir_set(git_sysdir_t which, const char *search_path)
152 {
153 const char *expand_path = NULL;
154 git_buf merge = GIT_BUF_INIT;
155
156 GITERR_CHECK_ERROR(git_sysdir_check_selector(which));
157
158 if (search_path != NULL)
159 expand_path = strstr(search_path, PATH_MAGIC);
160
161 /* init with default if not yet done and needed (ignoring error) */
162 if ((!search_path || expand_path) &&
163 !git_buf_len(&git_sysdir__dirs[which]))
164 git_sysdir__dir_guess[which](&git_sysdir__dirs[which]);
165
166 /* if $PATH is not referenced, then just set the path */
167 if (!expand_path)
168 return git_buf_sets(&git_sysdir__dirs[which], search_path);
169
170 /* otherwise set to join(before $PATH, old value, after $PATH) */
171 if (expand_path > search_path)
172 git_buf_set(&merge, search_path, expand_path - search_path);
173
174 if (git_buf_len(&git_sysdir__dirs[which]))
175 git_buf_join(&merge, GIT_PATH_LIST_SEPARATOR,
176 merge.ptr, git_sysdir__dirs[which].ptr);
177
178 expand_path += strlen(PATH_MAGIC);
179 if (*expand_path)
180 git_buf_join(&merge, GIT_PATH_LIST_SEPARATOR, merge.ptr, expand_path);
181
182 git_buf_swap(&git_sysdir__dirs[which], &merge);
183 git_buf_free(&merge);
184
185 return git_buf_oom(&git_sysdir__dirs[which]) ? -1 : 0;
186 }
187
188 static int git_sysdir_find_in_dirlist(
189 git_buf *path,
190 const char *name,
191 git_sysdir_t which,
192 const char *label)
193 {
194 size_t len;
195 const char *scan, *next = NULL;
196 const git_buf *syspath;
197
198 GITERR_CHECK_ERROR(git_sysdir_get(&syspath, which));
199 if (!syspath || !git_buf_len(syspath))
200 goto done;
201
202 for (scan = git_buf_cstr(syspath); scan; scan = next) {
203 /* find unescaped separator or end of string */
204 for (next = scan; *next; ++next) {
205 if (*next == GIT_PATH_LIST_SEPARATOR &&
206 (next <= scan || next[-1] != '\\'))
207 break;
208 }
209
210 len = (size_t)(next - scan);
211 next = (*next ? next + 1 : NULL);
212 if (!len)
213 continue;
214
215 GITERR_CHECK_ERROR(git_buf_set(path, scan, len));
216 if (name)
217 GITERR_CHECK_ERROR(git_buf_joinpath(path, path->ptr, name));
218
219 if (git_path_exists(path->ptr))
220 return 0;
221 }
222
223 done:
224 git_buf_free(path);
225 giterr_set(GITERR_OS, "The %s file '%s' doesn't exist", label, name);
226 return GIT_ENOTFOUND;
227 }
228
229 int git_sysdir_find_system_file(git_buf *path, const char *filename)
230 {
231 return git_sysdir_find_in_dirlist(
232 path, filename, GIT_SYSDIR_SYSTEM, "system");
233 }
234
235 int git_sysdir_find_global_file(git_buf *path, const char *filename)
236 {
237 return git_sysdir_find_in_dirlist(
238 path, filename, GIT_SYSDIR_GLOBAL, "global");
239 }
240
241 int git_sysdir_find_xdg_file(git_buf *path, const char *filename)
242 {
243 return git_sysdir_find_in_dirlist(
244 path, filename, GIT_SYSDIR_XDG, "global/xdg");
245 }
246
247 int git_sysdir_find_template_dir(git_buf *path)
248 {
249 return git_sysdir_find_in_dirlist(
250 path, NULL, GIT_SYSDIR_TEMPLATE, "template");
251 }
252