1 /* error.c - Error-management and messaging routines.
2  *
3  * Copyright (C) 1998 Oskar Liljeblad
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #if HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22 #include <errno.h>	/* C89 */
23 #include <string.h>	/* Gnulib/C89 */
24 #include <stdarg.h>	/* Gnulib/C89 */
25 #include <stdlib.h>	/* Gnulib/C89 */
26 #include <stdio.h>	/* Gnulib/C89 */
27 #include "gettext.h"	/* Gnulib/Gettext */
28 #define _(s) gettext(s)
29 #include "xvasprintf.h"	/* Gnulib */
30 #include "xalloc.h"	/* Gnulib */
31 #include "progname.h"	/* Gnulib */
32 #include "error.h"
33 
34 struct MessageHeader {
35 	struct MessageHeader *old;
36 	char *message;
37 };
38 
39 void (*program_termination_hook)(void) = NULL;
40 static char *error_message = NULL;
41 static struct MessageHeader *message_header = NULL;
42 
43 static inline const char *
get_message_header(void)44 get_message_header(void)
45 {
46 	if (message_header != NULL)
47 		return message_header->message;
48 	return program_name;
49 }
50 
51 static void
v_warn(const char * msg,va_list ap)52 v_warn(const char *msg, va_list ap)
53 {
54 	fprintf(stderr, "%s: ", get_message_header());
55 	if (msg != NULL)
56 		vfprintf(stderr, msg, ap);
57 	fprintf(stderr, "\n");
58 }
59 
60 static void
v_warn_errno(const char * msg,va_list ap)61 v_warn_errno(const char *msg, va_list ap)
62 {
63 	fprintf(stderr, "%s: ", get_message_header());
64 	if (msg != NULL) {
65 		vfprintf(stderr, msg, ap);
66 		fprintf(stderr, ": ");
67 	}
68 	fprintf(stderr, "%s\n", strerror(errno));
69 }
70 
71 /**
72  * Free all global memory allocated by the error facilities
73  * provided here.
74  */
75 void
free_error(void)76 free_error(void)
77 {
78 	struct MessageHeader *hdr;
79 
80 	for (hdr = message_header; hdr != NULL; hdr = hdr->old) {
81 		free(hdr->message);
82 		free(hdr);
83 	}
84 	if (error_message != NULL)
85 		free(error_message);
86 }
87 
88 /**
89  * This function should be called when an internal error has
90  * occured. It will display a more verbose message, asking
91  * the user to mail the program author.
92  *
93  * @param msg
94  *   Error message.
95  */
96 void
internal_error(const char * msg,...)97 internal_error(const char *msg, ...)
98 {
99 	va_list ap;
100 
101 	va_start(ap, msg);
102 	if (program_termination_hook != NULL)
103 		program_termination_hook();
104 	fprintf(stderr, _("\
105 An internal error has occured. Please report this error by sending the\n\
106 output below to %s.\n\
107 \n\
108 Program: %s\n\
109 Version: %s\n\
110 Error: "), PACKAGE_BUGREPORT, program_name, VERSION);
111 	vfprintf(stderr, msg, ap);
112 	va_end(ap);
113 
114 	free_error();
115 	exit(1);
116 }
117 
118 /**
119  * Terminate the program with an error message.
120  *
121  * @param msg
122  *   Error message.
123  */
124 void
die(const char * msg,...)125 die(const char *msg, ...)
126 {
127 	va_list ap;
128 
129 	va_start(ap, msg);
130 	if (program_termination_hook != NULL)
131 		program_termination_hook();
132 	v_warn(msg, ap);
133 	va_end(ap);
134 
135 	free_error();
136 	exit(1);
137 }
138 
139 /**
140  * Terminate the program with an error message and
141  * the current error in `errno'.
142  *
143  * @param msg
144  *   Error message, or NULL if only the error in `errno'
145  *   should be printed.
146  */
147 void
die_errno(const char * msg,...)148 die_errno(const char *msg, ...)
149 {
150 	va_list ap;
151 
152 	va_start(ap, msg);
153 	if (program_termination_hook != NULL)
154 		program_termination_hook();
155 	v_warn_errno(msg, ap);
156 	va_end(ap);
157 
158 	free_error();
159 	exit(1);
160 }
161 
162 /**
163  * Write a warning message to standard error.
164  *
165  * @param msg
166  *   The message.
167  */
168 void
warn(const char * msg,...)169 warn(const char *msg, ...)
170 {
171 	va_list ap;
172 
173 	va_start(ap, msg);
174 	v_warn(msg, ap);
175 	va_end(ap);
176 }
177 
178 
179 /**
180  * Write an error message and the current error in `errno'.
181  *
182  * @param msg
183  *   Error message, or NULL if only the error in `errno'
184  *   should be printed.
185  */
186 void
warn_errno(const char * msg,...)187 warn_errno(const char *msg, ...)
188 {
189 	va_list ap;
190 
191 	va_start(ap, msg);
192 	v_warn_errno(msg, ap);
193 	va_end(ap);
194 }
195 
196 /**
197  * Set the current message header.
198  */
199 void
set_message_header(const char * msg,...)200 set_message_header(const char *msg, ...)
201 {
202 	va_list ap;
203 	struct MessageHeader *hdr;
204 
205 	hdr = malloc(sizeof(struct MessageHeader));
206 	if (hdr == NULL)
207 		xalloc_die();
208 	hdr->old = message_header;
209 	va_start(ap, msg);
210 	if (vasprintf(&hdr->message, msg, ap) < 0)
211 		xalloc_die();
212 	message_header = hdr;
213 	va_end(ap);
214 }
215 
216 /**
217  * Restore the message header to the default.
218  */
219 void
restore_message_header(void)220 restore_message_header(void)
221 {
222 	if (message_header != NULL) {
223 		struct MessageHeader *old;
224 		old = message_header->old;
225 		free(message_header->message);
226 		free(message_header);
227 		message_header = old;
228 	}
229 }
230 
231 /**
232  * Set a global error message.
233  */
234 void
set_error(const char * format,...)235 set_error(const char *format, ...)
236 {
237 	va_list ap;
238 
239 	if (error_message != NULL)
240 		free(error_message);
241 
242 	if (format != NULL) {
243 		va_start(ap, format);
244 		if (vasprintf(&error_message, format, ap) < 0)
245 			xalloc_die();
246 		va_end(ap);
247 	} else {
248 		error_message = NULL;
249 	}
250 }
251 
252 /**
253  * Return the global error message.
254  * The returned value cannot be modified and should never
255  * be freed.
256  */
257 const char *
get_error(void)258 get_error(void)
259 {
260 	return error_message;
261 }
262 
263 /**
264  * Remove and return the global error message.
265  * The returned value may be modified and should
266  * be freed when no longer needed.
267  */
268 char *
remove_error(void)269 remove_error(void)
270 {
271 	char *msg = error_message;
272 	error_message = NULL;
273 	return msg;
274 }
275 
276 /**
277  * Die printing the current error message.
278  */
279 void
die_error(void)280 die_error(void)
281 {
282 	if (program_termination_hook != NULL)
283 		program_termination_hook();
284 	warn(error_message);
285 	free_error();
286 	exit(1);
287 }
288