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 
10 #include "global.h"
11 #include "posix.h"
12 #include "buffer.h"
13 
14 /********************************************
15  * New error handling
16  ********************************************/
17 
18 static git_error g_git_oom_error = {
19 	"Out of memory",
20 	GIT_ERROR_NOMEMORY
21 };
22 
set_error_from_buffer(int error_class)23 static void set_error_from_buffer(int error_class)
24 {
25 	git_error *error = &GIT_GLOBAL->error_t;
26 	git_buf *buf = &GIT_GLOBAL->error_buf;
27 
28 	error->message = buf->ptr;
29 	error->klass = error_class;
30 
31 	GIT_GLOBAL->last_error = error;
32 }
33 
set_error(int error_class,char * string)34 static void set_error(int error_class, char *string)
35 {
36 	git_buf *buf = &GIT_GLOBAL->error_buf;
37 
38 	git_buf_clear(buf);
39 	if (string) {
40 		git_buf_puts(buf, string);
41 		git__free(string);
42 	}
43 
44 	set_error_from_buffer(error_class);
45 }
46 
git_error_set_oom(void)47 void git_error_set_oom(void)
48 {
49 	GIT_GLOBAL->last_error = &g_git_oom_error;
50 }
51 
git_error_set(int error_class,const char * string,...)52 void git_error_set(int error_class, const char *string, ...)
53 {
54 	va_list arglist;
55 #ifdef GIT_WIN32
56 	DWORD win32_error_code = (error_class == GIT_ERROR_OS) ? GetLastError() : 0;
57 #endif
58 	int error_code = (error_class == GIT_ERROR_OS) ? errno : 0;
59 	git_buf *buf = &GIT_GLOBAL->error_buf;
60 
61 	git_buf_clear(buf);
62 	if (string) {
63 		va_start(arglist, string);
64 		git_buf_vprintf(buf, string, arglist);
65 		va_end(arglist);
66 
67 		if (error_class == GIT_ERROR_OS)
68 			git_buf_PUTS(buf, ": ");
69 	}
70 
71 	if (error_class == GIT_ERROR_OS) {
72 #ifdef GIT_WIN32
73 		char * win32_error = git_win32_get_error_message(win32_error_code);
74 		if (win32_error) {
75 			git_buf_puts(buf, win32_error);
76 			git__free(win32_error);
77 
78 			SetLastError(0);
79 		}
80 		else
81 #endif
82 		if (error_code)
83 			git_buf_puts(buf, strerror(error_code));
84 
85 		if (error_code)
86 			errno = 0;
87 	}
88 
89 	if (!git_buf_oom(buf))
90 		set_error_from_buffer(error_class);
91 }
92 
git_error_set_str(int error_class,const char * string)93 void git_error_set_str(int error_class, const char *string)
94 {
95 	git_buf *buf = &GIT_GLOBAL->error_buf;
96 
97 	assert(string);
98 
99 	if (!string)
100 		return;
101 
102 	git_buf_clear(buf);
103 	git_buf_puts(buf, string);
104 	if (!git_buf_oom(buf))
105 		set_error_from_buffer(error_class);
106 }
107 
git_error_set_regex(const regex_t * regex,int error_code)108 int git_error_set_regex(const regex_t *regex, int error_code)
109 {
110 	char error_buf[1024];
111 
112 	assert(error_code);
113 
114 	regerror(error_code, regex, error_buf, sizeof(error_buf));
115 	git_error_set_str(GIT_ERROR_REGEX, error_buf);
116 
117 	if (error_code == REG_NOMATCH)
118 		return GIT_ENOTFOUND;
119 
120 	return GIT_EINVALIDSPEC;
121 }
122 
git_error_clear(void)123 void git_error_clear(void)
124 {
125 	if (GIT_GLOBAL->last_error != NULL) {
126 		set_error(0, NULL);
127 		GIT_GLOBAL->last_error = NULL;
128 	}
129 
130 	errno = 0;
131 #ifdef GIT_WIN32
132 	SetLastError(0);
133 #endif
134 }
135 
git_error_last(void)136 const git_error *git_error_last(void)
137 {
138 	return GIT_GLOBAL->last_error;
139 }
140 
git_error_state_capture(git_error_state * state,int error_code)141 int git_error_state_capture(git_error_state *state, int error_code)
142 {
143 	git_error *error = GIT_GLOBAL->last_error;
144 	git_buf *error_buf = &GIT_GLOBAL->error_buf;
145 
146 	memset(state, 0, sizeof(git_error_state));
147 
148 	if (!error_code)
149 		return 0;
150 
151 	state->error_code = error_code;
152 	state->oom = (error == &g_git_oom_error);
153 
154 	if (error) {
155 		state->error_msg.klass = error->klass;
156 
157 		if (state->oom)
158 			state->error_msg.message = g_git_oom_error.message;
159 		else
160 			state->error_msg.message = git_buf_detach(error_buf);
161 	}
162 
163 	git_error_clear();
164 	return error_code;
165 }
166 
git_error_state_restore(git_error_state * state)167 int git_error_state_restore(git_error_state *state)
168 {
169 	int ret = 0;
170 
171 	git_error_clear();
172 
173 	if (state && state->error_msg.message) {
174 		if (state->oom)
175 			git_error_set_oom();
176 		else
177 			set_error(state->error_msg.klass, state->error_msg.message);
178 
179 		ret = state->error_code;
180 		memset(state, 0, sizeof(git_error_state));
181 	}
182 
183 	return ret;
184 }
185 
git_error_state_free(git_error_state * state)186 void git_error_state_free(git_error_state *state)
187 {
188 	if (!state)
189 		return;
190 
191 	if (!state->oom)
192 		git__free(state->error_msg.message);
193 
194 	memset(state, 0, sizeof(git_error_state));
195 }
196 
git_error_system_last(void)197 int git_error_system_last(void)
198 {
199 #ifdef GIT_WIN32
200 	return GetLastError();
201 #else
202 	return errno;
203 #endif
204 }
205 
git_error_system_set(int code)206 void git_error_system_set(int code)
207 {
208 #ifdef GIT_WIN32
209 	SetLastError(code);
210 #else
211 	errno = code;
212 #endif
213 }
214 
215 /* Deprecated error values and functions */
216 
giterr_last(void)217 const git_error *giterr_last(void)
218 {
219 	return git_error_last();
220 }
221 
giterr_clear(void)222 void giterr_clear(void)
223 {
224 	git_error_clear();
225 }
226 
giterr_set_str(int error_class,const char * string)227 void giterr_set_str(int error_class, const char *string)
228 {
229 	git_error_set_str(error_class, string);
230 }
231 
giterr_set_oom(void)232 void giterr_set_oom(void)
233 {
234 	git_error_set_oom();
235 }
236