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