/* Look up user and/or group names.
   This has nothing to do with cryptography.
   Copyright (C) 1988, 1992 Free Software Foundation

   From GNU Tar.
   
GNU Tar 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 2, or (at your option)
any later version.

GNU Tar 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 GNU Tar; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

/*
 * Look up user and/or group names.
 *
 * This file should be modified for non-unix systems to do something
 * reasonable.
 */

#include <config.h>
#include "net-includes.h"
#include "diffie/compat.h"

#define TUNMLEN 256
#define TGNMLEN 256

static int saveuid = -993;
static char saveuname[TUNMLEN];
static int my_uid = -993;

static int savegid = -993;
static char savegname[TGNMLEN];
static int my_gid = -993;

#define myuid	( my_uid < 0? (my_uid = getuid()): my_uid )
#define	mygid	( my_gid < 0? (my_gid = getgid()): my_gid )

struct passwd *cached_getpwuid (pid_t pid);
struct group *cached_getgrgid (gid_t gid);

/*
 * Look up a user or group name from a uid/gid, maintaining a cache.
 * FIXME, for now it's a one-entry cache.
 * FIXME2, the "-993" is to reduce the chance of a hit on the first lookup.
 *
 * This is ifdef'd because on Suns, it drags in about 38K of "yellow
 * pages" code, roughly doubling the program size.  Thanks guys.
 */
void finduname (char *uname, int uid)
{
    struct passwd *pw;
    if (uid != saveuid) {
	saveuid = uid;
	saveuname[0] = '\0';
	pw = cached_getpwuid (uid);
	if (pw)
	    strncpy (saveuname, pw->pw_name, TUNMLEN);
    }
    strncpy (uname, saveuname, TUNMLEN);
}

int finduid (char *uname)
{
    struct passwd *pw;
    extern struct passwd *getpwnam ();
    
    if (uname[0] != saveuname[0]/* Quick test w/o proc call */
	||0 != strncmp (uname, saveuname, TUNMLEN)) {
	strncpy (saveuname, uname, TUNMLEN);
	pw = getpwnam (uname);
	if (pw) {
	    saveuid = pw->pw_uid;
	} else {
	    saveuid = myuid;
	}
    }
    return saveuid;
}


void findgname (char *gname, int gid)
{
    struct group *gr;
    if (gid != savegid) {
	savegid = gid;
	savegname[0] = '\0';
	setgrent ();
	gr = cached_getgrgid (gid);
	if (gr)
	    strncpy (savegname, gr->gr_name, TGNMLEN);
    }
    (void) strncpy (gname, savegname, TGNMLEN);
}


int findgid (char *gname)
{
    struct group *gr;
    extern struct group *getgrnam ();
    
    if (gname[0] != savegname[0]/* Quick test w/o proc call */
	||0 != strncmp (gname, savegname, TUNMLEN)) {
	strncpy (savegname, gname, TUNMLEN);
	gr = getgrnam (gname);
	if (gr) {
	    savegid = gr->gr_gid;
	} else {
	    savegid = mygid;
	}
    }
    return savegid;
}

struct passwd_list {
    struct passwd *p;
    pid_t pid;
    struct passwd_list *next;
};

struct passwd *cached_getpwuid (pid_t pid)
{
    static struct passwd_list *l = 0, *last = 0;
    struct passwd_list *p;
    struct passwd *r;
    if (last)
	if (last->pid == pid)
	    return last->p;
    for (p = l; p; p = p->next)
	if (p->pid == pid) {
	    last = p;
	    return p->p;
	}
    r = getpwuid (pid);
    p = l;
    l = (struct passwd_list *) malloc (sizeof (struct passwd_list));
    if (r) {
	l->p = (struct passwd *) malloc (sizeof (struct passwd));
	memcpy (l->p, r, sizeof (struct passwd));
	r = l->p;
	if (r->pw_name)
	    r->pw_name = (char *) strdup (r->pw_name);
	if (r->pw_passwd)
	    r->pw_passwd = (char *) strdup (r->pw_passwd);
	if (r->pw_gecos)
	    r->pw_gecos = (char *) strdup (r->pw_gecos);
	if (r->pw_dir)
	    r->pw_dir = (char *) strdup (r->pw_dir);
	if (r->pw_shell)
	    r->pw_shell = (char *) strdup (r->pw_shell);
    } else {
	l->p = 0;
    }
    l->pid = pid;
    l->next = p;
    last = l;
    return l->p;
}

struct group_list {
    struct group *g;
    gid_t gid;
    struct group_list *next;
};

struct group *cached_getgrgid (gid_t gid)
{
    static struct group_list *l = 0, *last = 0;
    struct group_list *g;
    struct group *r;
    if (last)
	if (last->gid == gid)
	    return last->g;
    for (g = l; g; g = g->next)
	if (g->gid == gid) {
	    last = g;
	    return g->g;
	}
    r = getgrgid (gid);
    g = l;
    l = (struct group_list *) malloc (sizeof (struct group_list));
    if (r) {
	l->g = (struct group *) malloc (sizeof (struct group));
	memcpy (l->g, r, sizeof (struct group));
	r = l->g;
	if (r->gr_name)
	    r->gr_name = (char *) strdup (r->gr_name);
	if (r->gr_passwd)
	    r->gr_passwd = (char *) strdup (r->gr_passwd);
	if (r->gr_mem) {
	    int i;
	    char **m;
	    for (i = 0; r->gr_mem[i]; i++);
	    m = malloc (sizeof (char *) * (i + 1));
	    m[i] = 0;
	    for (i = 0; r->gr_mem[i]; i++)
		m[i] = (char *) strdup (r->gr_mem[i]);
	    r->gr_mem = m;
	}
    } else {
	l->g = 0;
    }
    l->gid = gid;
    l->next = g;
    last = l;
    return l->g;
}







