1/*-
2 * Copyright (c) 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 4. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#if defined(LIBC_SCCS) && !defined(lint)
31static char sccsid[] = "@(#)err.c	8.1 (Berkeley) 6/4/93";
32#endif /* LIBC_SCCS and not lint */
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD: src/lib/libc/gen/err.c,v 1.15 2008/04/03 20:36:44 imp Exp $");
35
36#include "namespace.h"
37#include <err.h>
38#include <errno.h>
39#include <stdarg.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <vis.h>
44#include "un-namespace.h"
45
46#ifdef __BLOCKS__
47#include <Block.h>
48#endif /* __BLOCKS__ */
49#include "libc_private.h"
50
51#define ERR_EXIT_UNDEF	0
52#ifdef __BLOCKS__
53#define ERR_EXIT_BLOCK	1
54#endif /* __BLOCKS__ */
55#define ERR_EXIT_FUNC	2
56struct _e_err_exit {
57	unsigned int type;
58#ifdef __BLOCKS__
59	union {
60#endif /* __BLOCKS__ */
61		void (*func)(int);
62#ifdef __BLOCKS__
63		void (^block)(int);
64	};
65#endif /* __BLOCKS__ */
66};
67
68#ifdef BUILDING_VARIANT
69
70__private_extern__ FILE *_e_err_file; /* file to use for error output */
71__private_extern__ struct _e_err_exit _e_err_exit;
72__private_extern__ void _e_visprintf(FILE * __restrict, const char * __restrict, va_list);
73
74#else /* !BUILDING_VARIANT */
75
76__private_extern__ FILE *_e_err_file = NULL; /* file to use for error output */
77__private_extern__ struct _e_err_exit _e_err_exit = {ERR_EXIT_UNDEF};
78
79/*
80 * zero means pass as is
81 * 255 means use \nnn (octal)
82 * otherwise use \x (x is value)
83 * (NUL isn't used)
84 */
85static const unsigned char escape[256] = {
86     /* NUL */
87	 0, /* Unused: strings can't contain nulls */
88     /*      SOH  STX  ETX  EOT  ENQ  ACK  BEL */
89	     255, 255, 255, 255, 255, 255, 'a',
90     /* BS   HT   NL   VT   NP   CR   SO   SI  */
91	'b',  0,   0,  'v', 'f', 'r', 255, 255,
92     /* DLE  DC1  DC2  DC3  DC4  NAK  SYN  ETB */
93	255, 255, 255, 255, 255, 255, 255, 255,
94     /* CAN  EM   SUB  ESC  FS   GS   RS   US  */
95	255, 255, 255, 255, 255, 255, 255, 255,
96     /* the rest are zero */
97};
98
99/*
100 * Make characters visible.  If we can't allocate enough
101 * memory, we fall back on vfprintf().
102 */
103__private_extern__ void
104_e_visprintf(FILE * __restrict stream, const char * __restrict format, va_list ap)
105{
106	int failed = 0;
107	char *str, *visstr;
108	va_list backup;
109
110	va_copy(backup, ap);
111	vasprintf(&str, format, ap);
112	if (str != NULL) {
113		if ((visstr = malloc(4 * strlen(str) + 1)) != NULL) {
114			unsigned char *fp = (unsigned char *)str;
115			unsigned char *tp = (unsigned char *)visstr;
116			while(*fp) {
117				switch(escape[*fp]) {
118				case 0:
119					*tp++ = *fp;
120					break;
121				case 255:
122					sprintf((char *)tp, "\\%03o", *fp);
123					tp += 4;
124					break;
125				default:
126					*tp++ = '\\';
127					*tp++ = escape[*fp];
128					break;
129				}
130				fp++;
131			}
132			*tp = 0;
133			fputs(visstr, stream);
134			free(visstr);
135		} else
136			failed = 1;
137		free(str);
138	} else
139		failed = 1;
140	if (failed)
141		vfprintf(stream, format, backup);
142	va_end(backup);
143}
144
145/*
146 * This is declared to take a `void *' so that the caller is not required
147 * to include <stdio.h> first.  However, it is really a `FILE *', and the
148 * manual page documents it as such.
149 */
150void
151err_set_file(void *fp)
152{
153	if (fp)
154		_e_err_file = fp;
155	else
156		_e_err_file = stderr;
157}
158
159void
160err_set_exit(void (*ef)(int))
161{
162	_e_err_exit.type = ERR_EXIT_FUNC;
163	_e_err_exit.func = ef;
164}
165
166#ifdef __BLOCKS__
167void
168err_set_exit_b(void (^ef)(int))
169{
170	_e_err_exit.type = ERR_EXIT_BLOCK;
171	_e_err_exit.block = Block_copy(ef);
172}
173#endif /* __BLOCKS__ */
174#endif /* !BUILDING_VARIANT */
175
176__weak_reference(_err, err);
177
178void
179_err(int eval, const char *fmt, ...)
180{
181	va_list ap;
182	va_start(ap, fmt);
183	verrc(eval, errno, fmt, ap);
184	va_end(ap);
185}
186
187void
188verr(eval, fmt, ap)
189	int eval;
190	const char *fmt;
191	va_list ap;
192{
193	verrc(eval, errno, fmt, ap);
194}
195
196void
197errc(int eval, int code, const char *fmt, ...)
198{
199	va_list ap;
200	va_start(ap, fmt);
201	verrc(eval, code, fmt, ap);
202	va_end(ap);
203}
204
205void
206verrc(int eval, int code, const char *fmt, va_list ap)
207{
208	if (_e_err_file == 0)
209		err_set_file((FILE *)0);
210	fprintf(_e_err_file, "%s: ", _getprogname());
211	if (fmt != NULL) {
212		_e_visprintf(_e_err_file, fmt, ap);
213		fprintf(_e_err_file, ": ");
214	}
215	fprintf(_e_err_file, "%s\n", strerror(code));
216	if (_e_err_exit.type) {
217#ifdef __BLOCKS__
218		if (_e_err_exit.type == ERR_EXIT_BLOCK) {
219			_e_err_exit.block(eval);
220		} else {
221			_e_err_exit.func(eval);
222		}
223#else
224		_e_err_exit.func(eval);
225#endif /* __BLOCKS__ */
226	}
227	exit(eval);
228}
229
230void
231errx(int eval, const char *fmt, ...)
232{
233	va_list ap;
234	va_start(ap, fmt);
235	verrx(eval, fmt, ap);
236	va_end(ap);
237}
238
239void
240verrx(int eval, const char *fmt, va_list ap)
241{
242	if (_e_err_file == 0)
243		err_set_file((FILE *)0);
244	fprintf(_e_err_file, "%s: ", _getprogname());
245	if (fmt != NULL)
246		_e_visprintf(_e_err_file, fmt, ap);
247	fprintf(_e_err_file, "\n");
248	if (_e_err_exit.type) {
249#ifdef __BLOCKS__
250		if (_e_err_exit.type == ERR_EXIT_BLOCK) {
251			_e_err_exit.block(eval);
252		} else {
253			_e_err_exit.func(eval);
254		}
255#else
256		_e_err_exit.func(eval);
257#endif /* __BLOCKS__ */
258	}
259	exit(eval);
260}
261
262__weak_reference(_warn, warn);
263
264void
265_warn(const char *fmt, ...)
266{
267	va_list ap;
268	va_start(ap, fmt);
269	vwarnc(errno, fmt, ap);
270	va_end(ap);
271}
272
273void
274vwarn(const char *fmt, va_list ap)
275{
276	vwarnc(errno, fmt, ap);
277}
278
279void
280warnc(int code, const char *fmt, ...)
281{
282	va_list ap;
283	va_start(ap, fmt);
284	vwarnc(code, fmt, ap);
285	va_end(ap);
286}
287
288void
289vwarnc(int code, const char *fmt, va_list ap)
290{
291	if (_e_err_file == 0)
292		err_set_file((FILE *)0);
293	fprintf(_e_err_file, "%s: ", _getprogname());
294	if (fmt != NULL) {
295		_e_visprintf(_e_err_file, fmt, ap);
296		fprintf(_e_err_file, ": ");
297	}
298	fprintf(_e_err_file, "%s\n", strerror(code));
299}
300
301void
302warnx(const char *fmt, ...)
303{
304	va_list ap;
305	va_start(ap, fmt);
306	vwarnx(fmt, ap);
307	va_end(ap);
308}
309
310void
311vwarnx(const char *fmt, va_list ap)
312{
313	if (_e_err_file == 0)
314		err_set_file((FILE *)0);
315	fprintf(_e_err_file, "%s: ", _getprogname());
316	if (fmt != NULL)
317		_e_visprintf(_e_err_file, fmt, ap);
318	fprintf(_e_err_file, "\n");
319}
320