1 /* @(#)comerr.c 1.47 19/09/27 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 char *p;
299
300 /*
301 * On a recent POSIX System that supports waitid(), siginfo.si_status
302 * holds the exit(2) value as an int. So if waitid() is used to wait
303 * for the process, we do not have problems from folded exit codes.
304 * All other wait*() functions fold the exit code by masking it
305 * with 0377.
306 *
307 * Exit codes used with comerr*()/comexit() are frequently errno values
308 * that have been in the range 0..31 with UNIX.V5 in the mid 1970s and
309 * that now are in the range 0..151 on Solaris. These values do not
310 * cause problems from folding to 8 bits, but "sysexits.h" contains
311 * definitions in the range 64..79 that cause (even unfolded) clashes
312 * with errno values.
313 *
314 * To avoid clashes with errno values, "schily/standard.h" defines
315 * EX_BAD (-1) (255) as default error exit code and
316 * EX_CLASH (-64) (192) as marker for clashes.
317 * Exit codes in the range -2..-63 (254..193 seen as unsigned two's
318 * complement) are available as software specific exit codes.
319 * We map all other negative exit codes to EX_CLASH.
320 *
321 * Do not map exit codes in case that the "COMERR_EXCODE" environment
322 * is present.
323 */
324 if ((p = getenv("COMERR_EXCODE")) != NULL) {
325 if (*p == '0') {
326 /*
327 * Modern shells that use waitpid() still interpret
328 * excode % 256 == 0 as a zero exit code with respect
329 * to conditional execution for compatibility with
330 * historic shells. Use "export COMERR_EXCODE=0" to
331 * request support for this problem.
332 */
333 if (exmod == 0 && exc != 0)
334 return(EX_CLASH);
335 }
336 return (exc);
337 }
338
339 if ((exc != exmod) ||
340 (exmod < EX_CLASH) ||
341 ((exmod > 0) && (exmod & 0377) >= (EX_CLASH & 0377)))
342 exc = EX_CLASH;
343 return (exc);
344 }
345
346 /*
347 * Do a program exit() with previously calling functions registered via
348 * on_comerr().
349 */
350 EXPORT void
comexit(err)351 comexit(err)
352 int err;
353 {
354 while (exfuncs) {
355 ex_t *fp;
356
357 (*exfuncs->func)(err, exfuncs->arg);
358 fp = exfuncs;
359 exfuncs = exfuncs->next;
360 free(fp);
361 }
362 exit(err);
363 /* NOTREACHED */
364 }
365
366 /*
367 * Wrapper around the strange POSIX strerror().
368 * If there is a problem with retrieving the correct error text,
369 * return NULL.
370 */
371 EXPORT char *
errmsgstr(err)372 errmsgstr(err)
373 int err;
374 {
375 #ifdef HAVE_STRERROR
376 /*
377 * POSIX compliance may look strange...
378 */
379 int errsav = geterrno();
380 char *ret;
381
382 seterrno(0);
383 ret = strerror(err);
384 err = geterrno();
385 seterrno(errsav);
386
387 if (ret == NULL || err)
388 return (NULL);
389 return (ret);
390 #else
391 if (err < 0 || err >= sys_nerr) {
392 return (NULL);
393 } else {
394 return (sys_errlist[err]);
395 }
396 #endif
397 }
398