/* vi: set sw=4 ts=4: */
/*
 * Signal name/number conversion routines.
 *
 * Copyright 2006 Rob Landley <rob@landley.net>
 *
 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
 */
//config:config FEATURE_RTMINMAX
//config:	bool "Support RTMIN[+n] and RTMAX[-n] signal names"
//config:	default y
//config:	help
//config:	Support RTMIN[+n] and RTMAX[-n] signal names
//config:	in kill, killall etc. This costs ~250 bytes.
//config:
//config:config FEATURE_RTMINMAX_USE_LIBC_DEFINITIONS
//config:	bool "Use the definitions of SIGRTMIN/SIGRTMAX provided by libc"
//config:	default y
//config:	depends on FEATURE_RTMINMAX
//config:	help
//config:	Some C libraries reserve a few real-time signals for internal
//config:	use, and adjust the values of SIGRTMIN/SIGRTMAX seen by
//config:	applications accordingly. Saying yes here means that a signal
//config:	name RTMIN+n will be interpreted according to the libc definition
//config:	of SIGRTMIN, and not the raw definition provided by the kernel.
//config:	This behavior matches "kill -l RTMIN+n" from bash.

#include "libbb.h"

/* Believe it or not, but some arches have more than 32 SIGs!
 * HPPA: SIGSTKFLT == 36. */

static const char signals[][7] ALIGN1 = {
	// SUSv3 says kill must support these, and specifies the numerical values,
	// http://www.opengroup.org/onlinepubs/009695399/utilities/kill.html
	// {0, "EXIT"}, {1, "HUP"}, {2, "INT"}, {3, "QUIT"},
	// {6, "ABRT"}, {9, "KILL"}, {14, "ALRM"}, {15, "TERM"}
	// And Posix adds the following:
	// {SIGILL, "ILL"}, {SIGTRAP, "TRAP"}, {SIGFPE, "FPE"}, {SIGUSR1, "USR1"},
	// {SIGSEGV, "SEGV"}, {SIGUSR2, "USR2"}, {SIGPIPE, "PIPE"}, {SIGCHLD, "CHLD"},
	// {SIGCONT, "CONT"}, {SIGSTOP, "STOP"}, {SIGTSTP, "TSTP"}, {SIGTTIN, "TTIN"},
	// {SIGTTOU, "TTOU"}

	[0] = "EXIT",
#ifdef SIGHUP
	[SIGHUP   ] = "HUP",
#endif
#ifdef SIGINT
	[SIGINT   ] = "INT",
#endif
#ifdef SIGQUIT
	[SIGQUIT  ] = "QUIT",
#endif
#ifdef SIGILL
	[SIGILL   ] = "ILL",
#endif
#ifdef SIGTRAP
	[SIGTRAP  ] = "TRAP",
#endif
#ifdef SIGABRT
	[SIGABRT  ] = "ABRT",
#endif
#ifdef SIGBUS
	[SIGBUS   ] = "BUS",
#endif
#ifdef SIGFPE
	[SIGFPE   ] = "FPE",
#endif
#ifdef SIGKILL
	[SIGKILL  ] = "KILL",
#endif
#ifdef SIGUSR1
	[SIGUSR1  ] = "USR1",
#endif
#ifdef SIGSEGV
	[SIGSEGV  ] = "SEGV",
#endif
#ifdef SIGUSR2
	[SIGUSR2  ] = "USR2",
#endif
#ifdef SIGPIPE
	[SIGPIPE  ] = "PIPE",
#endif
#ifdef SIGALRM
	[SIGALRM  ] = "ALRM",
#endif
#ifdef SIGTERM
	[SIGTERM  ] = "TERM",
#endif
#ifdef SIGSTKFLT
	[SIGSTKFLT] = "STKFLT",
#endif
#ifdef SIGCHLD
	[SIGCHLD  ] = "CHLD",
#endif
#ifdef SIGCONT
	[SIGCONT  ] = "CONT",
#endif
#ifdef SIGSTOP
	[SIGSTOP  ] = "STOP",
#endif
#ifdef SIGTSTP
	[SIGTSTP  ] = "TSTP",
#endif
#ifdef SIGTTIN
	[SIGTTIN  ] = "TTIN",
#endif
#ifdef SIGTTOU
	[SIGTTOU  ] = "TTOU",
#endif
#ifdef SIGURG
	[SIGURG   ] = "URG",
#endif
#ifdef SIGXCPU
	[SIGXCPU  ] = "XCPU",
#endif
#ifdef SIGXFSZ
	[SIGXFSZ  ] = "XFSZ",
#endif
#ifdef SIGVTALRM
	[SIGVTALRM] = "VTALRM",
#endif
#ifdef SIGPROF
	[SIGPROF  ] = "PROF",
#endif
#ifdef SIGWINCH
	[SIGWINCH ] = "WINCH",
#endif
#ifdef SIGPOLL
	[SIGPOLL  ] = "POLL",
#endif
#ifdef SIGPWR
	[SIGPWR   ] = "PWR",
#endif
#ifdef SIGSYS
	[SIGSYS   ] = "SYS",
#endif
#if ENABLE_FEATURE_RTMINMAX && !ENABLE_FEATURE_RTMINMAX_USE_LIBC_DEFINITIONS
# ifdef __SIGRTMIN
	[__SIGRTMIN] = "RTMIN",
# endif
// This makes array about x2 bigger.
// More compact approach is to special-case SIGRTMAX in print_signames()
//# ifdef __SIGRTMAX
//	[__SIGRTMAX] = "RTMAX",
//# endif
#endif
};

// Convert signal name to number.

int FAST_FUNC get_signum(const char *name)
{
	unsigned i;

	/* bb_strtou returns UINT_MAX on error. NSIG is smaller
	 * than UINT_MAX on any sane Unix. Hence no need
	 * to check errno after bb_strtou().
	 */
	i = bb_strtou(name, NULL, 10);
	if (i < NSIG) /* for shells, we allow 0 too */
		return i;
	if (strncasecmp(name, "SIG", 3) == 0)
		name += 3;
	for (i = 0; i < ARRAY_SIZE(signals); i++)
		if (strcasecmp(name, signals[i]) == 0)
			return i;

#if ENABLE_DESKTOP
# if defined(SIGIOT) || defined(SIGIO)
	/* SIGIO[T] are aliased to other names,
	 * thus cannot be stored in the signals[] array.
	 * Need special code to recognize them */
	if ((name[0] | 0x20) == 'i' && (name[1] | 0x20) == 'o') {
#  ifdef SIGIO
		if (!name[2])
			return SIGIO;
#  endif
#  ifdef SIGIOT
		if ((name[2] | 0x20) == 't' && !name[3])
			return SIGIOT;
#  endif
	}
# endif
#endif

#if ENABLE_FEATURE_RTMINMAX && defined(SIGRTMIN) && defined(SIGRTMAX)
	{
# if ENABLE_FEATURE_RTMINMAX_USE_LIBC_DEFINITIONS
		/* Use the libc provided values. */
		unsigned sigrtmin = SIGRTMIN;
		unsigned sigrtmax = SIGRTMAX;
# else
	/* Use the "raw" SIGRTMIN/MAX. Underscored names, if exist, provide
	 * them. If they don't exist, fall back to non-underscored ones: */
#  if !defined(__SIGRTMIN)
#   define __SIGRTMIN SIGRTMIN
#  endif
#  if !defined(__SIGRTMAX)
#   define __SIGRTMAX SIGRTMAX
#  endif

#  define sigrtmin __SIGRTMIN
#  define sigrtmax __SIGRTMAX
# endif
		if (strncasecmp(name, "RTMIN", 5) == 0) {
			if (!name[5])
				return sigrtmin;
			if (name[5] == '+') {
				i = bb_strtou(name + 6, NULL, 10);
				if (i <= sigrtmax - sigrtmin)
					return sigrtmin + i;
			}
		}
		else if (strncasecmp(name, "RTMAX", 5) == 0) {
			if (!name[5])
				return sigrtmax;
			if (name[5] == '-') {
				i = bb_strtou(name + 6, NULL, 10);
				if (i <= sigrtmax - sigrtmin)
					return sigrtmax - i;
			}
		}
# undef sigrtmin
# undef sigrtmax
	}
#endif

	return -1;
}

// Convert signal number to name

const char* FAST_FUNC get_signame(int number)
{
	if ((unsigned)number < ARRAY_SIZE(signals)) {
		if (signals[number][0]) /* if it's not an empty str */
			return signals[number];
	}

	return itoa(number);
}


// Print the whole signal list

void FAST_FUNC print_signames(void)
{
	unsigned signo;

	for (signo = 1; signo < ARRAY_SIZE(signals); signo++) {
		const char *name = signals[signo];
		if (name[0])
			printf("%2u) %s\n", signo, name);
	}
#if ENABLE_FEATURE_RTMINMAX
# if ENABLE_FEATURE_RTMINMAX_USE_LIBC_DEFINITIONS
#  if defined(SIGRTMIN) && defined(SIGRTMAX)
	printf("%2u) %s\n", SIGRTMIN, "RTMIN");
	printf("%2u) %s\n", SIGRTMAX, "RTMAX");
#  endif
# else
// __SIGRTMIN is included in signals[] array.
#  ifdef __SIGRTMAX
	printf("%2u) %s\n", __SIGRTMAX, "RTMAX");
#  endif
# endif
#endif
}