Don't include headers already included by system.h:
[platform/upstream/coreutils.git] / src / who.c
1 /* GNU's who.
2    Copyright (C) 1992-2003 Free Software Foundation, Inc.
3
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 2, or (at your option)
7    any later version.
8
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.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software Foundation,
16    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
17
18 /* Written by jla; revised by djm; revised again by mstone */
19
20 /* Output format:
21    name [state] line time [activity] [pid] [comment] [exit]
22    state: -T
23    name, line, time: not -q
24    idle: -u
25 */
26
27 #include <config.h>
28 #include <getopt.h>
29 #include <stdio.h>
30
31 #include <sys/types.h>
32 #include "system.h"
33
34 #include "readutmp.h"
35 #include "error.h"
36
37 /* The official name of this program (e.g., no `g' prefix).  */
38 #define PROGRAM_NAME "who"
39
40 #define AUTHORS N_ ("Joseph Arceneaux, David MacKenzie, and Michael Stone")
41
42 #ifndef MAXHOSTNAMELEN
43 # define MAXHOSTNAMELEN 64
44 #endif
45
46 #ifndef S_IWGRP
47 # define S_IWGRP 020
48 #endif
49
50 #ifndef USER_PROCESS
51 # define USER_PROCESS INT_MAX
52 #endif
53
54 #ifndef RUN_LVL
55 # define RUN_LVL INT_MAX
56 #endif
57
58 #ifndef INIT_PROCESS
59 # define INIT_PROCESS INT_MAX
60 #endif
61
62 #ifndef LOGIN_PROCESS
63 # define LOGIN_PROCESS INT_MAX
64 #endif
65
66 #ifndef DEAD_PROCESS
67 # define DEAD_PROCESS INT_MAX
68 #endif
69
70 #ifndef BOOT_TIME
71 # define BOOT_TIME 0
72 #endif
73
74 #ifndef NEW_TIME
75 # define NEW_TIME 0
76 #endif
77
78 #define IDLESTR_LEN 6
79
80 #if HAVE_STRUCT_XTMP_UT_PID
81 # define UT_PID(U) ((U)->ut_pid)
82 # define PIDSTR_DECL_AND_INIT(Var, Utmp_ent) \
83   char Var[INT_STRLEN_BOUND (Utmp_ent->ut_pid) + 1]; \
84   sprintf (Var, "%d", (int) (Utmp_ent->ut_pid))
85 #else
86 # define UT_PID(U) 0
87 # define PIDSTR_DECL_AND_INIT(Var, Utmp_ent) \
88   const char *Var = ""
89 #endif
90
91 #if HAVE_STRUCT_XTMP_UT_ID
92 # define UT_ID(U) ((U)->ut_id)
93 #else
94   /* Of course, sizeof "whatever" is the size of a pointer (often 4),
95      but that's ok, since the actual string has a length of only 2.  */
96 # define UT_ID(U) "??"
97 #endif
98
99 #define UT_TYPE_UNDEF 255
100
101 #if HAVE_STRUCT_XTMP_UT_TYPE
102 # define UT_TYPE(U) ((U)->ut_type)
103 #else
104 # define UT_TYPE(U) UT_TYPE_UNDEF
105 #endif
106
107 #define IS_USER_PROCESS(U)                      \
108   (UT_USER (utmp_buf)[0]                        \
109    && (UT_TYPE (utmp_buf) == USER_PROCESS       \
110        || (UT_TYPE (utmp_buf) == UT_TYPE_UNDEF  \
111            && UT_TIME_MEMBER (utmp_buf) != 0)))
112
113 int gethostname ();
114 char *ttyname ();
115 char *canon_host ();
116
117 /* The name this program was run with. */
118 char *program_name;
119
120 /* If nonzero, attempt to canonicalize hostnames via a DNS lookup. */
121 static int do_lookup;
122
123 /* If nonzero, display only a list of usernames and count of
124    the users logged on.
125    Ignored for `who am i'. */
126 static int short_list;
127
128 /* If nonzero, display only name, line, and time fields */
129 static int short_output;
130
131 /* If nonzero, display the hours:minutes since each user has touched
132    the keyboard, or "." if within the last minute, or "old" if
133    not within the last day. */
134 static int include_idle;
135
136 /* If nonzero, display a line at the top describing each field. */
137 static int include_heading;
138
139 /* If nonzero, display a `+' for each user if mesg y, a `-' if mesg n,
140    or a `?' if their tty cannot be statted. */
141 static int include_mesg;
142
143 /* If nonzero, display process termination & exit status */
144 static int include_exit;
145
146 /* If nonzero, display the last boot time */
147 static int need_boottime;
148
149 /* If nonzero, display dead processes */
150 static int need_deadprocs;
151
152 /* If nonzero, display processes waiting for user login */
153 static int need_login;
154
155 /* If nonzero, display processes started by init */
156 static int need_initspawn;
157
158 /* If nonzero, display the last clock change */
159 static int need_clockchange;
160
161 /* If nonzero, display the current runlevel */
162 static int need_runlevel;
163
164 /* If nonzero, display user processes */
165 static int need_users;
166
167 /* If nonzero, display info only for the controlling tty */
168 static int my_line_only;
169
170 /* for long options with no corresponding short option, use enum */
171 enum
172 {
173   LOOKUP_OPTION = CHAR_MAX + 1,
174   LOGIN_OPTION
175 };
176
177 static struct option const longopts[] = {
178   {"all", no_argument, NULL, 'a'},
179   {"boot", no_argument, NULL, 'b'},
180   {"count", no_argument, NULL, 'q'},
181   {"dead", no_argument, NULL, 'd'},
182   {"heading", no_argument, NULL, 'H'},
183   {"idle", no_argument, NULL, 'i'},
184   {"login", no_argument, NULL, LOGIN_OPTION},
185   {"lookup", no_argument, NULL, LOOKUP_OPTION},
186   {"message", no_argument, NULL, 'T'},
187   {"mesg", no_argument, NULL, 'T'},
188   {"process", no_argument, NULL, 'p'},
189   {"runlevel", no_argument, NULL, 'r'},
190   {"short", no_argument, NULL, 's'},
191   {"time", no_argument, NULL, 't'},
192   {"users", no_argument, NULL, 'u'},
193   {"writable", no_argument, NULL, 'T'},
194   {GETOPT_HELP_OPTION_DECL},
195   {GETOPT_VERSION_OPTION_DECL},
196   {NULL, 0, NULL, 0}
197 };
198
199 /* Return a string representing the time between WHEN and the time
200    that this function is first run.
201    FIXME: locale? */
202 static const char *
203 idle_string (time_t when)
204 {
205   static time_t now = 0;
206   static char idle_hhmm[IDLESTR_LEN];
207   time_t seconds_idle;
208
209   if (now == 0)
210     time (&now);
211
212   seconds_idle = now - when;
213   if (seconds_idle < 60)        /* One minute. */
214     return "  .  ";
215   if (seconds_idle < (24 * 60 * 60))    /* One day. */
216     {
217       sprintf (idle_hhmm, "%02d:%02d",
218                (int) (seconds_idle / (60 * 60)),
219                (int) ((seconds_idle % (60 * 60)) / 60));
220       return (const char *) idle_hhmm;
221     }
222   return _(" old ");
223 }
224
225 /* Return a standard time string, "mon dd hh:mm"
226    FIXME: handle localization */
227 static const char *
228 time_string (const STRUCT_UTMP *utmp_ent)
229 {
230   /* Don't take the address of UT_TIME_MEMBER directly.
231      Ulrich Drepper wrote:
232      ``... GNU libc (and perhaps other libcs as well) have extended
233      utmp file formats which do not use a simple time_t ut_time field.
234      In glibc, ut_time is a macro which selects for backward compatibility
235      the tv_sec member of a struct timeval value.''  */
236   time_t tm = UT_TIME_MEMBER (utmp_ent);
237
238   char *ptr = ctime (&tm) + 4;
239   ptr[12] = '\0';
240   return ptr;
241 }
242
243 /* Print formatted output line. Uses mostly arbitrary field sizes, probably
244    will need tweaking if any of the localization stuff is done, or for 64 bit
245    pids, etc. */
246 static void
247 print_line (const char *user, const char state, const char *line,
248             const char *time_str, const char *idle, const char *pid,
249             const char *comment, const char *exitstr)
250 {
251   static char mesg[3] = { ' ', 'x', '\0' };
252   char *buf;
253   char x_idle[1 + IDLESTR_LEN + 1];
254   char x_pid[1 + INT_STRLEN_BOUND (pid_t) + 1];
255   char *x_exitstr;
256   int err;
257
258   mesg[1] = state;
259
260   if (include_idle && !short_output)
261     sprintf (x_idle, " %-6s", idle);
262   else
263     *x_idle = '\0';
264
265   if (!short_output)
266     sprintf (x_pid, " %10s", pid);
267   else
268     *x_pid = '\0';
269
270   x_exitstr = xmalloc (include_exit ? 1 + MAX (12, strlen (exitstr)) + 1 : 1);
271   if (include_exit)
272     sprintf (x_exitstr, " %-12s", exitstr);
273   else
274     *x_exitstr = '\0';
275
276   err = asprintf (&buf,
277                   "%-8.8s"
278                   "%s"
279                   " %-12s"
280                   " %-12s"
281                   "%s"
282                   "%s"
283                   " %-8s"
284                   "%s"
285                   ,
286                   user ? user : "   .",
287                   include_mesg ? mesg : "",
288                   line,
289                   time_str,
290                   x_idle,
291                   x_pid,
292                   /* FIXME: it's not really clear whether the following
293                      field should be in the short_output.  A strict reading
294                      of SUSv2 would suggest not, but I haven't seen any
295                      implementations that actually work that way... */
296                   comment,
297                   x_exitstr
298                   );
299   if (err == -1)
300     xalloc_die ();
301
302   {
303     /* Remove any trailing spaces.  */
304     char *p = buf + strlen (buf);
305     while (*--p == ' ')
306       /* empty */;
307     *(p + 1) = '\0';
308   }
309
310   puts (buf);
311   free (buf);
312   free (x_exitstr);
313 }
314
315 /* Send properly parsed USER_PROCESS info to print_line */
316 static void
317 print_user (const STRUCT_UTMP *utmp_ent)
318 {
319   struct stat stats;
320   time_t last_change;
321   char mesg;
322   char idlestr[IDLESTR_LEN];
323   static char *hoststr;
324   static size_t hostlen;
325
326 #define DEV_DIR_WITH_TRAILING_SLASH "/dev/"
327 #define DEV_DIR_LEN (sizeof (DEV_DIR_WITH_TRAILING_SLASH) - 1)
328
329   char line[sizeof (utmp_ent->ut_line) + DEV_DIR_LEN + 1];
330   PIDSTR_DECL_AND_INIT (pidstr, utmp_ent);
331
332   /* Copy ut_line into LINE, prepending `/dev/' if ut_line is not
333      already an absolute pathname.  Some system may put the full,
334      absolute pathname in ut_line.  */
335   if (utmp_ent->ut_line[0] == '/')
336     {
337       strncpy (line, utmp_ent->ut_line, sizeof (utmp_ent->ut_line));
338       line[sizeof (utmp_ent->ut_line)] = '\0';
339     }
340   else
341     {
342       strcpy (line, DEV_DIR_WITH_TRAILING_SLASH);
343       strncpy (line + DEV_DIR_LEN, utmp_ent->ut_line,
344                sizeof (utmp_ent->ut_line));
345       line[DEV_DIR_LEN + sizeof (utmp_ent->ut_line)] = '\0';
346     }
347
348   if (stat (line, &stats) == 0)
349     {
350       mesg = (stats.st_mode & S_IWGRP) ? '+' : '-';
351       last_change = stats.st_atime;
352     }
353   else
354     {
355       mesg = '?';
356       last_change = 0;
357     }
358
359   if (last_change)
360     sprintf (idlestr, "%.6s", idle_string (last_change));
361   else
362     sprintf (idlestr, "  ?");
363
364 #if HAVE_UT_HOST
365   if (utmp_ent->ut_host[0])
366     {
367       char ut_host[sizeof (utmp_ent->ut_host) + 1];
368       char *host = 0, *display = 0;
369
370       /* Copy the host name into UT_HOST, and ensure it's nul terminated. */
371       strncpy (ut_host, utmp_ent->ut_host, (int) sizeof (utmp_ent->ut_host));
372       ut_host[sizeof (utmp_ent->ut_host)] = '\0';
373
374       /* Look for an X display.  */
375       display = strrchr (ut_host, ':');
376       if (display)
377         *display++ = '\0';
378
379       if (*ut_host && do_lookup)
380         {
381           /* See if we can canonicalize it.  */
382           host = canon_host (ut_host);
383         }
384
385       if (! host)
386         host = ut_host;
387
388       if (display)
389         {
390           if (hostlen < strlen (host) + strlen (display) + 4)
391             {
392               hostlen = strlen (host) + strlen (display) + 4;
393               hoststr = (char *) realloc (hoststr, hostlen);
394             }
395           sprintf (hoststr, "(%s:%s)", host, display);
396         }
397       else
398         {
399           if (hostlen < strlen (host) + 3)
400             {
401               hostlen = strlen (host) + 3;
402               hoststr = (char *) realloc (hoststr, hostlen);
403             }
404           sprintf (hoststr, "(%s)", host);
405         }
406     }
407   else
408     {
409       if (hostlen < 1)
410         {
411           hostlen = 1;
412           hoststr = (char *) realloc (hoststr, hostlen);
413         }
414       stpcpy (hoststr, "");
415     }
416 #endif
417
418   print_line (UT_USER (utmp_ent), mesg, utmp_ent->ut_line,
419               time_string (utmp_ent), idlestr, pidstr,
420               hoststr ? hoststr : "", "");
421 }
422
423 static void
424 print_boottime (const STRUCT_UTMP *utmp_ent)
425 {
426   print_line ("", ' ', "system boot", time_string (utmp_ent), "", "", "", "");
427 }
428
429 static char *
430 make_id_equals_comment (STRUCT_UTMP const *utmp_ent)
431 {
432   char *comment = xmalloc (strlen (_("id=")) + sizeof UT_ID (utmp_ent) + 1);
433
434   /* Cast field width argument to `int' to avoid warning from gcc.  */
435   sprintf (comment, "%s%.*s", _("id="), (int) sizeof UT_ID (utmp_ent),
436            UT_ID (utmp_ent));
437   return comment;
438 }
439
440 static void
441 print_deadprocs (const STRUCT_UTMP *utmp_ent)
442 {
443   static char *exitstr;
444   char *comment = make_id_equals_comment (utmp_ent);
445   PIDSTR_DECL_AND_INIT (pidstr, utmp_ent);
446
447   if (!exitstr)
448     exitstr = xmalloc (strlen (_("term="))
449                        + INT_STRLEN_BOUND (UT_EXIT_E_TERMINATION (utmp_ent)) + 1
450                        + strlen (_("exit="))
451                        + INT_STRLEN_BOUND (UT_EXIT_E_EXIT (utmp_ent))
452                        + 1);
453   sprintf (exitstr, "%s%d %s%d", _("term="), UT_EXIT_E_TERMINATION (utmp_ent),
454            _("exit="), UT_EXIT_E_EXIT (utmp_ent));
455
456   /* FIXME: add idle time? */
457
458   print_line ("", ' ', utmp_ent->ut_line,
459               time_string (utmp_ent), "", pidstr, comment, exitstr);
460   free (comment);
461 }
462
463 static void
464 print_login (const STRUCT_UTMP *utmp_ent)
465 {
466   char *comment = make_id_equals_comment (utmp_ent);
467   PIDSTR_DECL_AND_INIT (pidstr, utmp_ent);
468
469   /* FIXME: add idle time? */
470
471   print_line ("LOGIN", ' ', utmp_ent->ut_line,
472               time_string (utmp_ent), "", pidstr, comment, "");
473   free (comment);
474 }
475
476 static void
477 print_initspawn (const STRUCT_UTMP *utmp_ent)
478 {
479   char *comment = make_id_equals_comment (utmp_ent);
480   PIDSTR_DECL_AND_INIT (pidstr, utmp_ent);
481
482   print_line ("", ' ', utmp_ent->ut_line,
483               time_string (utmp_ent), "", pidstr, comment, "");
484   free (comment);
485 }
486
487 static void
488 print_clockchange (const STRUCT_UTMP *utmp_ent)
489 {
490   /* FIXME: handle NEW_TIME & OLD_TIME both */
491   print_line ("", ' ', _("clock change"),
492               time_string (utmp_ent), "", "", "", "");
493 }
494
495 static void
496 print_runlevel (const STRUCT_UTMP *utmp_ent)
497 {
498   static char *runlevline, *comment;
499   int last = UT_PID (utmp_ent) / 256;
500   int curr = UT_PID (utmp_ent) % 256;
501
502   if (!runlevline)
503     runlevline = xmalloc (strlen (_("run-level")) + 3);
504   sprintf (runlevline, "%s %c", _("run-level"), curr);
505
506   if (!comment)
507     comment = xmalloc (strlen (_("last=")) + 2);
508   sprintf (comment, "%s%c", _("last="), (last == 'N') ? 'S' : last);
509
510   print_line ("", ' ', runlevline, time_string (utmp_ent),
511               "", "", comment, "");
512
513   return;
514 }
515
516 /* Print the username of each valid entry and the number of valid entries
517    in UTMP_BUF, which should have N elements. */
518 static void
519 list_entries_who (int n, const STRUCT_UTMP *utmp_buf)
520 {
521   int entries = 0;
522
523   while (n--)
524     {
525       if (UT_USER (utmp_buf)[0] && UT_TYPE (utmp_buf) == USER_PROCESS)
526         {
527           char *trimmed_name;
528
529           trimmed_name = extract_trimmed_name (utmp_buf);
530
531           printf ("%s ", trimmed_name);
532           free (trimmed_name);
533           entries++;
534         }
535       utmp_buf++;
536     }
537   printf (_("\n# users=%u\n"), entries);
538 }
539
540 static void
541 print_heading (void)
542 {
543   print_line (_("NAME"), ' ', _("LINE"), _("TIME"), _("IDLE"), _("PID"),
544               _("COMMENT"), _("EXIT"));
545 }
546
547 /* Display UTMP_BUF, which should have N entries. */
548 static void
549 scan_entries (int n, const STRUCT_UTMP *utmp_buf)
550 {
551   char *ttyname_b IF_LINT ( = NULL);
552
553   if (include_heading)
554     print_heading ();
555
556   if (my_line_only)
557     {
558       ttyname_b = ttyname (0);
559       if (!ttyname_b)
560         return;
561       if (strncmp (ttyname_b, DEV_DIR_WITH_TRAILING_SLASH, DEV_DIR_LEN) == 0)
562         ttyname_b += DEV_DIR_LEN;       /* Discard /dev/ prefix.  */
563     }
564
565   while (n--)
566     {
567       if (!my_line_only ||
568           strncmp (ttyname_b, utmp_buf->ut_line,
569                    sizeof (utmp_buf->ut_line)) == 0)
570         {
571           if (need_users && IS_USER_PROCESS (utmp_buf))
572             print_user (utmp_buf);
573           else if (need_runlevel && UT_TYPE (utmp_buf) == RUN_LVL)
574             print_runlevel (utmp_buf);
575           else if (need_boottime && UT_TYPE (utmp_buf) == BOOT_TIME)
576             print_boottime (utmp_buf);
577           /* I've never seen one of these, so I don't know what it should
578              look like :^)
579              FIXME: handle OLD_TIME also, perhaps show the delta? */
580           else if (need_clockchange && UT_TYPE (utmp_buf) == NEW_TIME)
581             print_clockchange (utmp_buf);
582           else if (need_initspawn && UT_TYPE (utmp_buf) == INIT_PROCESS)
583             print_initspawn (utmp_buf);
584           else if (need_login && UT_TYPE (utmp_buf) == LOGIN_PROCESS)
585             print_login (utmp_buf);
586           else if (need_deadprocs && UT_TYPE (utmp_buf) == DEAD_PROCESS)
587             print_deadprocs (utmp_buf);
588         }
589
590       utmp_buf++;
591     }
592 }
593
594 /* Display a list of who is on the system, according to utmp file filename. */
595 static void
596 who (const char *filename)
597 {
598   int n_users;
599   STRUCT_UTMP *utmp_buf;
600   int fail = read_utmp (filename, &n_users, &utmp_buf);
601
602   if (fail)
603     error (EXIT_FAILURE, errno, "%s", filename);
604
605   if (short_list)
606     list_entries_who (n_users, utmp_buf);
607   else
608     scan_entries (n_users, utmp_buf);
609 }
610
611 void
612 usage (int status)
613 {
614   if (status != 0)
615     fprintf (stderr, _("Try `%s --help' for more information.\n"),
616              program_name);
617   else
618     {
619       printf (_("Usage: %s [OPTION]... [ FILE | ARG1 ARG2 ]\n"), program_name);
620       fputs (_("\
621 \n\
622   -a, --all         same as -b -d --login -p -r -t -T -u\n\
623   -b, --boot        time of last system boot\n\
624   -d, --dead        print dead processes\n\
625   -H, --heading     print line of column headings\n\
626 "), stdout);
627       fputs (_("\
628   -i, --idle        add idle time as HOURS:MINUTES, . or old\n\
629                     (deprecated, use -u)\n\
630       --login       print system login processes\n\
631                     (equivalent to SUS -l)\n\
632 "), stdout);
633       fputs (_("\
634   -l, --lookup      attempt to canonicalize hostnames via DNS\n\
635                     (-l is deprecated, use --lookup)\n\
636   -m                only hostname and user associated with stdin\n\
637   -p, --process     print active processes spawned by init\n\
638 "), stdout);
639       fputs (_("\
640   -q, --count       all login names and number of users logged on\n\
641   -r, --runlevel    print current runlevel\n\
642   -s, --short       print only name, line, and time (default)\n\
643   -t, --time        print last system clock change\n\
644 "), stdout);
645       fputs (_("\
646   -T, -w, --mesg    add user's message status as +, - or ?\n\
647   -u, --users       list users logged in\n\
648       --message     same as -T\n\
649       --writable    same as -T\n\
650 "), stdout);
651       fputs (HELP_OPTION_DESCRIPTION, stdout);
652       fputs (VERSION_OPTION_DESCRIPTION, stdout);
653       printf (_("\
654 \n\
655 If FILE is not specified, use %s.  %s as FILE is common.\n\
656 If ARG1 ARG2 given, -m presumed: `am i' or `mom likes' are usual.\n\
657 "), UTMP_FILE, WTMP_FILE);
658       printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
659     }
660   exit (status);
661 }
662
663 int
664 main (int argc, char **argv)
665 {
666   int optc, longind;
667   int assumptions = 1;
668
669   initialize_main (&argc, &argv);
670   program_name = argv[0];
671   setlocale (LC_ALL, "");
672   bindtextdomain (PACKAGE, LOCALEDIR);
673   textdomain (PACKAGE);
674
675   atexit (close_stdout);
676
677   while ((optc = getopt_long (argc, argv, "abdilmpqrstuwHT", longopts,
678                               &longind)) != -1)
679     {
680       switch (optc)
681         {
682         case 0:
683           break;
684
685         case 'a':
686           need_boottime = 1;
687           need_deadprocs = 1;
688           need_login = 1;
689           need_initspawn = 1;
690           need_runlevel = 1;
691           need_clockchange = 1;
692           need_users = 1;
693           include_mesg = 1;
694           include_idle = 1;
695           include_exit = 1;
696           assumptions = 0;
697           break;
698
699         case 'b':
700           need_boottime = 1;
701           assumptions = 0;
702           break;
703
704         case 'd':
705           need_deadprocs = 1;
706           include_idle = 1;
707           include_exit = 1;
708           assumptions = 0;
709           break;
710
711         case 'H':
712           include_heading = 1;
713           break;
714
715           /* FIXME: This should be -l in a future version */
716         case LOGIN_OPTION:
717           need_login = 1;
718           include_idle = 1;
719           assumptions = 0;
720           break;
721
722         case 'm':
723           my_line_only = 1;
724           break;
725
726         case 'p':
727           need_initspawn = 1;
728           assumptions = 0;
729           break;
730
731         case 'q':
732           short_list = 1;
733           break;
734
735         case 'r':
736           need_runlevel = 1;
737           include_idle = 1;
738           assumptions = 0;
739           break;
740
741         case 's':
742           short_output = 1;
743           break;
744
745         case 't':
746           need_clockchange = 1;
747           assumptions = 0;
748           break;
749
750         case 'T':
751         case 'w':
752           include_mesg = 1;
753           break;
754
755         case 'i':
756           error (0, 0,
757                  _("Warning: -i will be removed in a future release; \
758   use -u instead"));
759           /* Fall through.  */
760         case 'u':
761           need_users = 1;
762           include_idle = 1;
763           assumptions = 0;
764           break;
765
766         case 'l':
767           error (0, 0,
768                  _("Warning: the meaning of '-l' will change in a future\
769  release to conform to POSIX"));
770         case LOOKUP_OPTION:
771           do_lookup = 1;
772           break;
773
774           case_GETOPT_HELP_CHAR;
775
776           case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
777
778         default:
779           usage (EXIT_FAILURE);
780         }
781     }
782
783   if (assumptions)
784     {
785       need_users = 1;
786       short_output = 1;
787     }
788
789   if (include_exit)
790     {
791       short_output = 0;
792     }
793
794   switch (argc - optind)
795     {
796     case -1:
797     case 0:                     /* who */
798       who (UTMP_FILE);
799       break;
800
801     case 1:                     /* who <utmp file> */
802       who (argv[optind]);
803       break;
804
805     case 2:                     /* who <blurf> <glop> */
806       my_line_only = 1;
807       who (UTMP_FILE);
808       break;
809
810     default:                    /* lose */
811       error (0, 0, _("too many arguments"));
812       usage (EXIT_FAILURE);
813     }
814
815   exit (EXIT_SUCCESS);
816 }