RCS_ID_C = "$Id: finger.c,v 6.3 1993/10/18 15:45:30 ppessi Exp $";
/*
 * finger.c - finger main program
 *
 * Copyright © 1993 AmiTCP/IP Group, <AmiTCP-group@hut.fi>
 *                  Helsinki University of Technology, Finland.
 *
 * This program is derived from original work distributed in BSD Net 2.
 *
 * Created      : Fri Oct 15 01:29:07 1993 ppessi
 * Last modified: Mon Oct 18 17:34:32 1993 ppessi
 *
 * $Log: finger.c,v $
 * Revision 6.3  1993/10/18  15:45:30  ppessi
 * Added real version tags.
 *
 * Revision 6.2  93/10/15  03:02:00  ppessi
 * Removed -s support (until we got utmp from somewhere)
 */

/*
 * Finger prints out information about users.  It is not portable since
 * certain fields (e.g. the full user name, office, and phone numbers) are
 * extracted from the gecos field of the passwd file which other UNIXes
 * may not have or may use for other things.
 *
 * There are currently two output formats; the short format is one line
 * per user and displays login name, tty, login time, real name, idle time,
 * and office location/phone number.  The long format gives the same
 * information (in a more legible format) as well as home directory, shell,
 * mail info, and .plan/.project files.
 */

#include "finger_rev.h"
const char version[] = VERSTAG;

char copyright[] =
  "Copyright © 1993 AmiTCP/IP Group, <amitcp-group@hut.fi>\n"
  "Helsinki University of Technology, Finland.\n";

char BSD_copyright[] =
  "@(#) Copyright (c) 1989 The Regents of the University of California.\n"
  "All rights reserved.\n";

/*
 * Copyright (c) 1989 The Regents of the University of California.
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/types.h>
#include <sys/param.h>
#ifdef AMIGA
#include <fcntl.h>
#else
#include <sys/file.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include "finger.h"

time_t now;
int lflag, sflag, mflag, pplan;

PERSON *htab[HSIZE];		/* the buckets */
PERSON *phead, *ptail;		/* the linked list of all people */

int entries = 0;

char tbuf[1024];

void loginlist(void);
void userlist(register argc, register char **argv);

main(int argc, char **argv)
{
  extern int optind;
  int ch;
  time_t time();

  while ((ch = getopt(argc, argv, "lmps")) != EOF)
    switch(ch) {
    case 'l':
      lflag = 1;		/* long format */
      break;
    case 'm':
      mflag = 1;		/* force exact match of names */
      break;
    case 'p':
      pplan = 1;		/* don't show .plan/.project */
      break;
#if HAVE_UTMP
    case 's':
      sflag = 1;		/* short format */
      break;
#endif
    case '?':
    default:
      (void)fprintf(stderr,
#if HAVE_UTMP
		    "usage: finger [-lmps] [login ...]\n"
#else
		    "usage: finger [-lmp] [login ...]\n");
#endif
      exit(1);
    }
  argc -= optind;
  argv += optind;

  (void)time(&now);

  if (!*argv) {
#if AMIGA
    fprintf(stderr, "No utmp support yet.");
    exit(0);
#else
    /*
     * Assign explicit "small" format if no names given and -l
     * not selected.  Force the -s BEFORE we get names so proper
     * screening will be done.
     */
    if (!lflag)
      sflag = 1;		/* if -l not explicit, force -s */
    loginlist();
    if (entries == 0)
      (void)printf("No one logged on.\n");
#endif
  } else {
    userlist(argc, argv);
    /*
     * Assign explicit "large" format if names given and -s not
     * explicitly stated.  Force the -l AFTER we get names so any
     * remote finger attempts specified won't be mishandled.
     */
    if (!sflag)
      lflag = 1;		/* if -s not explicit, force -l */
  }
  if (entries != 0) {
#if HAVE_UTMP
    if (lflag)
      lflag_print();
    else
      sflag_print();
#else
    lflag_print();
#endif
  }
  exit(0);
}

#if !AMIGA
void
loginlist(void)
{
  register PERSON *pn;
  struct passwd *pw;
  struct utmp user;
  char name[UT_NAMESIZE + 1];

  if (!freopen(_PATH_UTMP, "r", stdin)) {
    (void)fprintf(stderr, "finger: can't read %s.\n", _PATH_UTMP);
    exit(2);
  }
  name[UT_NAMESIZE] = NULL;
  while (fread((char *)&user, sizeof(user), 1, stdin) == 1) {
    if (!user.ut_name[0])
      continue;
    if ((pn = find_person(user.ut_name)) == NULL) {
      bcopy(user.ut_name, name, UT_NAMESIZE);
      if ((pw = getpwnam(name)) == NULL)
	continue;
      pn = enter_person(pw);
    }
    enter_where(&user, pn);
  }
  for (pn = phead; lflag && pn != NULL; pn = pn->next)
    enter_lastlog(pn);
}
#endif

void
userlist(register argc, register char **argv)
{
  register i;
  register PERSON *pn;
  PERSON *nethead, **nettail;
#if HAVE_UTMP
  struct utmp user;
#endif
  struct passwd *pw;
  int dolocal, *used;

  if (!(used = (int *)calloc((u_int)argc, (u_int)sizeof(int)))) {
    (void)fprintf(stderr, "finger: out of space.\n");
    exit(1);
  }

  /* pull out all network requests */
  for (i = 0, dolocal = 0, nettail = &nethead; i < argc; i++) {
    if (!index(argv[i], '@')) {
      dolocal = 1;
      continue;
    }
    pn = palloc();
    *nettail = pn;
    nettail = &pn->next;
    pn->name = argv[i];
    used[i] = -1;
  }
  *nettail = NULL;

  if (!dolocal)
    goto net;

  /*
   * traverse the list of possible login names and check the login name
   * and real name against the name specified by the user.
   */
#if !AMIGA
  if (mflag) {
#endif
    for (i = 0; i < argc; i++)
      if (used[i] >= 0 && (pw = getpwnam(argv[i]))) {
	enter_person(pw);
	used[i] = 1;
      }
#if !AMIGA
  } else while (pw = getpwent())
    for (i = 0; i < argc; i++)
      if (used[i] >= 0 &&
	  (!strcasecmp(pw->pw_name, argv[i]) ||
	   match(pw, argv[i]))) {
	enter_person(pw);
	used[i] = 1;
      }
#endif

  /* list errors */
  for (i = 0; i < argc; i++)
    if (!used[i])
      (void)fprintf(stderr,
		    "finger: %s: no such user.\n", argv[i]);

  /* handle network requests */
 net:
  for (pn = nethead; pn; pn = pn->next) {
    netfinger(pn->name);
    if (pn->next || entries)
      putchar('\n');
  }

#if HAVE_UTMP
  if (entries == 0)
    return;

  /*
   * Scan thru the list of users currently logged in, saving
   * appropriate data whenever a match occurs.
   */
  if (!freopen(_PATH_UTMP, "r", stdin)) {
    (void)fprintf( stderr, "finger: can't read %s.\n", _PATH_UTMP);
    exit(1);
  }
  while (fread((char *)&user, sizeof(user), 1, stdin) == 1) {
    if (!user.ut_name[0])
      continue;
    if ((pn = find_person(user.ut_name)) == NULL)
      continue;
    enter_where(&user, pn);
  }
  for (pn = phead; pn != NULL; pn = pn->next)
    enter_lastlog(pn);
#endif
}
