| /*	$NetBSD: error.c,v 1.31 2003/08/07 09:05:30 agc Exp $	*/ | 
 |  | 
 | /*- | 
 |  * Copyright (c) 1991, 1993 | 
 |  *	The Regents of the University of California.  All rights reserved. | 
 |  * | 
 |  * This code is derived from software contributed to Berkeley by | 
 |  * Kenneth Almquist. | 
 |  * | 
 |  * Redistribution and use in source and binary forms, with or without | 
 |  * modification, are permitted provided that the following conditions | 
 |  * are met: | 
 |  * 1. Redistributions of source code must retain the above copyright | 
 |  *    notice, this list of conditions and the following disclaimer. | 
 |  * 2. Redistributions in binary form must reproduce the above copyright | 
 |  *    notice, this list of conditions and the following disclaimer in the | 
 |  *    documentation and/or other materials provided with the distribution. | 
 |  * 3. Neither the name of the University nor the names of its contributors | 
 |  *    may be used to endorse or promote products derived from this software | 
 |  *    without specific prior written permission. | 
 |  * | 
 |  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | 
 |  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
 |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 
 |  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | 
 |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
 |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | 
 |  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 
 |  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 
 |  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | 
 |  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 
 |  * SUCH DAMAGE. | 
 |  */ | 
 |  | 
 | #include <sys/cdefs.h> | 
 | #ifndef lint | 
 | #if 0 | 
 | static char sccsid[] = "@(#)error.c	8.2 (Berkeley) 5/4/95"; | 
 | #else | 
 | __RCSID("$NetBSD: error.c,v 1.31 2003/08/07 09:05:30 agc Exp $"); | 
 | #endif | 
 | #endif /* not lint */ | 
 |  | 
 | /* | 
 |  * Errors and exceptions. | 
 |  */ | 
 |  | 
 | #include <signal.h> | 
 | #include <stdlib.h> | 
 | #include <unistd.h> | 
 | #include <errno.h> | 
 | #include <stdio.h> | 
 | #include <string.h> | 
 |  | 
 | #include "shell.h" | 
 | #include "main.h" | 
 | #include "options.h" | 
 | #include "output.h" | 
 | #include "error.h" | 
 | #include "show.h" | 
 |  | 
 | #define signal bsd_signal | 
 | /* | 
 |  * Code to handle exceptions in C. | 
 |  */ | 
 |  | 
 | struct jmploc *handler; | 
 | int exception; | 
 | volatile int suppressint; | 
 | volatile int intpending; | 
 | char *commandname; | 
 |  | 
 |  | 
 | static void exverror(int, const char *, va_list) | 
 |     __attribute__((__noreturn__)); | 
 |  | 
 | /* | 
 |  * Called to raise an exception.  Since C doesn't include exceptions, we | 
 |  * just do a longjmp to the exception handler.  The type of exception is | 
 |  * stored in the global variable "exception". | 
 |  */ | 
 |  | 
 | void | 
 | exraise(int e) | 
 | { | 
 | 	if (handler == NULL) | 
 | 		abort(); | 
 | 	exception = e; | 
 | 	longjmp(handler->loc, 1); | 
 | } | 
 |  | 
 |  | 
 | /* | 
 |  * Called from trap.c when a SIGINT is received.  (If the user specifies | 
 |  * that SIGINT is to be trapped or ignored using the trap builtin, then | 
 |  * this routine is not called.)  Suppressint is nonzero when interrupts | 
 |  * are held using the INTOFF macro.  The call to _exit is necessary because | 
 |  * there is a short period after a fork before the signal handlers are | 
 |  * set to the appropriate value for the child.  (The test for iflag is | 
 |  * just defensive programming.) | 
 |  */ | 
 |  | 
 | void | 
 | onint(void) | 
 | { | 
 | 	sigset_t nsigset; | 
 |  | 
 | 	if (suppressint) { | 
 | 		intpending = 1; | 
 | 		return; | 
 | 	} | 
 | 	intpending = 0; | 
 | 	sigemptyset(&nsigset); | 
 | 	sigprocmask(SIG_SETMASK, &nsigset, NULL); | 
 | 	if (rootshell && iflag) | 
 | 		exraise(EXINT); | 
 | 	else { | 
 | 		signal(SIGINT, SIG_DFL); | 
 | 		raise(SIGINT); | 
 | 	} | 
 | 	/* NOTREACHED */ | 
 | } | 
 |  | 
 | static void | 
 | exvwarning(int sv_errno, const char *msg, va_list ap) | 
 | { | 
 | 	/* Partially emulate line buffered output so that: | 
 | 	 *	printf '%d\n' 1 a 2 | 
 | 	 * and | 
 | 	 *	printf '%d %d %d\n' 1 a 2 | 
 | 	 * both generate sensible text when stdout and stderr are merged. | 
 | 	 */ | 
 | 	if (output.nextc != output.buf && output.nextc[-1] == '\n') | 
 | 		flushout(&output); | 
 | 	if (commandname) | 
 | 		outfmt(&errout, "%s: ", commandname); | 
 | 	if (msg != NULL) { | 
 | 		doformat(&errout, msg, ap); | 
 | 		if (sv_errno >= 0) | 
 | 			outfmt(&errout, ": "); | 
 | 	} | 
 | 	if (sv_errno >= 0) | 
 | 		outfmt(&errout, "%s", strerror(sv_errno)); | 
 | 	out2c('\n'); | 
 | 	flushout(&errout); | 
 | } | 
 |  | 
 | /* | 
 |  * Exverror is called to raise the error exception.  If the second argument | 
 |  * is not NULL then error prints an error message using printf style | 
 |  * formatting.  It then raises the error exception. | 
 |  */ | 
 | static void | 
 | exverror(int cond, const char *msg, va_list ap) | 
 | { | 
 | 	CLEAR_PENDING_INT; | 
 | 	INTOFF; | 
 |  | 
 | #ifdef DEBUG | 
 | 	if (msg) { | 
 | 		TRACE(("exverror(%d, \"", cond)); | 
 | 		TRACEV((msg, ap)); | 
 | 		TRACE(("\") pid=%d\n", getpid())); | 
 | 	} else | 
 | 		TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid())); | 
 | #endif | 
 | 	if (msg) | 
 | 		exvwarning(-1, msg, ap); | 
 |  | 
 | 	flushall(); | 
 | 	exraise(cond); | 
 | 	/* NOTREACHED */ | 
 | } | 
 |  | 
 |  | 
 | void | 
 | error(const char *msg, ...) | 
 | { | 
 | 	va_list ap; | 
 |  | 
 | 	va_start(ap, msg); | 
 | 	exverror(EXERROR, msg, ap); | 
 | 	/* NOTREACHED */ | 
 | 	va_end(ap); | 
 | } | 
 |  | 
 |  | 
 | void | 
 | exerror(int cond, const char *msg, ...) | 
 | { | 
 | 	va_list ap; | 
 |  | 
 | 	va_start(ap, msg); | 
 | 	exverror(cond, msg, ap); | 
 | 	/* NOTREACHED */ | 
 | 	va_end(ap); | 
 | } | 
 |  | 
 | /* | 
 |  * error/warning routines for external builtins | 
 |  */ | 
 |  | 
 | void | 
 | sh_exit(int rval) | 
 | { | 
 | 	exerrno = rval & 255; | 
 | 	exraise(EXEXEC); | 
 | } | 
 |  | 
 | void | 
 | sh_err(int status, const char *fmt, ...) | 
 | { | 
 | 	va_list ap; | 
 |  | 
 | 	va_start(ap, fmt); | 
 | 	exvwarning(errno, fmt, ap); | 
 | 	va_end(ap); | 
 | 	sh_exit(status); | 
 | } | 
 |  | 
 | void | 
 | sh_verr(int status, const char *fmt, va_list ap) | 
 | { | 
 | 	exvwarning(errno, fmt, ap); | 
 | 	sh_exit(status); | 
 | } | 
 |  | 
 | void | 
 | sh_errx(int status, const char *fmt, ...) | 
 | { | 
 | 	va_list ap; | 
 |  | 
 | 	va_start(ap, fmt); | 
 | 	exvwarning(-1, fmt, ap); | 
 | 	va_end(ap); | 
 | 	sh_exit(status); | 
 | } | 
 |  | 
 | void | 
 | sh_verrx(int status, const char *fmt, va_list ap) | 
 | { | 
 | 	exvwarning(-1, fmt, ap); | 
 | 	sh_exit(status); | 
 | } | 
 |  | 
 | void | 
 | sh_warn(const char *fmt, ...) | 
 | { | 
 | 	va_list ap; | 
 |  | 
 | 	va_start(ap, fmt); | 
 | 	exvwarning(errno, fmt, ap); | 
 | 	va_end(ap); | 
 | } | 
 |  | 
 | void | 
 | sh_vwarn(const char *fmt, va_list ap) | 
 | { | 
 | 	exvwarning(errno, fmt, ap); | 
 | } | 
 |  | 
 | void | 
 | sh_warnx(const char *fmt, ...) | 
 | { | 
 | 	va_list ap; | 
 |  | 
 | 	va_start(ap, fmt); | 
 | 	exvwarning(-1, fmt, ap); | 
 | 	va_end(ap); | 
 | } | 
 |  | 
 | void | 
 | sh_vwarnx(const char *fmt, va_list ap) | 
 | { | 
 | 	exvwarning(-1, fmt, ap); | 
 | } | 
 |  | 
 |  | 
 | /* | 
 |  * Table of error messages. | 
 |  */ | 
 |  | 
 | struct errname { | 
 | 	short errcode;		/* error number */ | 
 | 	short action;		/* operation which encountered the error */ | 
 | 	const char *msg;	/* text describing the error */ | 
 | }; | 
 |  | 
 |  | 
 | #define ALL (E_OPEN|E_CREAT|E_EXEC) | 
 |  | 
 | STATIC const struct errname errormsg[] = { | 
 | 	{ EINTR,	ALL,	"interrupted" }, | 
 | 	{ EACCES,	ALL,	"permission denied" }, | 
 | 	{ EIO,		ALL,	"I/O error" }, | 
 | 	{ EEXIST,	ALL,	"file exists" }, | 
 | 	{ ENOENT,	E_OPEN,	"no such file" }, | 
 | 	{ ENOENT,	E_CREAT,"directory nonexistent" }, | 
 | 	{ ENOENT,	E_EXEC,	"not found" }, | 
 | 	{ ENOTDIR,	E_OPEN,	"no such file" }, | 
 | 	{ ENOTDIR,	E_CREAT,"directory nonexistent" }, | 
 | 	{ ENOTDIR,	E_EXEC,	"not found" }, | 
 | 	{ EISDIR,	ALL,	"is a directory" }, | 
 | #ifdef EMFILE | 
 | 	{ EMFILE,	ALL,	"too many open files" }, | 
 | #endif | 
 | 	{ ENFILE,	ALL,	"file table overflow" }, | 
 | 	{ ENOSPC,	ALL,	"file system full" }, | 
 | #ifdef EDQUOT | 
 | 	{ EDQUOT,	ALL,	"disk quota exceeded" }, | 
 | #endif | 
 | #ifdef ENOSR | 
 | 	{ ENOSR,	ALL,	"no streams resources" }, | 
 | #endif | 
 | 	{ ENXIO,	ALL,	"no such device or address" }, | 
 | 	{ EROFS,	ALL,	"read-only file system" }, | 
 | 	{ ETXTBSY,	ALL,	"text busy" }, | 
 | #ifdef EAGAIN | 
 | 	{ EAGAIN,	E_EXEC,	"not enough memory" }, | 
 | #endif | 
 | 	{ ENOMEM,	ALL,	"not enough memory" }, | 
 | #ifdef ENOLINK | 
 | 	{ ENOLINK,	ALL,	"remote access failed" }, | 
 | #endif | 
 | #ifdef EMULTIHOP | 
 | 	{ EMULTIHOP,	ALL,	"remote access failed" }, | 
 | #endif | 
 | #ifdef ECOMM | 
 | 	{ ECOMM,	ALL,	"remote access failed" }, | 
 | #endif | 
 | #ifdef ESTALE | 
 | 	{ ESTALE,	ALL,	"remote access failed" }, | 
 | #endif | 
 | #ifdef ETIMEDOUT | 
 | 	{ ETIMEDOUT,	ALL,	"remote access failed" }, | 
 | #endif | 
 | #ifdef ELOOP | 
 | 	{ ELOOP,	ALL,	"symbolic link loop" }, | 
 | #endif | 
 | 	{ E2BIG,	E_EXEC,	"argument list too long" }, | 
 | #ifdef ELIBACC | 
 | 	{ ELIBACC,	E_EXEC,	"shared library missing" }, | 
 | #endif | 
 | 	{ 0,		0,	NULL }, | 
 | }; | 
 |  | 
 |  | 
 | /* | 
 |  * Return a string describing an error.  The returned string may be a | 
 |  * pointer to a static buffer that will be overwritten on the next call. | 
 |  * Action describes the operation that got the error. | 
 |  */ | 
 |  | 
 | const char * | 
 | errmsg(int e, int action) | 
 | { | 
 | 	struct errname const *ep; | 
 | 	static char buf[12]; | 
 |  | 
 | 	for (ep = errormsg ; ep->errcode ; ep++) { | 
 | 		if (ep->errcode == e && (ep->action & action) != 0) | 
 | 			return ep->msg; | 
 | 	} | 
 | 	fmtstr(buf, sizeof buf, "error %d", e); | 
 | 	return buf; | 
 | } |