Make the new printf-surprise test more precise.
[coreutils/ericb.git] / src / kill.c
blob8d1a1c202feafdcdbab5b3c779205ff65b27db83
1 /* kill -- send a signal to a process
2 Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
17 /* Written by Paul Eggert. */
19 #include <config.h>
20 #include <stdio.h>
21 #include <getopt.h>
22 #include <sys/types.h>
23 #include <signal.h>
25 #if HAVE_SYS_WAIT_H
26 # include <sys/wait.h>
27 #endif
28 #ifndef WIFSIGNALED
29 # define WIFSIGNALED(s) (((s) & 0xFFFF) - 1 < (unsigned int) 0xFF)
30 #endif
31 #ifndef WTERMSIG
32 # define WTERMSIG(s) ((s) & 0x7F)
33 #endif
35 #include "system.h"
36 #include "error.h"
37 #include "sig2str.h"
39 /* The official name of this program (e.g., no `g' prefix). */
40 #define PROGRAM_NAME "kill"
42 #define AUTHORS "Paul Eggert"
44 #if ! (HAVE_DECL_STRSIGNAL || defined strsignal)
45 # if ! (HAVE_DECL_SYS_SIGLIST || defined sys_siglist)
46 # if HAVE_DECL__SYS_SIGLIST || defined _sys_siglist
47 # define sys_siglist _sys_siglist
48 # elif HAVE_DECL___SYS_SIGLIST || defined __sys_siglist
49 # define sys_siglist __sys_siglist
50 # endif
51 # endif
52 # if HAVE_DECL_SYS_SIGLIST || defined sys_siglist
53 # define strsignal(signum) (0 <= (signum) && (signum) <= SIGNUM_BOUND \
54 ? sys_siglist[signum] \
55 : 0)
56 # endif
57 # ifndef strsignal
58 # define strsignal(signum) 0
59 # endif
60 #endif
62 /* The name this program was run with, for error messages. */
63 char *program_name;
65 static char const short_options[] =
66 "0::1::2::3::4::5::6::7::8::9::"
67 "A::B::C::D::E::F::G::H::I::J::K::L::M::"
68 "N::O::P::Q::R::S::T::U::V::W::X::Y::Z::"
69 "ln:s:t";
71 static struct option const long_options[] =
73 {"list", no_argument, NULL, 'l'},
74 {"signal", required_argument, NULL, 's'},
75 {"table", no_argument, NULL, 't'},
76 {GETOPT_HELP_OPTION_DECL},
77 {GETOPT_VERSION_OPTION_DECL},
78 {NULL, 0, NULL, 0}
81 void
82 usage (int status)
84 if (status != EXIT_SUCCESS)
85 fprintf (stderr, _("Try `%s --help' for more information.\n"),
86 program_name);
87 else
89 printf (_("\
90 Usage: %s [-s SIGNAL | -SIGNAL] PID...\n\
91 or: %s -l [SIGNAL]...\n\
92 or: %s -t [SIGNAL]...\n\
93 "),
94 program_name, program_name, program_name);
95 fputs (_("\
96 Send signals to processes, or list signals.\n\
97 \n\
98 "), stdout);
99 fputs (_("\
100 Mandatory arguments to long options are mandatory for short options too.\n\
101 "), stdout);
102 fputs (_("\
103 -s, --signal=SIGNAL, -SIGNAL\n\
104 specify the name or number of the signal to be sent\n\
105 -l, --list list signal names, or convert signal names to/from numbers\n\
106 -t, --table print a table of signal information\n\
107 "), stdout);
108 fputs (HELP_OPTION_DESCRIPTION, stdout);
109 fputs (VERSION_OPTION_DESCRIPTION, stdout);
110 fputs (_("\n\
111 SIGNAL may be a signal name like `HUP', or a signal number like `1',\n\
112 or an exit status of a process terminated by a signal.\n\
113 PID is an integer; if negative it identifies a process group.\n\
114 "), stdout);
115 printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
116 emit_bug_reporting_address ();
118 exit (status);
121 /* Convert OPERAND to a signal number with printable representation SIGNAME.
122 Return the signal number, or -1 if unsuccessful. */
124 static int
125 operand2sig (char const *operand, char *signame)
127 int signum;
129 if (ISDIGIT (*operand))
131 char *endp;
132 long int l = (errno = 0, strtol (operand, &endp, 10));
133 int i = l;
134 signum = (operand == endp || *endp || errno || i != l ? -1
135 : WIFSIGNALED (i) ? WTERMSIG (i)
136 : i);
138 else
140 /* Convert signal to upper case in the C locale, not in the
141 current locale. Don't assume ASCII; it might be EBCDIC. */
142 char *upcased = xstrdup (operand);
143 char *p;
144 for (p = upcased; *p; p++)
145 if (strchr ("abcdefghijklmnopqrstuvwxyz", *p))
146 *p += 'A' - 'a';
148 /* Look for the signal name, possibly prefixed by "SIG",
149 and possibly lowercased. */
150 if (! (str2sig (upcased, &signum) == 0
151 || (upcased[0] == 'S' && upcased[1] == 'I' && upcased[2] == 'G'
152 && str2sig (upcased + 3, &signum) == 0)))
153 signum = -1;
155 free (upcased);
158 if (signum < 0 || sig2str (signum, signame) != 0)
160 error (0, 0, _("%s: invalid signal"), operand);
161 return -1;
164 return signum;
167 /* Print a row of `kill -t' output. NUM_WIDTH is the maximum signal
168 number width, and SIGNUM is the signal number to print. The
169 maximum name width is NAME_WIDTH, and SIGNAME is the name to print. */
171 static void
172 print_table_row (unsigned int num_width, int signum,
173 unsigned int name_width, char const *signame)
175 char const *description = strsignal (signum);
176 printf ("%*d %-*s %s\n", num_width, signum, name_width, signame,
177 description ? description : "?");
180 /* Print a list of signal names. If TABLE, print a table.
181 Print the names specified by ARGV if nonzero; otherwise,
182 print all known names. Return a suitable exit status. */
184 static int
185 list_signals (bool table, char *const *argv)
187 int signum;
188 int status = EXIT_SUCCESS;
189 char signame[SIG2STR_MAX];
191 if (table)
193 unsigned int name_width = 0;
195 /* Compute the maximum width of a signal number. */
196 unsigned int num_width = 1;
197 for (signum = 1; signum <= SIGNUM_BOUND / 10; signum *= 10)
198 num_width++;
200 /* Compute the maximum width of a signal name. */
201 for (signum = 1; signum <= SIGNUM_BOUND; signum++)
202 if (sig2str (signum, signame) == 0)
204 size_t len = strlen (signame);
205 if (name_width < len)
206 name_width = len;
209 if (argv)
210 for (; *argv; argv++)
212 signum = operand2sig (*argv, signame);
213 if (signum < 0)
214 status = EXIT_FAILURE;
215 else
216 print_table_row (num_width, signum, name_width, signame);
218 else
219 for (signum = 1; signum <= SIGNUM_BOUND; signum++)
220 if (sig2str (signum, signame) == 0)
221 print_table_row (num_width, signum, name_width, signame);
223 else
225 if (argv)
226 for (; *argv; argv++)
228 signum = operand2sig (*argv, signame);
229 if (signum < 0)
230 status = EXIT_FAILURE;
231 else
233 if (ISDIGIT (**argv))
234 puts (signame);
235 else
236 printf ("%d\n", signum);
239 else
240 for (signum = 1; signum <= SIGNUM_BOUND; signum++)
241 if (sig2str (signum, signame) == 0)
242 puts (signame);
245 return status;
248 /* Send signal SIGNUM to all the processes or process groups specified
249 by ARGV. Return a suitable exit status. */
251 static int
252 send_signals (int signum, char *const *argv)
254 int status = EXIT_SUCCESS;
255 char const *arg = *argv;
259 char *endp;
260 intmax_t n = (errno = 0, strtoimax (arg, &endp, 10));
261 pid_t pid = n;
263 if (errno == ERANGE || pid != n || arg == endp || *endp)
265 error (0, 0, _("%s: invalid process id"), arg);
266 status = EXIT_FAILURE;
268 else if (kill (pid, signum) != 0)
270 error (0, errno, "%s", arg);
271 status = EXIT_FAILURE;
274 while ((arg = *++argv));
276 return status;
280 main (int argc, char **argv)
282 int optc;
283 bool list = false;
284 bool table = false;
285 int signum = -1;
286 char signame[SIG2STR_MAX];
288 initialize_main (&argc, &argv);
289 program_name = argv[0];
290 setlocale (LC_ALL, "");
291 bindtextdomain (PACKAGE, LOCALEDIR);
292 textdomain (PACKAGE);
294 atexit (close_stdout);
296 while ((optc = getopt_long (argc, argv, short_options, long_options, NULL))
297 != -1)
298 switch (optc)
300 case '0': case '1': case '2': case '3': case '4':
301 case '5': case '6': case '7': case '8': case '9':
302 if (optind != 2)
304 /* This option is actually a process-id. */
305 optind--;
306 goto no_more_options;
308 /* Fall through. */
309 case 'A': case 'B': case 'C': case 'D': case 'E':
310 case 'F': case 'G': case 'H': case 'I': case 'J':
311 case 'K': case 'L': case 'M': case 'N': case 'O':
312 case 'P': case 'Q': case 'R': case 'S': case 'T':
313 case 'U': case 'V': case 'W': case 'X': case 'Y':
314 case 'Z':
315 if (! optarg)
316 optarg = argv[optind - 1] + strlen (argv[optind - 1]);
317 if (optarg != argv[optind - 1] + 2)
319 error (0, 0, _("invalid option -- %c"), optc);
320 usage (EXIT_FAILURE);
322 optarg--;
323 /* Fall through. */
324 case 'n': /* -n is not documented, but is for Bash compatibility. */
325 case 's':
326 if (0 <= signum)
328 error (0, 0, _("%s: multiple signals specified"), optarg);
329 usage (EXIT_FAILURE);
331 signum = operand2sig (optarg, signame);
332 if (signum < 0)
333 usage (EXIT_FAILURE);
334 break;
336 case 't':
337 table = true;
338 /* Fall through. */
339 case 'l':
340 if (list)
342 error (0, 0, _("multiple -l or -t options specified"));
343 usage (EXIT_FAILURE);
345 list = true;
346 break;
348 case_GETOPT_HELP_CHAR;
349 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
350 default:
351 usage (EXIT_FAILURE);
353 no_more_options:;
355 if (signum < 0)
356 signum = SIGTERM;
357 else if (list)
359 error (0, 0, _("cannot combine signal with -l or -t"));
360 usage (EXIT_FAILURE);
363 if ( ! list && argc <= optind)
365 error (0, 0, _("no process ID specified"));
366 usage (EXIT_FAILURE);
369 return (list
370 ? list_signals (table, optind < argc ? argv + optind : NULL)
371 : send_signals (signum, argv + optind));