]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
Rewrite from scratch. Support everything
authorJim Meyering <jim@meyering.net>
Sun, 28 Apr 2002 21:32:36 +0000 (21:32 +0000)
committerJim Meyering <jim@meyering.net>
Sun, 28 Apr 2002 21:32:36 +0000 (21:32 +0000)
required by POSIX 1003.1-2001; when this conflicts with Bash,
stick with POSIX.  The conflicts are kill -l output format,
and lower case signal names preceded by `-' (e.g., "kill -hup"
is no longer supported).  Remove -L or --long-list option.
Add -t or --table option.  Rename --sigspec to --signal;
remove --signum and do not advertise obsolescent option -n.
Use str2sig and str2sig to convert between signal names and
numbers.

src/kill.c

index d0c8ea84f3ea67b46fd8c9ba47527db40865463d..f53bb3a5b4bdbc854c205f5a91f905161741f430 100644 (file)
@@ -1,5 +1,5 @@
 /* kill -- send a signal to a process
-   Copyright (C) 2001 Free Software Foundation, Inc.
+   Copyright (C) 2002 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    along with this program; if not, write to the Free Software Foundation,
    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
-/* Written by Marcus Brinkmann.  */
+/* Written by Paul Eggert.  */
 
 #include <config.h>
 #include <stdio.h>
 #include <getopt.h>
 #include <sys/types.h>
-
 #include <signal.h>
 
+#if HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+#ifndef WIFSIGNALED
+# define WIFSIGNALED(s) (((s) & 0xFFFF) - 1 < (unsigned int) 0xFF)
+#endif
+#ifndef WTERMSIG
+# define WTERMSIG(s) ((s) & 0x7F)
+#endif
+
 #include "system.h"
 #include "closeout.h"
-#include "human.h"
 #include "error.h"
-#include "xstrtol.h"
+#include "sig2str.h"
 
 /* The official name of this program (e.g., no `g' prefix).  */
 #define PROGRAM_NAME "kill"
 
-#define AUTHORS "Marcus Brinkmann"
-
+#define AUTHORS "Paul Eggert"
 \f
-/* An invalid signal number.  */
-#define NO_SIG -1
-
-/* A structure holding the number and the name of a signal.  */
-struct sigspec
-{
-  int signum;
-  char *signame;
-};
-
-/* The list of signals was taken from bash 2.05.  The best name for a
-   signal comes after any possible alias, so it is read it from back
-   to front.  This is why the terminating null entry comes first.  */
-static struct sigspec sigspecs[] =
-{
-  { NO_SIG, NULL },
-
-  /* Null is used to test for the existance and ownership of a PID.  */
-  { 0, "0" },
-
-/* AIX */
-#if defined (SIGLOST)  /* resource lost (eg, record-lock lost) */
-  { SIGLOST, "LOST" },
-#endif
-
-#if defined (SIGMSG)   /* HFT input data pending */
-  { SIGMSG, "MSG" },
-#endif
-
-#if defined (SIGDANGER)        /* system crash imminent */
-  { SIGDANGER, "DANGER" },
-#endif
-
-#if defined (SIGMIGRATE) /* migrate process to another CPU */
-  { SIGMIGRATE, "MIGRATE" },
-#endif
-
-#if defined (SIGPRE)   /* programming error */
-  { SIGPRE, "PRE" },
-#endif
-
-#if defined (SIGVIRT)  /* AIX virtual time alarm */
-  { SIGVIRT, "VIRT" },
-#endif
-
-#if defined (SIGALRM1) /* m:n condition variables */
-  { SIGALRM1, "ALRM1" },
-#endif
-
-#if defined (SIGWAITING)       /* m:n scheduling */
-  { SIGWAITING, "WAITING" },
-#endif
-
-#if defined (SIGGRANT) /* HFT monitor mode granted */
-  { SIGGRANT, "GRANT" },
-#endif
-
-#if defined (SIGKAP)   /* keep alive poll from native keyboard */
-  { SIGKAP, "KAP" },
-#endif
-
-#if defined (SIGRETRACT) /* HFT monitor mode retracted */
-  { SIGRETRACT, "RETRACT" },
-#endif
-
-#if defined (SIGSOUND) /* HFT sound sequence has completed */
-  { SIGSOUND, "SOUND" },
-#endif
-
-#if defined (SIGSAK)   /* Secure Attention Key */
-  { SIGSAK, "SAK" },
-#endif
-
-/* SunOS5 */
-#if defined (SIGLWP)   /* special signal used by thread library */
-  { SIGLWP, "LWP" },
-#endif
-
-#if defined (SIGFREEZE)        /* special signal used by CPR */
-  { SIGFREEZE, "FREEZE" },
-#endif
-
-#if defined (SIGTHAW)  /* special signal used by CPR */
-  { SIGTHAW, "THAW" },
-#endif
-
-#if defined (SIGCANCEL)        /* thread cancellation signal used by libthread */
-  { SIGCANCEL, "CANCEL" },
-#endif
-
-/* HP-UX */
-#if defined (SIGDIL)   /* DIL signal (?) */
-  { SIGDIL, "DIL" },
-#endif
-
-/* System V */
-#if defined (SIGCLD)   /* Like SIGCHLD.  */
-  { SIGCLD, "CLD" },
-#endif
-
-#if defined (SIGPWR)   /* power state indication */
-  { SIGPWR, "PWR" },
-#endif
-
-#if defined (SIGPOLL)  /* Pollable event (for streams)  */
-  { SIGPOLL, "POLL" },
-#endif
-
-/* Unknown */
-#if defined (SIGWINDOW)
-  { SIGWINDOW, "WINDOW" },
-#endif
-
-/* Common */
-#if defined (SIGHUP)   /* hangup */
-  { SIGHUP, "HUP" },
-#endif
-
-#if defined (SIGINT)   /* interrupt */
-  { SIGINT, "INT" },
-#endif
-
-#if defined (SIGQUIT)  /* quit */
-  { SIGQUIT, "QUIT" },
-#endif
-
-#if defined (SIGILL)   /* illegal instruction (not reset when caught) */
-  { SIGILL, "ILL" },
-#endif
-
-#if defined (SIGTRAP)  /* trace trap (not reset when caught) */
-  { SIGTRAP, "TRAP" },
-#endif
-
-#if defined (SIGIOT)   /* IOT instruction */
-  { SIGIOT, "IOT" },
-#endif
-
-#if defined (SIGABRT)  /* Cause current process to dump core. */
-  { SIGABRT, "ABRT" },
-#endif
-
-#if defined (SIGEMT)   /* EMT instruction */
-  { SIGEMT, "EMT" },
-#endif
-
-#if defined (SIGFPE)   /* floating point exception */
-  { SIGFPE, "FPE" },
-#endif
-
-#if defined (SIGKILL)  /* kill (cannot be caught or ignored) */
-  { SIGKILL, "KILL" },
-#endif
-
-#if defined (SIGBUS)   /* bus error */
-  { SIGBUS, "BUS" },
-#endif
-
-#if defined (SIGSEGV)  /* segmentation violation */
-  { SIGSEGV, "SEGV" },
-#endif
-
-#if defined (SIGSYS)   /* bad argument to system call */
-  { SIGSYS, "SYS" },
-#endif
-
-#if defined (SIGPIPE)  /* write on a pipe with no one to read it */
-  { SIGPIPE, "PIPE" },
-#endif
-
-#if defined (SIGALRM)  /* alarm clock */
-  { SIGALRM, "ALRM" },
-#endif
-
-#if defined (SIGTERM)  /* software termination signal from kill */
-  { SIGTERM, "TERM" },
-#endif
-
-#if defined (SIGURG)   /* urgent condition on IO channel */
-  { SIGURG, "URG" },
-#endif
-
-#if defined (SIGSTOP)  /* sendable stop signal not from tty */
-  { SIGSTOP, "STOP" },
-#endif
-
-#if defined (SIGTSTP)  /* stop signal from tty */
-  { SIGTSTP, "TSTP" },
-#endif
-
-#if defined (SIGCONT)  /* continue a stopped process */
-  { SIGCONT, "CONT" },
-#endif
-
-#if defined (SIGCHLD)  /* to parent on child stop or exit */
-  { SIGCHLD, "CHLD" },
+#if ! (HAVE_DECL_STRTOIMAX || defined strtoimax)
+intmax_t strtoimax ();
 #endif
 
-#if defined (SIGTTIN)  /* to readers pgrp upon background tty read */
-  { SIGTTIN, "TTIN" },
-#endif
-
-#if defined (SIGTTOU)  /* like TTIN for output if (tp->t_local&LTOSTOP) */
-  { SIGTTOU, "TTOU" },
-#endif
-
-#if defined (SIGIO)    /* input/output possible signal */
-  { SIGIO, "IO" },
-#endif
-
-#if defined (SIGXCPU)  /* exceeded CPU time limit */
-  { SIGXCPU, "XCPU" },
-#endif
-
-#if defined (SIGXFSZ)  /* exceeded file size limit */
-  { SIGXFSZ, "XFSZ" },
-#endif
-
-#if defined (SIGVTALRM)        /* virtual time alarm */
-  { SIGVTALRM, "VTALRM" },
-#endif
-
-#if defined (SIGPROF)  /* profiling time alarm */
-  { SIGPROF, "PROF" },
-#endif
-
-#if defined (SIGWINCH) /* window changed */
-  { SIGWINCH, "WINCH" },
-#endif
-
-/* 4.4 BSD */
-#if defined (SIGINFO) && !defined (_SEQUENT_)  /* information request */
-  { SIGINFO, "INFO" },
-#endif
-
-#if defined (SIGUSR1)  /* user defined signal 1 */
-  { SIGUSR1, "USR1" },
-#endif
-
-#if defined (SIGUSR2)  /* user defined signal 2 */
-  { SIGUSR2, "USR2" },
-#endif
-
-#if defined (SIGKILLTHR)       /* BeOS: Kill Thread */
-  { SIGKILLTHR, "KILLTHR" }
+#if ! (HAVE_DECL_STRSIGNAL || defined strsignal)
+# if ! (HAVE_DECL_SYS_SIGLIST || defined sys_siglist)
+#  if HAVE_DECL__SYS_SIGLIST || defined _sys_siglist
+#   define sys_siglist _sys_siglist
+#  endif
+# endif
+# if HAVE_DECL_SYS_SIGLIST || defined sys_siglist
+#  define strsignal(signum) (0 <= (signum) && (signum) <= SIGNUM_BOUND \
+                            ? sys_siglist[signum] \
+                            : 0)
+# endif
+# ifndef strsignal
+#  define strsignal(signum) 0
+# endif
 #endif
-};
-
-/* The last entry of the complete signal list, including real time
-   signals.  */
-struct sigspec *sigspecs_last;
-
-/* The number of sigspecs in the list.  */
-int sigspecs_size;
-
-/* The number of digits in NSIG (approx.).  */
-int nsig_digits;
-
 \f
 /* The name this program was run with, for error messages.  */
 char *program_name;
 
-/* All options which require an argument are known to the
-   pre-scan loop in main().  */
-#define OPT_SIGSPEC_LONG "sigspec"
-#define OPT_SIGNUM_LONG "signum"
+static char const short_options[] =
+  "0::1::2::3::4::5::6::7::8::9::"
+  "A::B::C::D::E::F::G::H::I::J::K::L::M::"
+  "N::O::P::Q::R::S::T::U::V::W::X::Y::Z::"
+  "ln:s:t";
 
 static struct option const long_options[] =
 {
   {"list", no_argument, NULL, 'l'},
-  {"long-list", no_argument, NULL, 'L'},
-  {OPT_SIGSPEC_LONG, required_argument, NULL, 's'},
-  {OPT_SIGNUM_LONG, required_argument, NULL, 'n'},
+  {"signal", required_argument, NULL, 's'},
+  {"table", no_argument, NULL, 't'},
   {GETOPT_HELP_OPTION_DECL},
   {GETOPT_VERSION_OPTION_DECL},
   {NULL, 0, NULL, 0}
@@ -323,282 +91,207 @@ usage (int status)
   else
     {
       printf (_("\
-Usage: %s [-s SIGSPEC | -n SIGNUM | -SIGSPEC] PID ...\n\
-  or:  %s -l [SIGSPEC] ...\n\
+Usage: %s [-s SIGNAL | -SIGNAL] PID...\n\
+  or:  %s -l [SIGNAL]...\n\
+  or:  %s -t [SIGNAL]...\n\
 "),
-             program_name, program_name);
+             program_name, program_name, program_name);
       fputs (_("\
-Send the signal named by SIGSPEC or SIGNUM to processes named by PID.\n\
-\n\
-  -s, --" OPT_SIGSPEC_LONG " SIGSPEC     name or number of signal to be sent\n\
-  -n, --" OPT_SIGNUM_LONG " SIGNUM       number of signal to be sent\n\
-\n\
-  -l, --list                list the signal names\n\
-  -L, --long-list           list the signal names with their numbers\n\
+Send signals to processes, or list signals.\n\
 \n\
+"), stdout);
+      fputs (_("\
+Mandatory arguments to long options are mandatory for short options too.\n\
+"), stdout);
+      fputs (_("\
+  -s, --signal SIGNAL, -SIGNAL  Name or number of signal to be sent.\n\
+  -l, --list                    List signal names.\n\
+  -t, --table                   Print a table of signal information.\n\
 "), stdout);
       fputs (HELP_OPTION_DESCRIPTION, stdout);
       fputs (VERSION_OPTION_DESCRIPTION, stdout);
-      fputs (_("\
-\n\
-kill returns true if at least one signal was successfully sent, or\n\
-false if an error occurs or an invalid option is encountered.\n\
+      fputs (_("\n\
+SIGNAL may be a signal name like `HUP', or a signal number like `1',\n\
+or an exit status of a process terminated by a signal.\n\
+PID is an integer; if negative it identifies a process group.\n\
 "), stdout);
       puts (_("\nReport bugs to <bug-sh-utils@gnu.org>."));
     }
   exit (status);
 }
-
 \f
-/* The function is called once before option parsing.  It calculates
-   the maximum number of digits in a signum, adds the realtime signal
-   specs to the signal list, and adds all signals of the form -SIGTERM
-   and -TERM as possible options.  */
-static void
-initialize (void)
-{
-  char number[LONGEST_HUMAN_READABLE + 1];
-  struct sigspec *newspecs;
+/* Convert OPERAND to a signal number with printable representation SIGNAME.
+   Return the signal number, or -1 if unsuccessful.  */
 
-  nsig_digits = strlen (human_readable ((uintmax_t) NSIG, number, 1, 1));
-
-  sigspecs_size = sizeof (sigspecs) / sizeof (struct sigspec);
-
-#if defined (SIGRTMIN) || defined (SIGRTMAX)
-  /* POSIX 1003.1b-1993 defines real time signals.  The following code
-     is so convoluted because it also takes care of incomplete
-     implementations.  */
-# ifndef SIGRTMIN
-#  define SIGRTMIN SIGRTMAX
-# endif
-# ifndef SIGRTMAX
-#  define SIGRTMAX SIGRTMIN
-# endif
+static int
+operand2sig (char const *operand, char *signame)
+{
+  int signum;
 
-  /* Sanity check.  */
-  if (SIGRTMAX >= SIGRTMIN)
+  if (ISDIGIT (*operand))
     {
-      int rtsigc = SIGRTMAX - SIGRTMIN + 1;
-      int i;
-      /* Account for "RTMIN+" resp "RTMAX-", the number and '\0'.  */
-      int maxlength = nsig_digits + 6 + 1;
-
-      newspecs = xmalloc (sizeof (struct sigspec)
-                         * (sigspecs_size + rtsigc));
-
-      /* After this, newspecs will always point to the last element of
-        the array.  */
-      newspecs->signum = NO_SIG;
-      newspecs->signame = NULL;
-
-      (++newspecs)->signum = SIGRTMAX;
-      newspecs->signame = "RTMAX";
-      if (rtsigc > 1)
-       {
-         (++newspecs)->signum = SIGRTMIN;
-         newspecs->signame = "RTMIN";
-       }
-
-      /* Create new elements for all missing realtime signals.  */
-      for (i = 0; i < rtsigc - 2; i++)
-       {
-         (++newspecs)->signum = SIGRTMIN + 1 + i;
-         newspecs->signame = xmalloc (maxlength);
-
-         snprintf (newspecs->signame, maxlength, "%s%d",
-                   (i < (rtsigc - 2)/2
-                    ? "RTMIN+" : "RTMAX-"),
-                   (i < (rtsigc - 2)/2
-                    ? i + 1 : (rtsigc - 2) - i));
-       }
-
-      /* Copy the existing elements in the following space.  */
-      for (i = 1; i < sigspecs_size; i++)
-       *(++newspecs) = sigspecs[i];
+      char *endp;
+      long int l = (errno = 0, strtol (operand, &endp, 10));
+      int i = l;
+      signum = (operand == endp || *endp || errno || i != l ? -1
+               : WIFSIGNALED (i) ? WTERMSIG (i)
+               : i);
+    }
+  else
+    {
+      /* Convert signal to upper case in the C locale, not in the
+        current locale.  Don't assume ASCII; it might be EBCDIC.  */
+      char *upcased = xstrdup (operand);
+      char *p;
+      for (p = upcased; *p; p++)
+       if (strchr ("abcdefghijklmnopqrstuvwxyz", *p))
+         *p += 'A' - 'a';
+
+      /* Look for the signal name, possibly prefixed by "SIG",
+        and possibly lowercased.  */
+      if (! (str2sig (upcased, &signum) == 0
+            || (upcased[0] == 'S' && upcased[1] == 'I' && upcased[2] == 'G'
+                && str2sig (upcased + 3, &signum) == 0)))
+       signum = -1;
+
+      free (upcased);
+    }
 
-      sigspecs_last = newspecs;
-      sigspecs_size += rtsigc;
+  if (signum < 0 || sig2str (signum, signame) != 0)
+    {
+      error (0, 0, _("%s: invalid signal"), operand);
+      return -1;
     }
-#else
-  sigspecs_last = sigspecs + (sigspecs_size - 1);
-#endif
-}
 
+  return signum;
+}
 \f
-typedef enum { LIST_NONE, LIST_FLAT, LIST_PRETTY } list_t;
+/* Print a row of `kill -t' output.  NUM_WIDTH is the maximum signal
+   number width, and SIGNUM is the signal number to print.  The
+   maximum name width is NAME_WIDTH, and SIGNAME is the name to print.  */
 
-/* Print out a table listing all signals specifications with their
-   preferred name.  */
 static void
-list_signals (list_t type)
+print_table_row (int num_width, int signum,
+                int name_width, char const *signame)
 {
-  int i = 0;
-  int entrylen = 0;
-  int unsorted;
-  int entries;
-  int last_signum = NO_SIG;
-  int column = 0;
-  struct sigspec *spec = sigspecs_last;
-  struct sigspec **specs = xmalloc (sizeof (struct sigspec *)
-                                   * (sigspecs_size - 1));
-
-
-  /* Gather maximum name length and prepare sort array.  Note that the
-     list is reversed in the array.  This is taken into account by the
-     output routine below.  */
-  while (spec->signum != NO_SIG)
-    {
-      specs[i++] = spec;
-      if (spec->signame && strlen (spec->signame) > entrylen)
-       entrylen = strlen (spec->signame);
-      spec--;
-    }
-
-  /* Sort the array by signal number.  This is a simple bubble sort,
-     but the point is that the order of entries with the same signum
-     is presevered (otherwise the preferred alias is lost).  */
-  if (sigspecs_size > 2)
-    do
-      {
-       unsorted = 0;
-
-       for (i = 0; i < sigspecs_size - 2; i++)
-         {
-           if (specs[i]->signum > specs[i+1]->signum)
-             {
-               struct sigspec *saved = specs[i];
-               specs[i] = specs[i+1];
-               specs[i+1] = saved;
-               unsorted = 1;
-             }
-         }
-      }
-    while (unsorted);
-
-  /* Account for "NR) NAME ".  Calculate for 79 columns, the 80 takes
-     into account that the last entry is not followed by a space.  */
-  entrylen += nsig_digits + 2 + 1;
-  entries = 80 / entrylen;
-  if (entries < 1)
-    entries = 1;
-
-  for (i = 0; i < sigspecs_size - 1; i++)
-    {
-      /* Skip duplicated signal numbers, signums without name and the
-        special signal number `0'.  */
-      if (specs[i]->signame && specs[i]->signum
-         && (last_signum == NO_SIG || last_signum != specs[i]->signum))
-       {
-         switch (type)
-           {
-           case LIST_PRETTY:
-             column++;
-             printf ("%*i) %-*s", nsig_digits, specs[i]->signum,
-                     entrylen - nsig_digits - 2 + (column != entries ? 1 : 0),
-                     specs[i]->signame);
-             if (column == entries - 1)
-               {
-                 column = 0;
-                 putchar ('\n');
-               }
-             break;
-           case LIST_FLAT:
-             column += printf ("%s%s",
-                               (column == 0 ? ""
-                                : (column + strlen (specs[i]->signame) > 78
-                                   ? "\n" : " ")),
-                               specs[i]->signame);
-             if (column > 79)
-               column = strlen (specs[i]->signame);
-             break;
-           default:
-             break;
-           }
-         last_signum = specs[i]->signum;
-       }
-      if (i == sigspecs_size - 2)
-       putchar ('\n');
-    }
+  char const *description = strsignal (signum);
+  printf ("%*d %-*s %s\n", num_width, signum, name_width, signame,
+         description ? description : "?");
 }
 
-\f
-/* Turn a string into a signal number, or a number into a signal
-   number.  If STRING is "2", or "INT", then return the integer 2.
-   Return NO_SIG if STRING doesn't contain a valid signal
-   descriptor.  */
+/* Print a list of signal names.  If TABLE, print a table.
+   Print the names specified by ARGV if nonzero; otherwise,
+   print all known names.  Return a suitable exit status.  */
+
 static int
-decode_signal (char const *sigspec)
+list_signals (bool table, char *const *argv)
 {
-  struct sigspec const *spec = sigspecs_last;
-  long l;
+  int signum;
+  int status = EXIT_SUCCESS;
+  char signame[SIG2STR_MAX];
 
-  if (xstrtol (sigspec, NULL, 0, &l, "") == LONGINT_OK)
+  if (table)
     {
-      int sig = l;
+      int name_width = 0;
 
-      if (sig != l)
-       return NO_SIG;
+      /* Compute the maximum width of a signal number.  */
+      int num_width = 1;
+      for (signum = 1; signum <= SIGNUM_BOUND / 10; signum *= 10)
+       num_width++;
 
-      while (spec->signum != NO_SIG && spec->signum != sig)
-       spec--;
-      return spec->signum;
-    }
+      /* Compute the maximum width of a signal name.  */
+      for (signum = 1; signum <= SIGNUM_BOUND; signum++)
+       if (sig2str (signum, signame) == 0)
+         {
+           size_t len = strlen (signame);
+           if (name_width < len)
+             name_width = len;
+         }
 
-  /* A leading `SIG' may be omitted. */
-  while (spec->signum != NO_SIG)
+      if (argv)
+       for (; *argv; argv++)
+         {
+           signum = operand2sig (*argv, signame);
+           if (signum < 0)
+             status = EXIT_FAILURE;
+           else
+             print_table_row (num_width, signum, name_width, signame);
+         }
+      else
+       for (signum = 1; signum <= SIGNUM_BOUND; signum++)
+         if (sig2str (signum, signame) == 0)
+           print_table_row (num_width, signum, name_width, signame);
+    }
+  else
     {
-      if (spec->signame
-         && (strcasecmp (sigspec, spec->signame) == 0
-             || (strncasecmp (sigspec, "SIG", 3) == 0
-                 && strcasecmp (sigspec + 3, spec->signame) == 0)))
-       return (spec->signum);
-      spec--;
+      if (argv)
+       for (; *argv; argv++)
+         {
+           signum = operand2sig (*argv, signame);
+           if (signum < 0)
+             status = EXIT_FAILURE;
+           else
+             {
+               if (ISDIGIT (**argv))
+                 puts (signame);
+               else
+                 printf ("%d\n", signum);
+             }
+         }
+      else
+       for (signum = 1; signum <= SIGNUM_BOUND; signum++)
+         if (sig2str (signum, signame) == 0)
+           puts (signame);
     }
-  return NO_SIG;
-}
 
-/* Find the preferred name for the signal with the number SIGNUM.  */
-static char const *const
-name_signal (int signum)
-{
-  struct sigspec const *spec = sigspecs_last;
-
-  while (spec->signum != NO_SIG && spec->signum != signum)
-    spec--;
-  return spec->signame;
+  return status;
 }
+\f
+/* Send signal SIGNUM to all the processes or process groups specified
+   by ARGV.  Return a suitable exit status.  */
 
-/* Send the signal SIGNUM to process PID, using kill ().  If an error
-   occurs, it is reported and passed through to the caller.  */
 static int
-send_signal (pid_t pid, int signum)
+send_signals (int signum, char *const *argv)
 {
-  int err;
+  int status = EXIT_SUCCESS;
+  char const *arg = *argv;
 
-  err = kill (pid, signum);
-  if (err)
+  if (! arg)
     {
-      uintmax_t nr = (uintmax_t) (pid < 0 ? -pid : pid);
-      char number[LONGEST_HUMAN_READABLE + 1];
+      error (0, 0, _("missing operand after `%s'"), argv[-1]);
+      usage (EXIT_FAILURE);
+    }
 
-      error (0, errno, "(%s%s)", (pid < 0 ? "-" : ""),
-            human_readable (nr, number, 1, 1));
+  do
+    {
+      char *endp;
+      intmax_t n = (errno = 0, strtoimax (arg, &endp, 10));
+      pid_t pid = n;
+
+      if (errno == ERANGE || pid != n || arg == endp || *endp)
+       {
+         error (0, 0, _("%s: invalid process id"), arg);
+         status = EXIT_FAILURE;
+       }
+      else if (kill (pid, signum) != 0)
+       {
+         error (0, errno, "%s", arg);
+         status = EXIT_FAILURE;
+       }
     }
-  return err;
-}
+  while ((arg = *++argv));
 
+  return status;
+}
 \f
 int
 main (int argc, char **argv)
 {
   int optc;
-  int err = 0;
-  int success = 0;
-  list_t list = LIST_NONE;
-  int sig_num = NO_SIG;
-  int i;
-  char **extra_opt;
-  int extra_opt_size = 0;
+  bool list = false;
+  bool table = false;
+  int signum = -1;
+  char signame[SIG2STR_MAX];
 
   program_name = argv[0];
   setlocale (LC_ALL, "");
@@ -607,162 +300,74 @@ main (int argc, char **argv)
 
   atexit (close_stdout);
 
-  initialize ();
-
-  extra_opt = xmalloc ((argc - 1) * sizeof (char *));
-  for (i = 1; i < argc; i++)
-    {
-      intmax_t dummy;
-
-      /* getopt will ignore everything following `--', so we do as
-         well.  */
-      if (!strcmp ("--", argv[i]))
-       break;
-
-      /* Skip this argument if it doesn't look like an option.  */
-      if (argv[i][0] != '-')
-       continue;
-
-      /* Skip it if it follows an option requiring an argument.  */
-      if (i > 1 && argv[i-1][0] == '-')
-       {
-         /* A short option that doesn't look like -nTERM.  */
-         if ((argv[i-1][1] == 'n' || argv[i-1][1] == 's')
-             && argv[i-1][2] == '\0')
-           continue;
-
-         /* A long option (not `--', which was excluded above).  */
-         if (argv[i-1][1] == '-'
-             && (!strncmp (OPT_SIGNUM_LONG, &(argv[i-1][2]),
-                           strlen (&argv[i-1][2]))
-                 || !strncmp (OPT_SIGSPEC_LONG, &(argv[i-1][2]),
-                              strlen (&argv[i-1][2]))))
-           continue;
-       }
-
-      /* At this point we know that this argument is not argument to
-        another option, and that it starts with `-' but is not `--'.  */
-
-      if (decode_signal (&(argv[i][1])) != NO_SIG
-         || xstrtoimax (argv[i], NULL, 10, &dummy, "") == LONGINT_OK)
-       {
-         /* It is either a valid signal specifier or potentially a
-            valid process group.  So remember it and make getopt not
-            care about it.  */
-         extra_opt[extra_opt_size++] = argv[i];
-         argv[i][0] = 'X';
-       }
-    }
-
-  while ((optc = getopt_long (argc, argv, "s:n:lL", long_options, NULL))
+  while ((optc = getopt_long (argc, argv, short_options, long_options, NULL))
         != -1)
     switch (optc)
       {
+      case '0': case '1': case '2': case '3': case '4':
+      case '5': case '6': case '7': case '8': case '9':
+       if (optind != 2)
+         {
+           /* This option is actually a process-id.  */
+           optind--;
+           goto no_more_options;
+         }
+       /* Fall through.  */
+      case 'A': case 'B': case 'C': case 'D': case 'E':
+      case 'F': case 'G': case 'H': case 'I': case 'J':
+      case 'K': case 'L': case 'M': case 'N': case 'O':
+      case 'P': case 'Q': case 'R': case 'S': case 'T':
+      case 'U': case 'V': case 'W': case 'X': case 'Y':
+      case 'Z':
+       if (! optarg)
+         optarg = argv[optind - 1] + strlen (argv[optind - 1]);
+       if (optarg != argv[optind - 1] + 2)
+         {
+           error (0, 0, _("invalid option -- %c"), optc);
+           usage (EXIT_FAILURE);
+         }
+       optarg--;
+       /* Fall through.  */
+      case 'n': /* -n is not documented, but is for Bash compatibility.  */
       case 's':
-      case 'n':
-       if (sig_num != NO_SIG)
+       if (0 <= signum)
          {
-           error (0, 0, _("%s: only one signal specififier allowed"), optarg);
-           usage (1);
+           error (0, 0, _("%s: multiple signals specified"), optarg);
+           usage (EXIT_FAILURE);
          }
-       sig_num = decode_signal (optarg);
-       if (sig_num == NO_SIG)
-         error (1, 0, _("%s: invalid signal specifier"), optarg);
+       signum = operand2sig (optarg, signame);
+       if (signum < 0)
+         usage (EXIT_FAILURE);
        break;
+
+      case 't':
+       table = true;
+       /* Fall through.  */
       case 'l':
-       list = LIST_FLAT;
-       break;
-      case 'L':
-       list = LIST_PRETTY;
+       if (list)
+         {
+           error (0, 0, _("multiple -l or -t options specified"));
+           usage (EXIT_FAILURE);
+         }
+       list = true;
        break;
+
       case_GETOPT_HELP_CHAR;
       case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
       default:
-       usage (1);
+       usage (EXIT_FAILURE);
       }
+ no_more_options:;
 
-  argc -= optind;
-  argv += optind;
-
-  for (i = 0; i < extra_opt_size; i++)
-    extra_opt[i][0] = '-';
-
-  if (extra_opt_size > 0 && sig_num == NO_SIG)
-    {
-      char **arg = argv;
-
-      /* Find the first extra option collected in the remaining
-        argument list and if necessary, replace it with the first
-        remaining argument.  This is a precaution in case getopt()
-        mangles the order of non-option arguments.  */
-
-      while (*arg && *arg != extra_opt[0])
-       arg++;
-      if (*arg && *arg != argv[0])
-       *arg = argv[0];
-
-      argc--;
-      argv++;
-
-      /* Interpret the first one as a signal specifier.  */
-      sig_num = decode_signal (&(extra_opt[0][1]));
-      if (sig_num == NO_SIG)
-       error (1, 0, _("%s: invalid signal specifier"), extra_opt[0]);
-    }
-
-  if (!list && sig_num == NO_SIG)
-    sig_num = SIGTERM;
-
-  if (argc == 0)
-    {
-      if (list)
-       list_signals (list);
-      else
-       {
-         error (0, 0, _("too few arguments"));
-         usage (1);
-       }
-    }
-  else
+  if (signum < 0)
+    signum = SIGTERM;
+  else if (list)
     {
-      int j;
-      for (j = 0; j < argc; j++)
-       if (list)
-         {
-           int signum = decode_signal (argv[j]);
-           if (signum == NO_SIG)
-             {
-               error (0, 0, _("%s: invalid signal specifier"), argv[j]);
-               err = 1;
-             }
-           else
-             {
-               char const *const name = name_signal (signum);
-               printf ("%s\n", name ? name : "(unknown)");
-               success = 1;
-             }
-         }
-       else
-         {
-           intmax_t nr;
-           pid_t pid;
-           int inval = 0;
-
-           if (xstrtoimax (argv[j], NULL, 10, &nr, "") != LONGINT_OK)
-             inval = 1;
-           pid = (pid_t) nr;
-           if (inval || pid != nr)
-             {
-               error (0, 0, _("%s: invalid process id"), argv[j]);
-               err = 1;
-               continue;
-             }
-           if (send_signal (pid, sig_num))
-             err = 1;
-           else
-             success = 1;
-         }
+      error (0, 0, _("cannot combine signal with -l or -t"));
+      usage (EXIT_FAILURE);
     }
 
-  exit ((success || !err) ? 0 : 1);
+  return (list
+         ? list_signals (table, optind == argc ? NULL : argv + optind)
+         : send_signals (signum, argv + optind));
 }