.
[coreutils.git] / src / pinky.c
blob1a6ff3524a36dfa6c9c807dfe9a46d5000842ad2
1 /* GNU's pinky.
2 Copyright (C) 92, 93, 94, 95, 96, 1997 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
18 /* Created by hacking who.c by Kaveh Ghazi ghazi@caip.rutgers.edu */
20 #include <config.h>
21 #include <getopt.h>
22 #include <pwd.h>
24 #include "system.h"
25 #include "error.h"
26 #include "readutmp.h"
28 #ifndef MAXHOSTNAMELEN
29 # define MAXHOSTNAMELEN 64
30 #endif
32 #ifndef S_IWGRP
33 # define S_IWGRP 020
34 #endif
36 int gethostname ();
37 char *ttyname ();
39 /* The name this program was run with. */
40 const char *program_name;
42 /* If nonzero, display usage information and exit. */
43 static int show_help;
45 /* If nonzero, print the version on standard output and exit. */
46 static int show_version;
48 /* If nonzero, display the hours:minutes since each user has touched
49 the keyboard, or blank if within the last minute, or days followed
50 by a 'd' if not within the last day. */
51 static int include_idle = 1;
53 /* If nonzero, display a line at the top describing each field. */
54 static int include_heading = 1;
56 /* if nonzero, display the user's full name from pw_gecos. */
57 static int include_fullname = 1;
59 /* if nonzero, display the user's ~/.project file when doing long format. */
60 static int include_project = 1;
62 /* if nonzero, display the user's ~/.plan file when doing long format. */
63 static int include_plan = 1;
65 /* if nonzero, display the user's home directory and shell
66 when doing long format. */
67 static int include_home_and_shell = 1;
69 /* if nonzero, use the "short" output format. */
70 static int do_short_format = 1;
72 /* if nonzero, display the ut_host field. */
73 #ifdef HAVE_UT_HOST
74 static int include_where = 1;
75 #endif
77 static struct option const longopts[] =
79 {"help", no_argument, &show_help, 1},
80 {"version", no_argument, &show_version, 1},
81 {NULL, 0, NULL, 0}
84 /* Return a string representing the time between WHEN and the time
85 that this function is first run. */
87 static const char *
88 idle_string (time_t when)
90 static time_t now = 0;
91 static char idle_hhmm[10];
92 time_t seconds_idle;
94 if (now == 0)
95 time (&now);
97 seconds_idle = now - when;
98 if (seconds_idle < 60) /* One minute. */
99 return " ";
100 if (seconds_idle < (24 * 60 * 60)) /* One day. */
102 sprintf (idle_hhmm, "%02d:%02d",
103 (int) (seconds_idle / (60 * 60)),
104 (int) ((seconds_idle % (60 * 60)) / 60));
105 return (const char *) idle_hhmm;
107 sprintf (idle_hhmm, "%dd", (int) (seconds_idle / (24 * 60 * 60)));
108 return (const char *) idle_hhmm;
111 /* Display a line of information about UTMP_ENT. */
113 static void
114 print_entry (const STRUCT_UTMP *utmp_ent)
116 struct stat stats;
117 time_t last_change;
118 char mesg;
120 #define DEV_DIR_WITH_TRAILING_SLASH "/dev/"
121 #define DEV_DIR_LEN (sizeof (DEV_DIR_WITH_TRAILING_SLASH) - 1)
123 char line[sizeof (utmp_ent->ut_line) + DEV_DIR_LEN + 1];
124 time_t tm;
126 /* Copy ut_line into LINE, prepending `/dev/' if ut_line is not
127 already an absolute pathname. Some system may put the full,
128 absolute pathname in ut_line. */
129 if (utmp_ent->ut_line[0] == '/')
131 strncpy (line, utmp_ent->ut_line, sizeof (utmp_ent->ut_line));
132 line[sizeof (utmp_ent->ut_line)] = '\0';
134 else
136 strcpy (line, DEV_DIR_WITH_TRAILING_SLASH);
137 strncpy (line + DEV_DIR_LEN, utmp_ent->ut_line, sizeof (utmp_ent->ut_line));
138 line[DEV_DIR_LEN + sizeof (utmp_ent->ut_line)] = '\0';
141 if (stat (line, &stats) == 0)
143 mesg = (stats.st_mode & S_IWGRP) ? ' ' : '*';
144 last_change = stats.st_atime;
146 else
148 mesg = '?';
149 last_change = 0;
152 printf ("%-8.*s", (int) sizeof (utmp_ent->ut_name), utmp_ent->ut_name);
154 if (include_fullname)
156 struct passwd *pw;
157 char name[sizeof (utmp_ent->ut_name) + 1];
159 strncpy (name, utmp_ent->ut_name, sizeof (utmp_ent->ut_name));
160 name[sizeof (utmp_ent->ut_name)] = '\0';
161 pw = getpwnam (name);
162 if (pw == NULL)
163 printf (" %19s", " ???");
164 else
166 char *const comma = strchr (pw->pw_gecos, ',');
167 if (comma)
168 *comma = '\0';
170 /* FIXME: we don't yet convert '&' to username capitalized. */
171 printf (" %-19.19s", pw->pw_gecos);
175 printf (" %c%-8.*s",
176 mesg, (int) sizeof (utmp_ent->ut_line), utmp_ent->ut_line);
178 if (include_idle)
180 if (last_change)
181 printf (" %-6s", idle_string (last_change));
182 else
183 printf (" %-6s", "???");
186 /* Don't take the address of UT_TIME_MEMBER directly.
187 Ulrich Drepper wrote:
188 ``... GNU libc (and perhaps other libcs as well) have extended
189 utmp file formats which do not use a simple time_t ut_time field.
190 In glibc, ut_time is a macro which selects for backward compatibility
191 the tv_sec member of a struct timeval value.'' */
192 tm = UT_TIME_MEMBER (utmp_ent);
193 printf (" %-12.12s", ctime (&tm) + 4);
195 #ifdef HAVE_UT_HOST
196 if (include_where && utmp_ent->ut_host[0])
198 extern char *canon_host ();
199 char ut_host[sizeof (utmp_ent->ut_host) + 1];
200 char *host = 0, *display = 0;
202 /* Copy the host name into UT_HOST, and ensure it's nul terminated. */
203 strncpy (ut_host, utmp_ent->ut_host, (int) sizeof (utmp_ent->ut_host));
204 ut_host[sizeof (utmp_ent->ut_host)] = '\0';
206 /* Look for an X display. */
207 display = strrchr (ut_host, ':');
208 if (display)
209 *display++ = '\0';
211 if (*ut_host)
212 /* See if we can canonicalize it. */
213 host = canon_host (ut_host);
214 if ( ! host)
215 host = ut_host;
217 if (display)
218 printf (" %s:%s", host, display);
219 else
220 printf (" %s", host);
222 #endif
224 putchar ('\n');
227 /* Display a verbose line of information about UTMP_ENT. */
229 static void
230 print_long_entry (const char name[])
232 struct passwd *pw;
234 pw = getpwnam (name);
236 printf (_("Login name: "));
237 printf ("%-28s", name);
239 printf (_("In real life: "));
240 if (pw == NULL)
242 printf (" %s", _("???\n"));
243 return;
245 else
247 char *const comma = strchr (pw->pw_gecos, ',');
248 if (comma)
249 *comma = '\0';
251 /* FIXME: we don't yet convert '&' to username capitalized. */
252 printf (" %s", pw->pw_gecos);
255 putchar ('\n');
257 if (include_home_and_shell)
259 printf (_("Directory: "));
260 printf ("%-29s", pw->pw_dir);
261 printf (_("Shell: "));
262 printf (" %s", pw->pw_shell);
263 putchar ('\n');
266 if (include_project)
268 FILE *stream;
269 char buf[1024];
270 const char *const baseproject = "/.project";
271 char *const project =
272 xmalloc (strlen (pw->pw_dir) + strlen (baseproject) + 1);
274 strcpy (project, pw->pw_dir);
275 strcat (project, baseproject);
277 stream = fopen (project, "r");
278 if (stream)
280 int bytes;
282 printf (_("Project: "));
284 while ((bytes = fread (buf, 1, sizeof (buf), stream)) > 0)
285 fwrite (buf, 1, bytes, stdout);
286 fclose (stream);
289 free (project);
292 if (include_plan)
294 FILE *stream;
295 char buf[1024];
296 const char *const baseplan = "/.plan";
297 char *const plan =
298 xmalloc (strlen (pw->pw_dir) + strlen (baseplan) + 1);
300 strcpy (plan, pw->pw_dir);
301 strcat (plan, baseplan);
303 stream = fopen (plan, "r");
304 if (stream)
306 int bytes;
308 printf (_("Plan:\n"));
310 while ((bytes = fread (buf, 1, sizeof (buf), stream)) > 0)
311 fwrite (buf, 1, bytes, stdout);
312 fclose (stream);
315 free (plan);
318 putchar ('\n');
321 /* Print the username of each valid entry and the number of valid entries
322 in UTMP_BUF, which should have N elements. */
324 static void
325 print_heading (void)
327 printf ("%-8s", _("Login"));
328 if (include_fullname)
329 printf (" %-19s", _(" Name"));
330 printf (" %-9s", _("TTY"));
331 if (include_idle)
332 printf (" %-6s", _("Idle"));
333 printf (" %-12s", _("When"));
334 #ifdef HAVE_UT_HOST
335 if (include_where)
336 printf (" %s", _("Where"));
337 #endif
338 putchar ('\n');
341 /* Display UTMP_BUF, which should have N entries. */
343 static void
344 scan_entries (int n, const STRUCT_UTMP *utmp_buf,
345 const int argc_names, char *const argv_names[])
347 if (include_heading)
348 print_heading ();
350 while (n--)
352 if (utmp_buf->ut_name[0]
353 #ifdef USER_PROCESS
354 && utmp_buf->ut_type == USER_PROCESS
355 #endif
358 if (argc_names)
360 int i;
362 for (i = 0; i < argc_names; i++)
363 if (strncmp (utmp_buf->ut_name, argv_names[i],
364 sizeof (utmp_buf->ut_name)) == 0)
366 print_entry (utmp_buf);
367 break;
370 else
371 print_entry (utmp_buf);
373 utmp_buf++;
377 /* Display a list of who is on the system, according to utmp file FILENAME. */
379 static void
380 short_pinky (const char *filename,
381 const int argc_names, char *const argv_names[])
383 int n_users;
384 STRUCT_UTMP *utmp_buf;
385 int fail = read_utmp (filename, &n_users, &utmp_buf);
387 if (fail)
388 error (1, errno, "%s", filename);
390 scan_entries (n_users, utmp_buf, argc_names, argv_names);
393 static void
394 long_pinky (const int argc_names, char *const argv_names[])
396 int i;
398 for (i = 0; i < argc_names; i++)
399 print_long_entry (argv_names[i]);
402 static void
403 usage (int status)
405 if (status != 0)
406 fprintf (stderr, _("Try `%s --help' for more information.\n"),
407 program_name);
408 else
410 printf (_("Usage: %s [OPTION]... [USER]...\n"), program_name);
411 printf (_("\
413 -l do long format output\n\
414 -b omit the user's home directory and shell in long format\n\
415 -h omit the user's project file in long format\n\
416 -p omit the user's plan file in long format\n\
417 -s do short format output, this is the default\n\
418 -f omit the line of column headings in short format\n\
419 -w omit the user's full name in short format\n\
420 -i omit the user's full name and remote host in short format\n\
421 -q omit the user's full name, remote host and idle time\n\
422 in short format\n\
423 --help display this help and exit\n\
424 --version output version information and exit\n\
426 The utmp file will be %s.\n\
427 "), UTMP_FILE);
428 puts (_("\nReport bugs to <bug-sh-utils@gnu.org>."));
430 exit (status);
434 main (int argc, char *const argv[])
436 int optc, longind;
438 program_name = argv[0];
439 setlocale (LC_ALL, "");
440 bindtextdomain (PACKAGE, LOCALEDIR);
441 textdomain (PACKAGE);
443 while ((optc = getopt_long (argc, argv, "sfwiqbhlp", longopts, &longind)) != -1)
445 switch (optc)
447 case 0:
448 break;
450 case 's':
451 do_short_format = 1;
452 break;
454 case 'l':
455 do_short_format = 0;
456 break;
458 case 'f':
459 include_heading = 0;
460 break;
462 case 'w':
463 include_fullname = 0;
464 break;
466 case 'i':
467 include_fullname = 0;
468 #ifdef HAVE_UT_HOST
469 include_where = 0;
470 #endif
471 break;
473 case 'q':
474 include_fullname = 0;
475 #ifdef HAVE_UT_HOST
476 include_where = 0;
477 #endif
478 include_idle = 0;
479 break;
481 case 'h':
482 include_project = 0;
483 break;
485 case 'p':
486 include_plan = 0;
487 break;
489 case 'b':
490 include_home_and_shell = 0;
491 break;
493 default:
494 usage (1);
498 if (show_version)
500 printf ("pinky (%s) %s\n", GNU_PACKAGE, VERSION);
501 exit (0);
504 if (show_help)
505 usage (0);
507 if (do_short_format)
508 short_pinky (UTMP_FILE, argc - optind, argv + optind);
509 else
510 long_pinky (argc - optind, argv + optind);
512 exit (0);