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 	git_buf_clear(buf);
111 	git_buf_puts(buf, string);
112 
113 	if (git_buf_oom(buf))
114 		return -1;
115 
116 	set_error_from_buffer(error_class);
117 	return 0;
118 }
119 
git_error_clear(void)120 void git_error_clear(void)
121 {
122 	if (GIT_THREADSTATE->last_error != NULL) {
123 		set_error(0, NULL);
124 		GIT_THREADSTATE->last_error = NULL;
125 	}
126 
127 	errno = 0;
128 #ifdef GIT_WIN32
129 	SetLastError(0);
130 #endif
131 }
132 
git_error_last(void)133 const git_error *git_error_last(void)
134 {
135 	/* If the library is not initialized, return a static error. */
136 	if (!git_libgit2_init_count())
137 		return &g_git_uninitialized_error;
138 
139 	return GIT_THREADSTATE->last_error;
140 }
141 
git_error_state_capture(git_error_state * state,int error_code)142 int git_error_state_capture(git_error_state *state, int error_code)
143 {
144 	git_error *error = GIT_THREADSTATE->last_error;
145 	git_buf *error_buf = &GIT_THREADSTATE->error_buf;
146 
147 	memset(state, 0, sizeof(git_error_state));
148 
149 	if (!error_code)
150 		return 0;
151 
152 	state->error_code = error_code;
153 	state->oom = (error == &g_git_oom_error);
154 
155 	if (error) {
156 		state->error_msg.klass = error->klass;
157 
158 		if (state->oom)
159 			state->error_msg.message = g_git_oom_error.message;
160 		else
161 			state->error_msg.message = git_buf_detach(error_buf);
162 	}
163 
164 	git_error_clear();
165 	return error_code;
166 }
167 
git_error_state_restore(git_error_state * state)168 int git_error_state_restore(git_error_state *state)
169 {
170 	int ret = 0;
171 
172 	git_error_clear();
173 
174 	if (state && state->error_msg.message) {
175 		if (state->oom)
176 			git_error_set_oom();
177 		else
178 			set_error(state->error_msg.klass, state->error_msg.message);
179 
180 		ret = state->error_code;
181 		memset(state, 0, sizeof(git_error_state));
182 	}
183 
184 	return ret;
185 }
186 
git_error_state_free(git_error_state * state)187 void git_error_state_free(git_error_state *state)
188 {
189 	if (!state)
190 		return;
191 
192 	if (!state->oom)
193 		git__free(state->error_msg.message);
194 
195 	memset(state, 0, sizeof(git_error_state));
196 }
197 
git_error_system_last(void)198 int git_error_system_last(void)
199 {
200 #ifdef GIT_WIN32
201 	return GetLastError();
202 #else
203 	return errno;
204 #endif
205 }
206 
git_error_system_set(int code)207 void git_error_system_set(int code)
208 {
209 #ifdef GIT_WIN32
210 	SetLastError(code);
211 #else
212 	errno = code;
213 #endif
214 }
215 
216 /* Deprecated error values and functions */
217 
218 #ifndef GIT_DEPRECATE_HARD
giterr_last(void)219 const git_error *giterr_last(void)
220 {
221 	return git_error_last();
222 }
223 
giterr_clear(void)224 void giterr_clear(void)
225 {
226 	git_error_clear();
227 }
228 
giterr_set_str(int error_class,const char * string)229 void giterr_set_str(int error_class, const char *string)
230 {
231 	git_error_set_str(error_class, string);
232 }
233 
giterr_set_oom(void)234 void giterr_set_oom(void)
235 {
236 	git_error_set_oom();
237 }
238 #endif
239