1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2024-05-05 23:26:38 +02:00

52594: support for POSIX real-time signals with kill and trap

Also add new -L option to kill for a more verbose listing of signals
This commit is contained in:
Oliver Kiddle 2024-02-28 00:21:11 +01:00
parent b68002d927
commit 5331ff11c6
14 changed files with 249 additions and 63 deletions

View File

@ -1,3 +1,12 @@
2024-02-28 Oliver Kiddle <opk@zsh.org>
* 52594: Completion/Zsh/Command/_kill, Doc/Zsh/builtins.yo,
Doc/Zsh/params.yo, Src/Modules/parameter.c, Src/builtin.c,
Src/exec.c, Src/hashtable.c, Src/init.c, Src/jobs.c, Src/params.c,
Src/signals.c, Src/signals.h, Src/signames2.awk: support for
POSIX real-time signals with kill and trap and add -L option to
kill for more verbose listing of signals
2024-02-24 Bart Schaefer <schaefer@zsh.org>
* 52597: Src/math.c: fix multibyte and metafied character counts

View File

@ -4,10 +4,11 @@ local curcontext="$curcontext" line state ret=1
typeset -A opt_args
_arguments -C \
'(-s -l 1)-n[specify signal number]:signal number' \
'(-l)-q[send the specified integer with the signal using sigqueue]:value' \
'(-n -l 1)-s[specify signal name]:signal:_signals -s' \
'(-n -s)-l[list signal names or numbers of specified signals]:*:signal:_signals' \
'(-s -l -L 1)-n[specify signal number]:signal number' \
'(-l -L)-q[send the specified integer with the signal using sigqueue]:value' \
'(-n -l -L 1)-s[specify signal name]:signal:_signals -s' \
'-l[list signal names or numbers of specified signals]:*:signal:_signals' \
'(- *)-L[list each signal and corresponding number]' \
'(-n -s -l)1::signal:_signals -p -s' \
'*:processes:->processes' && ret=0

View File

@ -1144,7 +1144,8 @@ cindex(killing jobs)
cindex(jobs, killing)
xitem(tt(kill) [ tt(-s) var(signal_name) | tt(-n) var(signal_number) | \
tt(-)var(sig) ] [ tt(-q) var(value) ] var(job) ...)
item(tt(kill) tt(-l) [ var(sig) ... ])(
xitem(tt(kill) tt(-l) [ var(sig) ... ])
item(tt(kill) tt(-L))(
Sends either tt(SIGTERM) or the specified signal to the given
jobs or processes.
Signals are given by number or by names, with or without the `tt(SIG)'
@ -1158,7 +1159,8 @@ specified the signal names are listed. Otherwise, for each
var(sig) that is a name, the corresponding signal number is
listed. For each var(sig) that is a signal number or a number
representing the exit status of a process which was terminated or
stopped by a signal the name of the signal is printed.
stopped by a signal the name of the signal is printed. The final
form with tt(-L) lists each signal name with its corresponding number.
On some systems, alternative signal names are allowed for a few signals.
Typical examples are tt(SIGCHLD) and tt(SIGCLD) or tt(SIGPOLL) and

View File

@ -953,7 +953,10 @@ has index 1, the signals are offset by 1 from the signal number
used by the operating system. For example, on typical Unix-like systems
tt(HUP) is signal number 1, but is referred to as tt($signals[2]). This
is because of tt(EXIT) at position 1 in the array, which is used
internally by zsh but is not known to the operating system.
internally by zsh but is not known to the operating system. On many systems
there is a block of reserved or unused signal numbers before the POSIX
real-time signals so the array index can't be used as an accurate indicator
of their signal number. Use, for example, tt(kill -l SIGRTMIN) instead.
)
vindex(TRY_BLOCK_ERROR)
item(tt(TRY_BLOCK_ERROR) <S>)(

View File

@ -309,7 +309,7 @@ setfunction(char *name, char *val, int dis)
shfunc_set_sticky(shf);
if (!strncmp(name, "TRAP", 4) &&
(sn = getsignum(name + 4)) != -1) {
(sn = getsigidx(name + 4)) != -1) {
if (settrap(sn, NULL, ZSIG_FUNC)) {
freeeprog(shf->funcdef);
zfree(shf, sizeof(*shf));

View File

@ -3425,16 +3425,16 @@ bin_functions(char *name, char **argv, Options ops, int func)
newsh->sticky = sticky_emulation_dup(shf->sticky, 0);
/* is newsh a signal trap? (adapted from exec.c) */
if (!strncmp(s, "TRAP", 4)) {
int signum = getsignum(s + 4);
if (signum != -1) {
if (settrap(signum, NULL, ZSIG_FUNC)) {
int sigidx = getsigidx(s + 4);
if (sigidx != -1) {
if (settrap(sigidx, NULL, ZSIG_FUNC)) {
freeeprog(newsh->funcdef);
dircache_set(&newsh->filename, NULL);
zfree(newsh, sizeof(*newsh));
return 1;
}
/* Remove any old node explicitly */
removetrapnode(signum);
removetrapnode(sigidx);
}
}
shfunctab->addnode(shfunctab, ztrdup(s), &newsh->node);
@ -3713,15 +3713,15 @@ bin_functions(char *name, char **argv, Options ops, int func)
/* no flags, so just print */
printshfuncexpand(&shf->node, pflags, expand);
} else if (on & PM_UNDEFINED) {
int signum = -1, ok = 1;
int sigidx = -1, ok = 1;
if (!strncmp(*argv, "TRAP", 4) &&
(signum = getsignum(*argv + 4)) != -1) {
(sigidx = getsigidx(*argv + 4)) != -1) {
/*
* Because of the possibility of alternative names,
* we must remove the trap explicitly.
*/
removetrapnode(signum);
removetrapnode(sigidx);
}
if (**argv == '/') {
@ -3757,8 +3757,8 @@ bin_functions(char *name, char **argv, Options ops, int func)
shfunc_set_sticky(shf);
add_autoload_function(shf, *argv);
if (signum != -1) {
if (settrap(signum, NULL, ZSIG_FUNC)) {
if (sigidx != -1) {
if (settrap(sigidx, NULL, ZSIG_FUNC)) {
shfunctab->removenode(shfunctab, *argv);
shfunctab->freenode(&shf->node);
returnval = 1;
@ -7346,7 +7346,7 @@ bin_trap(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
/* If given no arguments, list all currently-set traps */
if (!*argv) {
queue_signals();
for (sig = 0; sig < VSIGCOUNT; sig++) {
for (sig = 0; sig < TRAPCOUNT; sig++) {
if (sigtrapped[sig] & ZSIG_FUNC) {
HashNode hn;
@ -7372,13 +7372,13 @@ bin_trap(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
/* If we have a signal number, unset the specified *
* signals. With only -, remove all traps. */
if ((getsignum(*argv) != -1) || (!strcmp(*argv, "-") && argv++)) {
if ((getsigidx(*argv) != -1) || (!strcmp(*argv, "-") && argv++)) {
if (!*argv) {
for (sig = 0; sig < VSIGCOUNT; sig++)
for (sig = 0; sig < TRAPCOUNT; sig++)
unsettrap(sig);
} else {
for (; *argv; argv++) {
sig = getsignum(*argv);
sig = getsigidx(*argv);
if (sig == -1) {
zwarnnam(name, "undefined signal: %s", *argv);
break;
@ -7403,12 +7403,12 @@ bin_trap(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
Eprog t;
int flags;
sig = getsignum(*argv);
sig = getsigidx(*argv);
if (sig == -1) {
zwarnnam(name, "undefined signal: %s", *argv);
break;
}
if (idigit(**argv) ||
if (idigit(**argv) || (sig >= VSIGCOUNT) ||
!strcmp(sigs[sig], *argv) ||
(!strncmp("SIG", *argv, 3) && !strcmp(sigs[sig], *argv+3))) {
/* The signal was specified by number or by canonical name (with

View File

@ -5427,7 +5427,7 @@ execfuncdef(Estate state, Eprog redir_prog)
} else {
/* is this shell function a signal trap? */
if (!strncmp(s, "TRAP", 4) &&
(signum = getsignum(s + 4)) != -1) {
(signum = getsigidx(s + 4)) != -1) {
if (settrap(signum, NULL, ZSIG_FUNC)) {
freeeprog(shf->funcdef);
dircache_set(&shf->filename, NULL);

View File

@ -836,10 +836,10 @@ static HashNode
removeshfuncnode(UNUSED(HashTable ht), const char *nam)
{
HashNode hn;
int signum;
int sigidx;
if (!strncmp(nam, "TRAP", 4) && (signum = getsignum(nam + 4)) != -1)
hn = removetrap(signum);
if (!strncmp(nam, "TRAP", 4) && (sigidx = getsigidx(nam + 4)) != -1)
hn = removetrap(sigidx);
else
hn = removehashnode(shfunctab, nam);
@ -856,10 +856,10 @@ disableshfuncnode(HashNode hn, UNUSED(int flags))
{
hn->flags |= DISABLED;
if (!strncmp(hn->nam, "TRAP", 4)) {
int signum = getsignum(hn->nam + 4);
if (signum != -1) {
sigtrapped[signum] &= ~ZSIG_FUNC;
unsettrap(signum);
int sigidx = getsigidx(hn->nam + 4);
if (sigidx != -1) {
sigtrapped[sigidx] &= ~ZSIG_FUNC;
unsettrap(sigidx);
}
}
}
@ -876,9 +876,9 @@ enableshfuncnode(HashNode hn, UNUSED(int flags))
shf->node.flags &= ~DISABLED;
if (!strncmp(shf->node.nam, "TRAP", 4)) {
int signum = getsignum(shf->node.nam + 4);
if (signum != -1) {
settrap(signum, NULL, ZSIG_FUNC);
int sigidx = getsigidx(shf->node.nam + 4);
if (sigidx != -1) {
settrap(sigidx, NULL, ZSIG_FUNC);
}
}
}

View File

@ -1382,6 +1382,9 @@ setupshin(char *runscript)
void
init_signals(void)
{
sigtrapped = (int *) hcalloc(TRAPCOUNT * sizeof(int));
siglists = (Eprog *) hcalloc(TRAPCOUNT * sizeof(Eprog));
if (interact) {
int i;
signal_setmask(signal_mask(0));

View File

@ -1073,6 +1073,21 @@ should_report_time(Job j)
return 0;
}
/**/
char *
sigmsg(int sig)
{
static char *unknown = "unknown signal";
#if defined(SIGRTMIN) && defined(SIGRTMAX)
static char rtmsg[] = "real-time event XXX";
if (sig >= SIGRTMIN && sig <= SIGRTMAX) {
sprintf(rtmsg + sizeof(rtmsg) - 4, "%u", sig - SIGRTMIN + 1);
return rtmsg;
}
#endif
return sig <= SIGCOUNT ? sig_msg[sig] : unknown;
}
/* !(lng & 3) means jobs *
* (lng & 1) means jobs -l *
* (lng & 2) means jobs -p
@ -2694,7 +2709,7 @@ static const struct {
int
bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
{
int sig = SIGTERM;
int status, sig = SIGTERM;
int returnval = 0;
#ifdef HAVE_SIGQUEUE
union sigval sigqueue_info;
@ -2740,23 +2755,29 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
if ((*argv)[1] == 'l' && (*argv)[2] == '\0') {
if (argv[1]) {
while (*++argv) {
sig = zstrtol(*argv, &signame, 10);
status = zstrtol(*argv, &signame, 10);
if (signame == *argv) {
signame = casemodify(signame, CASMOD_UPPER);
if (!strncmp(signame, "SIG", 3))
signame += 3;
for (sig = 1; sig <= SIGCOUNT; sig++)
if (!strcasecmp(sigs[sig], signame))
if (!strcmp(sigs[sig], signame))
break;
if (sig > SIGCOUNT) {
int i;
for (i = 0; alt_sigs[i].name; i++)
if (!strcasecmp(alt_sigs[i].name, signame))
if (!strcmp(alt_sigs[i].name, signame))
{
sig = alt_sigs[i].num;
break;
}
}
#if defined(SIGRTMIN) && defined(SIGRTMAX)
if (sig > SIGCOUNT && (sig = rtsigno(signame))) {
printf("%d\n", sig);
} else
#endif
if (sig > SIGCOUNT) {
zwarnnam(nam, "unknown signal: SIG%s",
signame);
@ -2769,14 +2790,15 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
signame);
returnval++;
} else {
if (WIFSIGNALED(sig))
sig = WTERMSIG(sig);
else if (WIFSTOPPED(sig))
sig = WSTOPSIG(sig);
sig = status & ~0200;
if (1 <= sig && sig <= SIGCOUNT)
printf("%s\n", sigs[sig]);
#if defined(SIGRTMIN) && defined(SIGRTMAX)
else if ((signame = rtsigname(sig, 0)))
printf("%s\n", signame);
#endif
else
printf("%d\n", sig);
printf("%d\n", status);
}
}
}
@ -2785,10 +2807,42 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
printf("%s", sigs[1]);
for (sig = 2; sig <= SIGCOUNT; sig++)
printf(" %s", sigs[sig]);
#if defined(SIGRTMIN) && defined(SIGRTMAX)
for (sig = SIGRTMIN; sig <= SIGRTMAX; sig++)
printf(" %s", rtsigname(sig, 0));
#endif
putchar('\n');
return 0;
}
/* with argument "-L" list signals with their numbers in a table */
if ((*argv)[1] == 'L' && (*argv)[2] == '\0') {
#if defined(SIGRTMIN) && defined(SIGRTMAX)
const int width = SIGRTMAX >= 100 ? 3 : 2;
#else
const int width = SIGCOUNT >= 100 ? 3 : 2;
#endif
for (sig = 1; sig < SIGCOUNT
#if defined(SIGRTMIN) && defined(SIGRTMAX)
+ 1
#endif
; sig++)
{
printf("%*d) %-10s%c", width, sig, sigs[sig],
sig % 5 ? ' ' : '\n');
}
#if defined(SIGRTMIN) && defined(SIGRTMAX)
for (sig = SIGRTMIN; sig < SIGRTMAX; sig++) {
printf("%*d) %-10s%c", width, sig, rtsigname(sig, 0),
(sig - SIGRTMIN + SIGCOUNT + 1) % 5 ? ' ' : '\n');
}
printf("%*d) RTMAX\n", width, sig);
#else
printf("%*d) %s\n", width, sig, sigs[sig]);
#endif
return 0;
}
if ((*argv)[1] == 'n' && (*argv)[2] == '\0') {
char *endp;
@ -2833,9 +2887,13 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
break;
}
}
if (sig > SIGCOUNT) {
if (sig > SIGCOUNT
#if defined(SIGRTMIN) && defined(SIGRTMAX)
&& !(sig = rtsigno(signame))
#endif
) {
zwarnnam(nam, "unknown signal: SIG%s", signame);
zwarnnam(nam, "type kill -l for a list of signals");
zwarnnam(nam, "type kill -L for a list of signals");
return 1;
}
}
@ -2916,18 +2974,19 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
return returnval < 126 ? returnval : 1;
}
/* Get a signal number from a string */
/* Get index into table of traps from a string describing a signal */
/**/
mod_export int
getsignum(const char *s)
getsigidx(const char *s)
{
int x, i;
/* check for a signal specified by number */
x = atoi(s);
if (idigit(*s) && x >= 0 && x < VSIGCOUNT)
return x;
if (idigit(*s) && x >= 0)
return SIGIDX(x);
/* search for signal by name */
if (!strncmp(s, "SIG", 3))
@ -2943,11 +3002,16 @@ getsignum(const char *s)
return alt_sigs[i].num;
}
#if defined(SIGRTMIN) && defined(SIGRTMAX)
if ((x = rtsigno(s)))
return SIGIDX(x);
#endif
/* no matching signal */
return -1;
}
/* Get the name for a signal. */
/* Get the name for a signal given the index into the traps table. */
/**/
mod_export const char *
@ -2961,6 +3025,11 @@ getsigname(int sig)
return alt_sigs[i].name;
}
else
#if defined(SIGRTMIN) && defined(SIGRTMAX)
if (sig >= VSIGCOUNT)
return rtsigname(SIGNUM(sig), 0);
else
#endif
return sigs[sig];
/* shouldn't reach here */
@ -2985,10 +3054,22 @@ gettrapnode(int sig, int ignoredisable)
else
getptr = shfunctab->getnode;
#if defined(SIGRTMIN) && defined(SIGRTMAX)
if (sig >= VSIGCOUNT)
sprintf(fname, "TRAP%s", rtsigname(SIGNUM(sig), 0));
else
#endif
sprintf(fname, "TRAP%s", sigs[sig]);
if ((hn = getptr(shfunctab, fname)))
return hn;
#if defined(SIGRTMIN) && defined(SIGRTMAX)
if (sig >= VSIGCOUNT) {
sprintf(fname, "TRAP%s", rtsigname(SIGNUM(sig), 1));
return getptr(shfunctab, fname);
}
#endif
for (i = 0; alt_sigs[i].name; i++) {
if (alt_sigs[i].num == sig) {
sprintf(fname, "TRAP%s", alt_sigs[i].name);

View File

@ -946,8 +946,18 @@ createparamtable(void)
setsparam("ZSH_ARGZERO", ztrdup(posixzero));
setsparam("ZSH_VERSION", ztrdup_metafy(ZSH_VERSION));
setsparam("ZSH_PATCHLEVEL", ztrdup_metafy(ZSH_PATCHLEVEL));
setaparam("signals", sigptr = zalloc((SIGCOUNT+4) * sizeof(char *)));
for (t = sigs; (*sigptr++ = ztrdup_metafy(*t++)); );
setaparam("signals", sigptr = zalloc((TRAPCOUNT + 1) * sizeof(char *)));
t = sigs;
#if defined(SIGRTMIN) && defined(SIGRTMAX)
while (t - sigs <= SIGCOUNT)
*sigptr++ = ztrdup_metafy(*t++);
{
int sig;
for (sig = SIGRTMIN; sig <= SIGRTMAX; sig++)
*sigptr++ = ztrdup_metafy(rtsigname(sig, 0));
}
#endif
while ((*sigptr++ = ztrdup_metafy(*t++))) /* empty */ ;
noerrs = 0;
}

View File

@ -31,10 +31,12 @@
#include "signals.pro"
/* Array describing the state of each signal: an element contains *
* 0 for the default action or some ZSIG_* flags ored together. */
* 0 for the default action or some ZSIG_* flags ored together. *
* Contains TRAPCOUNT elements but can't be allocated statically *
* because that's a dynamic value on Linux */
/**/
mod_export int sigtrapped[VSIGCOUNT];
mod_export int *sigtrapped;
/*
* Trap programme lists for each signal.
@ -48,7 +50,7 @@ mod_export int sigtrapped[VSIGCOUNT];
*/
/**/
mod_export Eprog siglists[VSIGCOUNT];
mod_export Eprog *siglists;
/* Total count of trapped signals */
@ -892,7 +894,7 @@ dosavetrap(int sig, int level)
* Set a trap: note this does not handle manipulation of
* the function table for TRAPNAL functions.
*
* sig is the signal number.
* sig is index into the table of trapped signals.
*
* l is the list to be eval'd for a trap defined with the "trap"
* builtin and should be NULL for a function trap.
@ -931,6 +933,10 @@ settrap(int sig, Eprog l, int flags)
#endif
sig != SIGCHLD)
signal_ignore(sig);
#if defined(SIGRTMIN) && defined(SIGRTMAX)
else if (sig >= VSIGCOUNT && sig < TRAPCOUNT)
signal_ignore(SIGNUM(sig));
#endif
} else {
nsigtrapped++;
sigtrapped[sig] = ZSIG_TRAPPED;
@ -940,6 +946,10 @@ settrap(int sig, Eprog l, int flags)
#endif
sig != SIGCHLD)
install_handler(sig);
#if defined(SIGRTMIN) && defined(SIGRTMAX)
if (sig >= VSIGCOUNT && sig < TRAPCOUNT)
install_handler(SIGNUM(sig));
#endif
}
sigtrapped[sig] |= flags;
/*
@ -1019,6 +1029,11 @@ removetrap(int sig)
#endif
sig != SIGCHLD)
signal_default(sig);
#if defined(SIGRTMIN) && defined(SIGRTMAX)
else if (sig >= VSIGCOUNT && sig < TRAPCOUNT)
signal_default(SIGNUM(sig));
#endif
if (sig == SIGEXIT)
exit_trap_posix = 0;
@ -1172,7 +1187,7 @@ endtrapscope(void)
static int
handletrap(int sig)
{
if (!sigtrapped[sig])
if (!sigtrapped[SIGIDX(sig)])
return 0;
if (trap_queueing_enabled)
@ -1189,7 +1204,7 @@ handletrap(int sig)
return 1;
}
dotrap(sig);
dotrap(SIGIDX(sig));
if (sig == SIGALRM)
{
@ -1481,3 +1496,60 @@ dotrap(int sig)
restore_queue_signals(q);
}
#if defined(SIGRTMIN) && defined(SIGRTMAX)
/* Realtime signals, these are a contiguous block that can
* be separated from the other signals with an unused gap. */
/**/
int
rtsigno(const char* signame)
{
const int maxofs = SIGRTMAX - SIGRTMIN;
const char *end = signame + 5;
int offset;
struct rtdir { int sig; int dir; char op; } x = { 0, 0, 0 };
if (!strncmp(signame, "RTMIN", 5)) {
x = (struct rtdir) { SIGRTMIN, 1, '+' };
} else if (!strncmp(signame, "RTMAX", 5)) {
x = (struct rtdir) { SIGRTMAX, -1, '-' };
} else
return 0;
if (signame[5] == x.op) {
if ((offset = strtol(signame + 6, (char **) &end, 10)) > maxofs)
return 0;
x.sig += offset * x.dir;
}
if (*end)
return 0;
return x.sig;
}
/**/
char *
rtsigname(int signo, int alt)
{
char* buf = (char *) zhalloc(10);
int minofs = signo - SIGRTMIN;
int maxofs = SIGRTMAX - signo;
int offset;
int form = alt ^ (maxofs < minofs);
if (signo < SIGRTMIN || signo > SIGRTMAX)
return NULL;
strcpy(buf, "RT");
strcpy(buf+2, form ? "MAX-" : "MIN+");
offset = form ? maxofs : minofs;
if (offset) {
snprintf(buf + 6, 4, "%d", offset);
} else {
buf[5] = '\0';
}
return buf;
}
#endif

View File

@ -36,6 +36,15 @@
#define SIGZERR (SIGCOUNT+1)
#define SIGDEBUG (SIGCOUNT+2)
#define VSIGCOUNT (SIGCOUNT+3)
#if defined(SIGRTMIN) && defined(SIGRTMAX)
# define TRAPCOUNT (VSIGCOUNT + SIGRTMAX - SIGRTMIN + 1)
# define SIGNUM(x) ((x) >= VSIGCOUNT ? (x) - VSIGCOUNT + SIGRTMIN : (x))
# define SIGIDX(x) ((x) >= SIGRTMIN && (x) <= SIGRTMAX ? (x) - SIGRTMIN + VSIGCOUNT : (x))
#else
# define TRAPCOUNT VSIGCOUNT
# define SIGNUM(x) (x)
# define SIGIDX(x) (x)
#endif
#define SIGEXIT 0
#ifdef SV_BSDSIG

View File

@ -15,7 +15,7 @@
if (signam == "CHLD" && sig[signum] == "CLD") sig[signum] = ""
if (signam == "POLL" && sig[signum] == "IO") sig[signum] = ""
if (signam == "ABRT" && sig[signum] == "IOT") sig[signum] = ""
if (sig[signum] == "") {
if (signam !~ /RTM(IN|AX)/ && sig[signum] == "") {
sig[signum] = signam
if (0 + max < 0 + signum && signum < 60)
max = signum
@ -66,10 +66,6 @@ END {
printf "#include %czsh.mdh%c\n", 34, 34
printf "\n"
printf "/**/\n"
printf "#define sigmsg(sig) ((sig) <= SIGCOUNT ? sig_msg[sig]"
printf " : %c%s%c)", 34, "unknown signal", 34
printf "\n"
printf "/**/\n"
printf "mod_export char *sig_msg[SIGCOUNT+2] = {\n"
printf "\t%c%s%c,\n", 34, "done", 34