1 /* @(#)comerr.c 1.44 17/03/13 Copyright 1985-1989, 1995-2017 J. Schilling */
2 /*
3 * Routines for printing command errors
4 *
5 * Copyright (c) 1985-1989, 1995-2017 J. Schilling
6 */
7 /*
8 * The contents of this file are subject to the terms of the
9 * Common Development and Distribution License, Version 1.0 only
10 * (the "License"). You may not use this file except in compliance
11 * with the License.
12 *
13 * See the file CDDL.Schily.txt in this distribution for details.
14 * A copy of the CDDL is also available via the Internet at
15 * http://www.opensource.org/licenses/cddl1.txt
16 *
17 * When distributing Covered Code, include this CDDL HEADER in each
18 * file and include the License file CDDL.Schily.txt from this distribution.
19 */
20
21 #include <schily/mconfig.h>
22 #include <schily/unistd.h> /* include <sys/types.h> try to get size_t */
23 #include <schily/stdio.h> /* Try again for size_t */
24 #include <schily/stdlib.h> /* Try again for size_t */
25 #include <schily/standard.h>
26 #include <schily/varargs.h>
27 #include <schily/string.h>
28 #include <schily/schily.h>
29 #include <schily/errno.h>
30
31 EXPORT int on_comerr __PR((void (*fun)(int, void *), void *arg));
32 EXPORT void comerr __PR((const char *, ...));
33 EXPORT void xcomerr __PR((int, const char *, ...));
34 EXPORT void comerrno __PR((int, const char *, ...));
35 EXPORT void xcomerrno __PR((int, int, const char *, ...));
36 EXPORT int errmsg __PR((const char *, ...));
37 EXPORT int errmsgno __PR((int, const char *, ...));
38 EXPORT int _comerr __PR((FILE *, int, int, int,
39 const char *, va_list));
40 LOCAL int _ex_clash __PR((int));
41 EXPORT void comexit __PR((int));
42 EXPORT char *errmsgstr __PR((int));
43
44 typedef struct ex {
45 struct ex *next;
46 void (*func) __PR((int, void *));
47 void *arg;
48 } ex_t;
49
50 LOCAL ex_t *exfuncs;
51
52 /*
53 * Set list of callback functions to call with *comerr() and comexit().
54 * The function set up last with on_comerr() is called first on exit;
55 * in other words: call order is the reverse order of registration.
56 */
57 EXPORT int
58 on_comerr(func, arg)
59 void (*func) __PR((int, void *));
60 void *arg;
61 {
62 ex_t *fp;
63
64 fp = malloc(sizeof (*fp));
65 if (fp == NULL)
66 return (-1);
67
68 fp->func = func;
69 fp->arg = arg;
70 fp->next = exfuncs;
71 exfuncs = fp;
72 return (0);
73 }
74
75 /*
76 * Fetch current errno, print a related message and exit(errno).
77 */
78 /* VARARGS1 */
79 #ifdef PROTOTYPES
80 EXPORT void
comerr(const char * msg,...)81 comerr(const char *msg, ...)
82 #else
83 EXPORT void
84 comerr(msg, va_alist)
85 char *msg;
86 va_dcl
87 #endif
88 {
89 va_list args;
90
91 #ifdef PROTOTYPES
92 va_start(args, msg);
93 #else
94 va_start(args);
95 #endif
96 (void) _comerr(stderr, COMERR_EXIT, 0, geterrno(), msg, args);
97 /* NOTREACHED */
98 va_end(args);
99 }
100
101 /*
102 * Fetch current errno, print a related message and exit(exc).
103 */
104 /* VARARGS2 */
105 #ifdef PROTOTYPES
106 EXPORT void
xcomerr(int exc,const char * msg,...)107 xcomerr(int exc, const char *msg, ...)
108 #else
109 EXPORT void
110 xcomerr(exc, msg, va_alist)
111 int exc;
112 char *msg;
113 va_dcl
114 #endif
115 {
116 va_list args;
117
118 #ifdef PROTOTYPES
119 va_start(args, msg);
120 #else
121 va_start(args);
122 #endif
123 (void) _comerr(stderr, COMERR_EXCODE, exc, geterrno(), msg, args);
124 /* NOTREACHED */
125 va_end(args);
126 }
127
128 /*
129 * Print a message related to err and exit(err).
130 */
131 /* VARARGS2 */
132 #ifdef PROTOTYPES
133 EXPORT void
comerrno(int err,const char * msg,...)134 comerrno(int err, const char *msg, ...)
135 #else
136 EXPORT void
137 comerrno(err, msg, va_alist)
138 int err;
139 char *msg;
140 va_dcl
141 #endif
142 {
143 va_list args;
144
145 #ifdef PROTOTYPES
146 va_start(args, msg);
147 #else
148 va_start(args);
149 #endif
150 (void) _comerr(stderr, COMERR_EXIT, 0, err, msg, args);
151 /* NOTREACHED */
152 va_end(args);
153 }
154
155 /*
156 * Print a message related to err and exit(exc).
157 */
158 /* VARARGS3 */
159 #ifdef PROTOTYPES
160 EXPORT void
xcomerrno(int exc,int err,const char * msg,...)161 xcomerrno(int exc, int err, const char *msg, ...)
162 #else
163 EXPORT void
164 xcomerrno(exc, err, msg, va_alist)
165 int exc;
166 int err;
167 char *msg;
168 va_dcl
169 #endif
170 {
171 va_list args;
172
173 #ifdef PROTOTYPES
174 va_start(args, msg);
175 #else
176 va_start(args);
177 #endif
178 (void) _comerr(stderr, COMERR_EXCODE, exc, err, msg, args);
179 /* NOTREACHED */
180 va_end(args);
181 }
182
183 /*
184 * Fetch current errno, print a related message and return(errno).
185 */
186 /* VARARGS1 */
187 #ifdef PROTOTYPES
188 EXPORT int
errmsg(const char * msg,...)189 errmsg(const char *msg, ...)
190 #else
191 EXPORT int
192 errmsg(msg, va_alist)
193 char *msg;
194 va_dcl
195 #endif
196 {
197 va_list args;
198 int ret;
199
200 #ifdef PROTOTYPES
201 va_start(args, msg);
202 #else
203 va_start(args);
204 #endif
205 ret = _comerr(stderr, COMERR_RETURN, 0, geterrno(), msg, args);
206 va_end(args);
207 return (ret);
208 }
209
210 /*
211 * Print a message related to err and return(err).
212 */
213 /* VARARGS2 */
214 #ifdef PROTOTYPES
215 EXPORT int
errmsgno(int err,const char * msg,...)216 errmsgno(int err, const char *msg, ...)
217 #else
218 EXPORT int
219 errmsgno(err, msg, va_alist)
220 int err;
221 char *msg;
222 va_dcl
223 #endif
224 {
225 va_list args;
226 int ret;
227
228 #ifdef PROTOTYPES
229 va_start(args, msg);
230 #else
231 va_start(args);
232 #endif
233 ret = _comerr(stderr, COMERR_RETURN, 0, err, msg, args);
234 va_end(args);
235 return (ret);
236 }
237
238 #if defined(__BEOS__) || defined(__HAIKU__)
239 /*
240 * On BeOS errno is a big negative number (0x80000000 + small number).
241 * We assume that small negative numbers are safe to be used as special
242 * values that prevent printing the errno text.
243 *
244 * We tried to use #if EIO < 0 but this does not work because EIO is
245 * defined to a enum. ENODEV may work as ENODEV is defined to a number
246 * directly.
247 */
248 #define silent_error(e) ((e) < 0 && (e) >= -1024)
249 #else
250 /*
251 * On UNIX errno is a small non-negative number, so we assume that
252 * negative values cannot be a valid errno and don't print the error
253 * string in this case. However the value may still be used as exit()
254 * code if 'exflg' is set.
255 */
256 #define silent_error(e) ((e) < 0)
257 #endif
258 EXPORT int
_comerr(f,exflg,exc,err,msg,args)259 _comerr(f, exflg, exc, err, msg, args)
260 FILE *f; /* FILE * to print to */
261 int exflg; /* COMERR_RETURN, COMERR_EXIT, COMERR_EXCODE */
262 int exc; /* Use for exit() if exflg & COMERR_EXCODE */
263 int err; /* Errno for text, exit(err) if !COMERR_EXIT*/
264 const char *msg; /* printf() format */
265 va_list args; /* printf() args for format */
266 {
267 char errbuf[20];
268 char *errnam;
269 char *prognam = get_progname();
270
271 if (silent_error(err)) {
272 js_fprintf(f, "%s: %r", prognam, msg, args);
273 } else {
274 errnam = errmsgstr(err);
275 if (errnam == NULL) {
276 (void) js_snprintf(errbuf, sizeof (errbuf),
277 "Error %d", err);
278 errnam = errbuf;
279 }
280 js_fprintf(f, "%s: %s. %r", prognam, errnam, msg, args);
281 }
282 if (exflg) {
283 if (exflg & COMERR_EXCODE)
284 err = exc;
285 else
286 err = _ex_clash(err);
287 comexit(err);
288 /* NOTREACHED */
289 }
290 return (err);
291 }
292
293 LOCAL int
_ex_clash(exc)294 _ex_clash(exc)
295 int exc;
296 {
297 int exmod = exc % 256;
298
299 /*
300 * On a recent POSIX System that supports waitid(), siginfo.si_status
301 * holds the exit(2) value as an int. So if waitid() is used to wait
302 * for the process, we do not have problems from folded exit codes.
303 * All other wait*() functions fold the exit code by masking it
304 * with 0377.
305 *
306 * Exit codes used with comerr*()/comexit() are frequently errno values
307 * that have been in the range 0..31 with UNIX.V5 in the mid 1970s and
308 * that now are in the range 0..151 on Solaris. These values do not
309 * cause problems from folding to 8 bits, but "sysexits.h" contains
310 * definitions in the range 64..79 that cause (even unfolded) clashes
311 * with errno values.
312 *
313 * To avoid clashes with errno values, "schily/standard.h" defines
314 * EX_BAD (-1) as default error exit code and
315 * EX_CLASH (-64) as marker for clashes.
316 * Exit codes in the range -2..-63 (254..193 seen as unsigned two's
317 * complement) are available as software specific exit codes.
318 * We map all other negative exit codes to EX_CLASH if they would fold
319 * to -2..-63.
320 */
321 if (exc != exmod && exmod <= 0 && exmod >= EX_CLASH)
322 exc = EX_CLASH;
323 return (exc);
324 }
325
326 /*
327 * Do a program exit() with previously calling functions registered via
328 * on_comerr().
329 */
330 EXPORT void
comexit(err)331 comexit(err)
332 int err;
333 {
334 while (exfuncs) {
335 ex_t *fp;
336
337 (*exfuncs->func)(err, exfuncs->arg);
338 fp = exfuncs;
339 exfuncs = exfuncs->next;
340 free(fp);
341 }
342 exit(err);
343 /* NOTREACHED */
344 }
345
346 /*
347 * Wrapper around the strange POSIX strerror().
348 * If there is a problem with retrieving the correct error text,
349 * return NULL.
350 */
351 EXPORT char *
errmsgstr(err)352 errmsgstr(err)
353 int err;
354 {
355 #ifdef HAVE_STRERROR
356 /*
357 * POSIX compliance may look strange...
358 */
359 int errsav = geterrno();
360 char *ret;
361
362 seterrno(0);
363 ret = strerror(err);
364 err = geterrno();
365 seterrno(errsav);
366
367 if (ret == NULL || err)
368 return (NULL);
369 return (ret);
370 #else
371 if (err < 0 || err >= sys_nerr) {
372 return (NULL);
373 } else {
374 return (sys_errlist[err]);
375 }
376 #endif
377 }
378