/* GNU's who.                                                                   This is the who utility
   Copyright (C) 1992-2018 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         
   the Free Software Foundation, either version 3 of the License, or            
   (at your option) any later version.                                          
                                                                                
   This program is distributed in the hope that it will be useful,              
   but WITHOUT ANY WARRANTY; without even the implied warranty of               
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                
   GNU General Public License for more details.                                 
                                                                                
   You should have received a copy of the GNU General Public License            
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */   The GNUv3 license
                                                                                
/* Written by jla; revised by djm; revised again by mstone */                   
                                                                                
/* Output format:                                                               
   name [state] line time [activity] [pid] [comment] [exit]                     
   state: -T                                                                    
   name, line, time: not -q                                                     
   idle: -u                                                                     
*/                                                                              
                                                                                
#include <config.h>                                                             Provides system specific information
#include <getopt.h>                                                             ...!includes auto-comment...
#include <stdio.h>                                                              Provides standard I/O capability
#include <assert.h>                                                             ...!includes auto-comment...
                                                                                
#include <sys/types.h>                                                          Provides system data types
#include "system.h"                                                             ...!includes auto-comment...
                                                                                
#include "c-ctype.h"                                                            ...!includes auto-comment...
#include "canon-host.h"                                                         ...!includes auto-comment...
#include "readutmp.h"                                                           ...!includes auto-comment...
#include "die.h"                                                                ...!includes auto-comment...
#include "error.h"                                                              ...!includes auto-comment...
#include "hard-locale.h"                                                        ...!includes auto-comment...
#include "quote.h"                                                              ...!includes auto-comment...
                                                                                
#ifdef TTY_GROUP_NAME                                                           Line 42
# include <grp.h>                                                               ...!includes auto-comment...
#endif                                                                          Line 44
                                                                                
/* The official name of this program (e.g., no 'g' prefix).  */                 
#define PROGRAM_NAME "who"                                                      Line 47
                                                                                
#define AUTHORS \                                                               Line 49
  proper_name ("Joseph Arceneaux"), \                                           Line 50
  proper_name ("David MacKenzie"), \                                            Line 51
  proper_name ("Michael Stone")                                                 Line 52
                                                                                
#ifdef RUN_LVL                                                                  Line 54
# define UT_TYPE_RUN_LVL(U) UT_TYPE_EQ (U, RUN_LVL)                             Line 55
#else                                                                           Line 56
# define UT_TYPE_RUN_LVL(U) false                                               Line 57
#endif                                                                          Line 58
                                                                                
#ifdef INIT_PROCESS                                                             Line 60
# define UT_TYPE_INIT_PROCESS(U) UT_TYPE_EQ (U, INIT_PROCESS)                   Line 61
#else                                                                           Line 62
# define UT_TYPE_INIT_PROCESS(U) false                                          Line 63
#endif                                                                          Line 64
                                                                                
#ifdef LOGIN_PROCESS                                                            Line 66
# define UT_TYPE_LOGIN_PROCESS(U) UT_TYPE_EQ (U, LOGIN_PROCESS)                 Line 67
#else                                                                           Line 68
# define UT_TYPE_LOGIN_PROCESS(U) false                                         Line 69
#endif                                                                          Line 70
                                                                                
#ifdef DEAD_PROCESS                                                             Line 72
# define UT_TYPE_DEAD_PROCESS(U) UT_TYPE_EQ (U, DEAD_PROCESS)                   Line 73
#else                                                                           Line 74
# define UT_TYPE_DEAD_PROCESS(U) false                                          Line 75
#endif                                                                          Line 76
                                                                                
#ifdef NEW_TIME                                                                 Line 78
# define UT_TYPE_NEW_TIME(U) UT_TYPE_EQ (U, NEW_TIME)                           Line 79
#else                                                                           Line 80
# define UT_TYPE_NEW_TIME(U) false                                              Line 81
#endif                                                                          Line 82
                                                                                
#define IDLESTR_LEN 6                                                           Line 84
                                                                                
#if HAVE_STRUCT_XTMP_UT_PID                                                     Line 86
# define PIDSTR_DECL_AND_INIT(Var, Utmp_ent) \                                  Line 87
  char Var[INT_STRLEN_BOUND (Utmp_ent->ut_pid) + 1]; \                          Line 88
  sprintf (Var, "%ld", (long int) (Utmp_ent->ut_pid))                           Line 89
#else                                                                           Line 90
# define PIDSTR_DECL_AND_INIT(Var, Utmp_ent) \                                  Line 91
  const char *Var = ""                                                          Line 92
#endif                                                                          Line 93
                                                                                
#if HAVE_STRUCT_XTMP_UT_ID                                                      Line 95
# define UT_ID(U) ((U)->ut_id)                                                  Line 96
#else                                                                           Line 97
# define UT_ID(U) "??"                                                          Line 98
#endif                                                                          Line 99
                                                                                
/* If true, attempt to canonicalize hostnames via a DNS lookup. */              
static bool do_lookup;                                                          Line 102
                                                                                
/* If true, display only a list of usernames and count of                       
   the users logged on.                                                         
   Ignored for 'who am i'.  */                                                  
static bool short_list;                                                         Line 107
                                                                                
/* If true, display only name, line, and time fields.  */                       
static bool short_output;                                                       Line 110
                                                                                
/* If true, display the hours:minutes since each user has touched               
   the keyboard, or "." if within the last minute, or "old" if                  
   not within the last day.  */                                                 
static bool include_idle;                                                       Line 115
                                                                                
/* If true, display a line at the top describing each field.  */                
static bool include_heading;                                                    Line 118
                                                                                
/* If true, display a '+' for each user if mesg y, a '-' if mesg n,             
   or a '?' if their tty cannot be statted. */                                  
static bool include_mesg;                                                       Line 122
                                                                                
/* If true, display process termination & exit status.  */                      
static bool include_exit;                                                       Line 125
                                                                                
/* If true, display the last boot time.  */                                     
static bool need_boottime;                                                      Line 128
                                                                                
/* If true, display dead processes.  */                                         
static bool need_deadprocs;                                                     Line 131
                                                                                
/* If true, display processes waiting for user login.  */                       
static bool need_login;                                                         Line 134
                                                                                
/* If true, display processes started by init.  */                              
static bool need_initspawn;                                                     Line 137
                                                                                
/* If true, display the last clock change.  */                                  
static bool need_clockchange;                                                   Line 140
                                                                                
/* If true, display the current runlevel.  */                                   
static bool need_runlevel;                                                      Line 143
                                                                                
/* If true, display user processes.  */                                         
static bool need_users;                                                         Line 146
                                                                                
/* If true, display info only for the controlling tty.  */                      
static bool my_line_only;                                                       Line 149
                                                                                
/* The strftime format to use for login times, and its expected                 
   output width.  */                                                            
static char const *time_format;                                                 Line 153
static int time_format_width;                                                   Line 154
                                                                                
/* for long options with no corresponding short option, use enum */             
enum                                                                            Line 157
{                                                                               
  LOOKUP_OPTION = CHAR_MAX + 1                                                  Line 159
};                                                                              Block 1
                                                                                
static struct option const longopts[] =                                         Line 162
{                                                                               
  {"all", no_argument, NULL, 'a'},                                              Line 164
  {"boot", no_argument, NULL, 'b'},                                             Line 165
  {"count", no_argument, NULL, 'q'},                                            Line 166
  {"dead", no_argument, NULL, 'd'},                                             Line 167
  {"heading", no_argument, NULL, 'H'},                                          Line 168
  {"login", no_argument, NULL, 'l'},                                            Line 169
  {"lookup", no_argument, NULL, LOOKUP_OPTION},                                 Line 170
  {"message", no_argument, NULL, 'T'},                                          Line 171
  {"mesg", no_argument, NULL, 'T'},                                             Line 172
  {"process", no_argument, NULL, 'p'},                                          Line 173
  {"runlevel", no_argument, NULL, 'r'},                                         Line 174
  {"short", no_argument, NULL, 's'},                                            Line 175
  {"time", no_argument, NULL, 't'},                                             Line 176
  {"users", no_argument, NULL, 'u'},                                            Line 177
  {"writable", no_argument, NULL, 'T'},                                         Line 178
  {GETOPT_HELP_OPTION_DECL},                                                    Line 179
  {GETOPT_VERSION_OPTION_DECL},                                                 Line 180
  {NULL, 0, NULL, 0}                                                            Line 181
};                                                                              Block 2
                                                                                
/* Return a string representing the time between WHEN and now.                  
   BOOTTIME is the time of last reboot.                                         
   FIXME: locale? */                                                            
static const char *                                                             Line 187
idle_string (time_t when, time_t boottime)                                      Line 188
{                                                                               
  static time_t now = TYPE_MINIMUM (time_t);                                    Line 190
                                                                                
  if (now == TYPE_MINIMUM (time_t))                                             Line 192
    time (&now);                                                                Line 193
                                                                                
  if (boottime < when && now - 24 * 60 * 60 < when && when <= now)              Line 195
    {                                                                           
      int seconds_idle = now - when;                                            Line 197
      if (seconds_idle < 60)                                                    Line 198
        return "  .  ";                                                         Line 199
      else                                                                      Line 200
        {                                                                       
          static char idle_hhmm[IDLESTR_LEN];                                   Line 202
          /* FIXME-in-2018: see if this assert is still required in order       
             to suppress gcc's unwarranted -Wformat-length= warning.  */        
          assert (seconds_idle / (60 * 60) < 24);                               Line 205
          sprintf (idle_hhmm, "%02d:%02d",                                      Line 206
                   seconds_idle / (60 * 60),                                    Line 207
                   (seconds_idle % (60 * 60)) / 60);                            Line 208
          return idle_hhmm;                                                     Line 209
        }                                                                       
    }                                                                           
                                                                                
  return _(" old ");                                                            Line 213
}                                                                               Block 3
                                                                                
/* Return a time string.  */                                                    
static const char *                                                             Line 217
time_string (const STRUCT_UTMP *utmp_ent)                                       Line 218
{                                                                               
  static char buf[INT_STRLEN_BOUND (intmax_t) + sizeof "-%m-%d %H:%M"];         Line 220
                                                                                
  /* Don't take the address of UT_TIME_MEMBER directly.                         
     Ulrich Drepper wrote:                                                      
     "... GNU libc (and perhaps other libcs as well) have extended              
     utmp file formats which do not use a simple time_t ut_time field.          
     In glibc, ut_time is a macro which selects for backward compatibility      
     the tv_sec member of a struct timeval value."  */                          
  time_t t = UT_TIME_MEMBER (utmp_ent);                                         Line 228
  struct tm *tmp = localtime (&t);                                              Line 229
                                                                                
  if (tmp)                                                                      Line 231
    {                                                                           
      strftime (buf, sizeof buf, time_format, tmp);                             Line 233
      return buf;                                                               Line 234
    }                                                                           
  else                                                                          Line 236
    return timetostr (t, buf);                                                  Line 237
}                                                                               Block 4
                                                                                
/* Print formatted output line. Uses mostly arbitrary field sizes, probably     
   will need tweaking if any of the localization stuff is done, or for 64 bit   
   pids, etc. */                                                                
static void                                                                     Line 243
print_line (int userlen, const char *user, const char state,                    Line 244
            int linelen, const char *line,                                      Line 245
            const char *time_str, const char *idle, const char *pid,            Line 246
            const char *comment, const char *exitstr)                           Line 247
{                                                                               
  static char mesg[3] = { ' ', 'x', '\0' };                                     Line 249
  char *buf;                                                                    Line 250
  char x_idle[1 + IDLESTR_LEN + 1];                                             Line 251
  char x_pid[1 + INT_STRLEN_BOUND (pid_t) + 1];                                 Line 252
  char *x_exitstr;                                                              Line 253
  int err;                                                                      Line 254
                                                                                
  mesg[1] = state;                                                              Line 256
                                                                                
  if (include_idle && !short_output && strlen (idle) < sizeof x_idle - 1)       Line 258
    sprintf (x_idle, " %-6s", idle);                                            Line 259
  else                                                                          Line 260
    *x_idle = '\0';                                                             Line 261
                                                                                
  if (!short_output && strlen (pid) < sizeof x_pid - 1)                         Line 263
    sprintf (x_pid, " %10s", pid);                                              Line 264
  else                                                                          Line 265
    *x_pid = '\0';                                                              Line 266
                                                                                
  x_exitstr = xmalloc (include_exit ? 1 + MAX (12, strlen (exitstr)) + 1 : 1);  Line 268
  if (include_exit)                                                             Line 269
    sprintf (x_exitstr, " %-12s", exitstr);                                     Line 270
  else                                                                          Line 271
    *x_exitstr = '\0';                                                          Line 272
                                                                                
  err = asprintf (&buf,                                                         Line 274
                  "%-8.*s"                                                      Line 275
                  "%s"                                                          Line 276
                  " %-12.*s"                                                    Line 277
                  " %-*s"                                                       Line 278
                  "%s"                                                          Line 279
                  "%s"                                                          Line 280
                  " %-8s"                                                       Line 281
                  "%s"                                                          Line 282
                  ,                                                             
                  userlen, user ? user : "   .",                                Line 284
                  include_mesg ? mesg : "",                                     Line 285
                  linelen, line,                                                Line 286
                  time_format_width,                                            Line 287
                  time_str,                                                     Line 288
                  x_idle,                                                       Line 289
                  x_pid,                                                        Line 290
                  /* FIXME: it's not really clear whether the following         
                     field should be in the short_output.  A strict reading     
                     of SUSv2 would suggest not, but I haven't seen any         
                     implementations that actually work that way... */          
                  comment,                                                      Line 295
                  x_exitstr                                                     Line 296
                  );                                                            
  if (err == -1)                                                                Line 298
    xalloc_die ();                                                              ...!common auto-comment...
                                                                                
  {                                                                             
    /* Remove any trailing spaces.  */                                          
    char *p = buf + strlen (buf);                                               Line 303
    while (*--p == ' ')                                                         Line 304
      /* empty */;                                                              
    *(p + 1) = '\0';                                                            Line 306
  }                                                                             
                                                                                
  puts (buf);                                                                   Line 309
  free (buf);                                                                   Line 310
  free (x_exitstr);                                                             Line 311
}                                                                               Block 5
                                                                                
/* Return true if a terminal device given as PSTAT allows other users           
   to send messages to; false otherwise */                                      
static bool                                                                     Line 316
is_tty_writable (struct stat const *pstat)                                      Line 317
{                                                                               
#ifdef TTY_GROUP_NAME                                                           Line 319
  /* Ensure the group of the TTY device matches TTY_GROUP_NAME, more info at    
     https://bugzilla.redhat.com/454261 */                                      
  struct group *ttygr = getgrnam (TTY_GROUP_NAME);                              Line 322
  if (!ttygr || (pstat->st_gid != ttygr->gr_gid))                               Line 323
    return false;                                                               Line 324
#endif                                                                          Line 325
                                                                                
  return pstat->st_mode & S_IWGRP;                                              Line 327
}                                                                               Block 6
                                                                                
/* Send properly parsed USER_PROCESS info to print_line.  The most              
   recent boot time is BOOTTIME. */                                             
static void                                                                     Line 332
print_user (const STRUCT_UTMP *utmp_ent, time_t boottime)                       Line 333
{                                                                               
  struct stat stats;                                                            Line 335
  time_t last_change;                                                           Line 336
  char mesg;                                                                    Line 337
  char idlestr[IDLESTR_LEN + 1];                                                Line 338
  static char *hoststr;                                                         Line 339
#if HAVE_UT_HOST                                                                Line 340
  static size_t hostlen;                                                        Line 341
#endif                                                                          Line 342
                                                                                
#define DEV_DIR_WITH_TRAILING_SLASH "/dev/"                                     Line 344
#define DEV_DIR_LEN (sizeof (DEV_DIR_WITH_TRAILING_SLASH) - 1)                  Line 345
                                                                                
  char line[sizeof (utmp_ent->ut_line) + DEV_DIR_LEN + 1];                      Line 347
  char *p = line;                                                               Line 348
  PIDSTR_DECL_AND_INIT (pidstr, utmp_ent);                                      Line 349
                                                                                
  /* Copy ut_line into LINE, prepending '/dev/' if ut_line is not               
     already an absolute file name.  Some systems may put the full,             
     absolute file name in ut_line.  */                                         
  if ( ! IS_ABSOLUTE_FILE_NAME (utmp_ent->ut_line))                             Line 354
    p = stpcpy (p, DEV_DIR_WITH_TRAILING_SLASH);                                Line 355
  stzncpy (p, utmp_ent->ut_line, sizeof (utmp_ent->ut_line));                   Line 356
                                                                                
  if (stat (line, &stats) == 0)                                                 Line 358...!syscalls auto-comment...
    {                                                                           
      mesg = is_tty_writable (&stats) ? '+' : '-';                              Line 360
      last_change = stats.st_atime;                                             Line 361
    }                                                                           
  else                                                                          Line 363
    {                                                                           
      mesg = '?';                                                               Line 365
      last_change = 0;                                                          Line 366
    }                                                                           
                                                                                
  if (last_change)                                                              Line 369
    sprintf (idlestr, "%.*s", IDLESTR_LEN, idle_string (last_change, boottime));Line 370
  else                                                                          Line 371
    sprintf (idlestr, "  ?");                                                   Line 372
                                                                                
#if HAVE_UT_HOST                                                                Line 374
  if (utmp_ent->ut_host[0])                                                     Line 375
    {                                                                           
      char ut_host[sizeof (utmp_ent->ut_host) + 1];                             Line 377
      char *host = NULL;                                                        Line 378
      char *display = NULL;                                                     Line 379
                                                                                
      /* Copy the host name into UT_HOST, and ensure it's nul terminated. */    
      stzncpy (ut_host, utmp_ent->ut_host, sizeof (utmp_ent->ut_host));         Line 382
                                                                                
      /* Look for an X display.  */                                             
      display = strchr (ut_host, ':');                                          Line 385
      if (display)                                                              Line 386
        *display++ = '\0';                                                      Line 387
                                                                                
      if (*ut_host && do_lookup)                                                Line 389
        {                                                                       
          /* See if we can canonicalize it.  */                                 
          host = canon_host (ut_host);                                          Line 392
        }                                                                       
                                                                                
      if (! host)                                                               Line 395
        host = ut_host;                                                         Line 396
                                                                                
      if (display)                                                              Line 398
        {                                                                       
          if (hostlen < strlen (host) + strlen (display) + 4)                   Line 400
            {                                                                   
              hostlen = strlen (host) + strlen (display) + 4;                   Line 402
              free (hoststr);                                                   Line 403
              hoststr = xmalloc (hostlen);                                      Line 404
            }                                                                   
          sprintf (hoststr, "(%s:%s)", host, display);                          Line 406
        }                                                                       
      else                                                                      Line 408
        {                                                                       
          if (hostlen < strlen (host) + 3)                                      Line 410
            {                                                                   
              hostlen = strlen (host) + 3;                                      Line 412
              free (hoststr);                                                   Line 413
              hoststr = xmalloc (hostlen);                                      Line 414
            }                                                                   
          sprintf (hoststr, "(%s)", host);                                      Line 416
        }                                                                       
                                                                                
      if (host != ut_host)                                                      Line 419
        free (host);                                                            Line 420
    }                                                                           
  else                                                                          Line 422
    {                                                                           
      if (hostlen < 1)                                                          Line 424
        {                                                                       
          hostlen = 1;                                                          Line 426
          free (hoststr);                                                       Line 427
          hoststr = xmalloc (hostlen);                                          Line 428
        }                                                                       
      *hoststr = '\0';                                                          Line 430
    }                                                                           
#endif                                                                          Line 432
                                                                                
  print_line (sizeof UT_USER (utmp_ent), UT_USER (utmp_ent), mesg,              Line 434
              sizeof utmp_ent->ut_line, utmp_ent->ut_line,                      Line 435
              time_string (utmp_ent), idlestr, pidstr,                          Line 436
              hoststr ? hoststr : "", "");                                      Line 437
}                                                                               Block 7
                                                                                
static void                                                                     Line 440
print_boottime (const STRUCT_UTMP *utmp_ent)                                    Line 441
{                                                                               
  print_line (-1, "", ' ', -1, _("system boot"),                                Line 443
              time_string (utmp_ent), "", "", "", "");                          Line 444
}                                                                               Block 8
                                                                                
static char *                                                                   Line 447
make_id_equals_comment (STRUCT_UTMP const *utmp_ent)                            Line 448
{                                                                               
  size_t utmpsize = sizeof UT_ID (utmp_ent);                                    Line 450
  char *comment = xmalloc (strlen (_("id=")) + utmpsize + 1);                   Line 451
                                                                                
  strcpy (comment, _("id="));                                                   Line 453
  strncat (comment, UT_ID (utmp_ent), utmpsize);                                Line 454
  return comment;                                                               Line 455
}                                                                               Block 9
                                                                                
static void                                                                     Line 458
print_deadprocs (const STRUCT_UTMP *utmp_ent)                                   Line 459
{                                                                               
  static char *exitstr;                                                         Line 461
  char *comment = make_id_equals_comment (utmp_ent);                            Line 462
  PIDSTR_DECL_AND_INIT (pidstr, utmp_ent);                                      Line 463
                                                                                
  if (!exitstr)                                                                 Line 465
    exitstr = xmalloc (strlen (_("term="))                                      Line 466
                       + INT_STRLEN_BOUND (UT_EXIT_E_TERMINATION (utmp_ent)) + 1Line 467
                       + strlen (_("exit="))                                    Line 468
                       + INT_STRLEN_BOUND (UT_EXIT_E_EXIT (utmp_ent))           Line 469
                       + 1);                                                    Line 470
  sprintf (exitstr, "%s%d %s%d", _("term="), UT_EXIT_E_TERMINATION (utmp_ent),  Line 471
           _("exit="), UT_EXIT_E_EXIT (utmp_ent));                              Line 472
                                                                                
  /* FIXME: add idle time? */                                                   
                                                                                
  print_line (-1, "", ' ', sizeof utmp_ent->ut_line, utmp_ent->ut_line,         Line 476
              time_string (utmp_ent), "", pidstr, comment, exitstr);            Line 477
  free (comment);                                                               Line 478
}                                                                               Block 10
                                                                                
static void                                                                     Line 481
print_login (const STRUCT_UTMP *utmp_ent)                                       Line 482
{                                                                               
  char *comment = make_id_equals_comment (utmp_ent);                            Line 484
  PIDSTR_DECL_AND_INIT (pidstr, utmp_ent);                                      Line 485
                                                                                
  /* FIXME: add idle time? */                                                   
                                                                                
  print_line (-1, _("LOGIN"), ' ', sizeof utmp_ent->ut_line, utmp_ent->ut_line, Line 489
              time_string (utmp_ent), "", pidstr, comment, "");                 Line 490
  free (comment);                                                               Line 491
}                                                                               Block 11
                                                                                
static void                                                                     Line 494
print_initspawn (const STRUCT_UTMP *utmp_ent)                                   Line 495
{                                                                               
  char *comment = make_id_equals_comment (utmp_ent);                            Line 497
  PIDSTR_DECL_AND_INIT (pidstr, utmp_ent);                                      Line 498
                                                                                
  print_line (-1, "", ' ', sizeof utmp_ent->ut_line, utmp_ent->ut_line,         Line 500
              time_string (utmp_ent), "", pidstr, comment, "");                 Line 501
  free (comment);                                                               Line 502
}                                                                               Block 12
                                                                                
static void                                                                     Line 505
print_clockchange (const STRUCT_UTMP *utmp_ent)                                 Line 506
{                                                                               
  /* FIXME: handle NEW_TIME & OLD_TIME both */                                  
  print_line (-1, "", ' ', -1, _("clock change"),                               Line 509
              time_string (utmp_ent), "", "", "", "");                          Line 510
}                                                                               
                                                                                
static void                                                                     Line 513
print_runlevel (const STRUCT_UTMP *utmp_ent)                                    Line 514
{                                                                               
  static char *runlevline, *comment;                                            Line 516
  unsigned char last = UT_PID (utmp_ent) / 256;                                 Line 517
  unsigned char curr = UT_PID (utmp_ent) % 256;                                 Line 518
                                                                                
  if (!runlevline)                                                              Line 520
    runlevline = xmalloc (strlen (_("run-level")) + 3);                         Line 521
  sprintf (runlevline, "%s %c", _("run-level"), curr);                          Line 522
                                                                                
  if (!comment)                                                                 Line 524
    comment = xmalloc (strlen (_("last=")) + 2);                                Line 525
  sprintf (comment, "%s%c", _("last="), (last == 'N') ? 'S' : last);            Line 526
                                                                                
  print_line (-1, "", ' ', -1, runlevline, time_string (utmp_ent),              Line 528
              "", "", c_isprint (last) ? comment : "", "");                     Line 529
                                                                                
  return;                                                                       Line 531
}                                                                               Block 14
                                                                                
/* Print the username of each valid entry and the number of valid entries       
   in UTMP_BUF, which should have N elements. */                                
static void                                                                     Line 536
list_entries_who (size_t n, const STRUCT_UTMP *utmp_buf)                        Line 537
{                                                                               
  unsigned long int entries = 0;                                                Line 539
  char const *separator = "";                                                   Line 540
                                                                                
  while (n--)                                                                   Line 542
    {                                                                           
      if (IS_USER_PROCESS (utmp_buf))                                           Line 544
        {                                                                       
          char *trimmed_name;                                                   Line 546
                                                                                
          trimmed_name = extract_trimmed_name (utmp_buf);                       Line 548
                                                                                
          printf ("%s%s", separator, trimmed_name);                             Line 550
          free (trimmed_name);                                                  Line 551
          separator = " ";                                                      Line 552
          entries++;                                                            Line 553
        }                                                                       
      utmp_buf++;                                                               Line 555
    }                                                                           
  printf (_("\n# users=%lu\n"), entries);                                       Line 557
}                                                                               Block 15
                                                                                
static void                                                                     Line 560
print_heading (void)                                                            Line 561
{                                                                               
  print_line (-1, _("NAME"), ' ', -1, _("LINE"), _("TIME"), _("IDLE"),          Line 563
              _("PID"), _("COMMENT"), _("EXIT"));                               Line 564
}                                                                               Block 16
                                                                                
/* Display UTMP_BUF, which should have N entries. */                            
static void                                                                     Line 568
scan_entries (size_t n, const STRUCT_UTMP *utmp_buf)                            Line 569
{                                                                               
  char *ttyname_b IF_LINT ( = NULL);                                            Line 571
  time_t boottime = TYPE_MINIMUM (time_t);                                      Line 572
                                                                                
  if (include_heading)                                                          Line 574
    print_heading ();                                                           Line 575
                                                                                
  if (my_line_only)                                                             Line 577
    {                                                                           
      ttyname_b = ttyname (STDIN_FILENO);                                       Line 579
      if (!ttyname_b)                                                           Line 580
        return;                                                                 Line 581
      if (STRNCMP_LIT (ttyname_b, DEV_DIR_WITH_TRAILING_SLASH) == 0)            Line 582
        ttyname_b += DEV_DIR_LEN; /* Discard /dev/ prefix.  */                  Line 583
    }                                                                           
                                                                                
  while (n--)                                                                   Line 586
    {                                                                           
      if (!my_line_only                                                         Line 588
          || STREQ_LEN (ttyname_b, utmp_buf->ut_line,                           Line 589
                        sizeof (utmp_buf->ut_line)))                            Line 590
        {                                                                       
          if (need_users && IS_USER_PROCESS (utmp_buf))                         Line 592
            print_user (utmp_buf, boottime);                                    Line 593
          else if (need_runlevel && UT_TYPE_RUN_LVL (utmp_buf))                 Line 594
            print_runlevel (utmp_buf);                                          Line 595
          else if (need_boottime && UT_TYPE_BOOT_TIME (utmp_buf))               Line 596
            print_boottime (utmp_buf);                                          Line 597
          /* I've never seen one of these, so I don't know what it should       
             look like :^)                                                      
             FIXME: handle OLD_TIME also, perhaps show the delta? */            
          else if (need_clockchange && UT_TYPE_NEW_TIME (utmp_buf))             Line 601
            print_clockchange (utmp_buf);                                       Line 602
          else if (need_initspawn && UT_TYPE_INIT_PROCESS (utmp_buf))           Line 603
            print_initspawn (utmp_buf);                                         Line 604
          else if (need_login && UT_TYPE_LOGIN_PROCESS (utmp_buf))              Line 605
            print_login (utmp_buf);                                             Line 606
          else if (need_deadprocs && UT_TYPE_DEAD_PROCESS (utmp_buf))           Line 607
            print_deadprocs (utmp_buf);                                         Line 608
        }                                                                       
                                                                                
      if (UT_TYPE_BOOT_TIME (utmp_buf))                                         Line 611
        boottime = UT_TIME_MEMBER (utmp_buf);                                   Line 612
                                                                                
      utmp_buf++;                                                               Line 614
    }                                                                           
}                                                                               Block 17
                                                                                
/* Display a list of who is on the system, according to utmp file FILENAME.     
   Use read_utmp OPTIONS to read the file.  */                                  
static void                                                                     Line 620
who (const char *filename, int options)                                         Line 621
{                                                                               
  size_t n_users;                                                               Line 623
  STRUCT_UTMP *utmp_buf;                                                        Line 624
                                                                                
  if (read_utmp (filename, &n_users, &utmp_buf, options) != 0)                  Line 626
    die (EXIT_FAILURE, errno, "%s", quotef (filename));                         Line 627
                                                                                
  if (short_list)                                                               Line 629
    list_entries_who (n_users, utmp_buf);                                       Line 630
  else                                                                          Line 631
    scan_entries (n_users, utmp_buf);                                           Line 632
                                                                                
  free (utmp_buf);                                                              Line 634
}                                                                               Block 18
                                                                                
void                                                                            Line 637
usage (int status)                                                              Line 638
{                                                                               
  if (status != EXIT_SUCCESS)                                                   Line 640
    emit_try_help ();                                                           ...!common auto-comment...
  else                                                                          Line 642
    {                                                                           
      printf (_("Usage: %s [OPTION]... [ FILE | ARG1 ARG2 ]\n"), program_name); Line 644
      fputs (_("\                                                               Line 645
Print information about users who are currently logged in.\n\                   Line 646
"), stdout);                                                                    Line 647
      fputs (_("\                                                               Line 648
\n\                                                                             
  -a, --all         same as -b -d --login -p -r -t -T -u\n\                     Line 650
  -b, --boot        time of last system boot\n\                                 Line 651
  -d, --dead        print dead processes\n\                                     Line 652
  -H, --heading     print line of column headings\n\                            Line 653
"), stdout);                                                                    Line 654
      fputs (_("\                                                               Line 655
  -l, --login       print system login processes\n\                             Line 656
"), stdout);                                                                    Line 657
      fputs (_("\                                                               Line 658
      --lookup      attempt to canonicalize hostnames via DNS\n\                Line 659
  -m                only hostname and user associated with stdin\n\             Line 660
  -p, --process     print active processes spawned by init\n\                   Line 661
"), stdout);                                                                    Line 662
      fputs (_("\                                                               Line 663
  -q, --count       all login names and number of users logged on\n\            Line 664
  -r, --runlevel    print current runlevel\n\                                   Line 665
  -s, --short       print only name, line, and time (default)\n\                Line 666
  -t, --time        print last system clock change\n\                           Line 667
"), stdout);                                                                    Line 668
      fputs (_("\                                                               Line 669
  -T, -w, --mesg    add user's message status as +, - or ?\n\                   Line 670
  -u, --users       list users logged in\n\                                     Line 671
      --message     same as -T\n\                                               Line 672
      --writable    same as -T\n\                                               Line 673
"), stdout);                                                                    Line 674
      fputs (HELP_OPTION_DESCRIPTION, stdout);                                  Line 675
      fputs (VERSION_OPTION_DESCRIPTION, stdout);                               Line 676
      printf (_("\                                                              Line 677
\n\                                                                             
If FILE is not specified, use %s.  %s as FILE is common.\n\                     Line 679
If ARG1 ARG2 given, -m presumed: 'am i' or 'mom likes' are usual.\n\            Line 680
"), UTMP_FILE, WTMP_FILE);                                                      Line 681
      emit_ancillary_info (PROGRAM_NAME);                                       Line 682
    }                                                                           
  exit (status);                                                                Line 684
}                                                                               Block 19
                                                                                
int                                                                             
main (int argc, char **argv)                                                    Line 688
{                                                                               
  int optc;                                                                     Line 690
  bool assumptions = true;                                                      Line 691
                                                                                
  initialize_main (&argc, &argv);                                               VMS-specific entry point handling wildcard expansion
  set_program_name (argv[0]);                                                   Retains program name and discards path
  setlocale (LC_ALL, "");                                                       Sets up internationalization (i18n)
  bindtextdomain (PACKAGE, LOCALEDIR);                                          Assigns i18n directorySets text domain for _() [gettext()] function
  textdomain (PACKAGE);                                                         Sets text domain for _() [gettext()] function
                                                                                
  atexit (close_stdout);                                                        Close stdout on exit (see gnulib)
                                                                                
  while ((optc = getopt_long (argc, argv, "abdlmpqrstuwHT", longopts, NULL))    Line 701
         != -1)                                                                 Line 702
    {                                                                           
      switch (optc)                                                             Line 704
        {                                                                       
        case 'a':                                                               Line 706
          need_boottime = true;                                                 Line 707
          need_deadprocs = true;                                                Line 708
          need_login = true;                                                    Line 709
          need_initspawn = true;                                                Line 710
          need_runlevel = true;                                                 Line 711
          need_clockchange = true;                                              Line 712
          need_users = true;                                                    Line 713
          include_mesg = true;                                                  Line 714
          include_idle = true;                                                  Line 715
          include_exit = true;                                                  Line 716
          assumptions = false;                                                  Line 717
          break;                                                                Line 718
                                                                                
        case 'b':                                                               Line 720
          need_boottime = true;                                                 Line 721
          assumptions = false;                                                  Line 722
          break;                                                                Line 723
                                                                                
        case 'd':                                                               Line 725
          need_deadprocs = true;                                                Line 726
          include_idle = true;                                                  Line 727
          include_exit = true;                                                  Line 728
          assumptions = false;                                                  Line 729
          break;                                                                Line 730
                                                                                
        case 'H':                                                               Line 732
          include_heading = true;                                               Line 733
          break;                                                                Line 734
                                                                                
        case 'l':                                                               Line 736
          need_login = true;                                                    Line 737
          include_idle = true;                                                  Line 738
          assumptions = false;                                                  Line 739
          break;                                                                Line 740
                                                                                
        case 'm':                                                               Line 742
          my_line_only = true;                                                  Line 743
          break;                                                                Line 744
                                                                                
        case 'p':                                                               Line 746
          need_initspawn = true;                                                Line 747
          assumptions = false;                                                  Line 748
          break;                                                                Line 749
                                                                                
        case 'q':                                                               Line 751
          short_list = true;                                                    Line 752
          break;                                                                Line 753
                                                                                
        case 'r':                                                               Line 755
          need_runlevel = true;                                                 Line 756
          include_idle = true;                                                  Line 757
          assumptions = false;                                                  Line 758
          break;                                                                Line 759
                                                                                
        case 's':                                                               Line 761
          short_output = true;                                                  Line 762
          break;                                                                Line 763
                                                                                
        case 't':                                                               Line 765
          need_clockchange = true;                                              Line 766
          assumptions = false;                                                  Line 767
          break;                                                                Line 768
                                                                                
        case 'T':                                                               Line 770
        case 'w':                                                               Line 771
          include_mesg = true;                                                  Line 772
          break;                                                                Line 773
                                                                                
        case 'u':                                                               Line 775
          need_users = true;                                                    Line 776
          include_idle = true;                                                  Line 777
          assumptions = false;                                                  Line 778
          break;                                                                Line 779
                                                                                
        case LOOKUP_OPTION:                                                     Line 781
          do_lookup = true;                                                     Line 782
          break;                                                                Line 783
                                                                                
        case_GETOPT_HELP_CHAR;                                                  Line 785
                                                                                
        case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);                       Line 787
                                                                                
        default:                                                                Line 789
          usage (EXIT_FAILURE);                                                 Line 790
        }                                                                       
    }                                                                           
                                                                                
  if (assumptions)                                                              Line 794
    {                                                                           
      need_users = true;                                                        Line 796
      short_output = true;                                                      Line 797
    }                                                                           
                                                                                
  if (include_exit)                                                             Line 800
    {                                                                           
      short_output = false;                                                     Line 802
    }                                                                           
                                                                                
  if (hard_locale (LC_TIME))                                                    Line 805
    {                                                                           
      time_format = "%Y-%m-%d %H:%M";                                           Line 807
      time_format_width = 4 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 2;                    Line 808
    }                                                                           
  else                                                                          Line 810
    {                                                                           
      time_format = "%b %e %H:%M";                                              Line 812
      time_format_width = 3 + 1 + 2 + 1 + 2 + 1 + 2;                            Line 813
    }                                                                           
                                                                                
  switch (argc - optind)                                                        Line 816
    {                                                                           
    case 2:   /* who <blurf> <glop> */                                          Line 818
      my_line_only = true;                                                      Line 819
      FALLTHROUGH;                                                              Line 820
    case -1:                                                                    Line 821
    case 0:   /* who */                                                         Line 822
      who (UTMP_FILE, READ_UTMP_CHECK_PIDS);                                    Line 823
      break;                                                                    Line 824
                                                                                
    case 1:   /* who <utmp file> */                                             Line 826
      who (argv[optind], 0);                                                    Line 827
      break;                                                                    Line 828
                                                                                
    default:   /* lose */                                                       Line 830
      error (0, 0, _("extra operand %s"), quote (argv[optind + 2]));            Line 831
      usage (EXIT_FAILURE);                                                     Line 832
    }                                                                           
                                                                                
  return EXIT_SUCCESS;                                                          Line 835
}                                                                               Block 20