/*
 *	Findnouser: find all files which aren't owned by any existing user.
 *
 *	Usage: findnouser starting_directory [legal login name] ...
 *
 *	with 1 arg, it prints the filenames of all files which are not owned
 *	by anybody with a current login.
 *
 *	args 2 onwards are an optional list of login names which you are not
 *	interested in. All files belonging to anyone other than those in the
 *	list will have their names printed.
 *
 *	Martin Guy, UKC, September 1986.
 */
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <signal.h>

#define MAXUIDS 1024		/* Should be dynamic. Yes, I'm lazy. */

char uidexists[MAXUIDS];	/* non-zero if this uid exists */

int checkowner();
int coredump();

char *progname;		/* argv[0] for error reporting */

main(argc,argv)
char **argv;
{
	progname = argv[0];

	/* Else it's a real bugger trying to find core dumps from this thing! */
	(void) signal(SIGSEGV, coredump);
	(void) signal(SIGFPE, coredump);
	(void) signal(SIGBUS, coredump);

	if (argc < 2) {
		fprintf(stderr, "Usage: %s <dir>\n", progname);
		exit(1);
	}
	if (argc == 2) { 
		builduidmap();
	}
	if (argc > 2) {
		/* list of valid usernames supplied... */
		register int i;
		register struct passwd *pp;

		for (i=2; i<argc; i++) {
			pp = getpwnam(argv[i]);
			if (pp == NULL) {
				fprintf(stderr, "%s: %s: no such user.\n", progname, argv[i]);
			} else {
				adduid(pp->pw_uid);
			}
		}
	}	
	if (descend(argv[1], checkowner) == 0) fprintf(stderr, "%s: None found.\n", progname);
}

builduidmap()
{
	struct passwd *pp;

	while ( (pp=getpwent()) != NULL) {
		adduid(pp->pw_uid);
	}
	endpwent();
}

adduid(uid)
int uid;
{
	if (uid >= MAXUIDS) {
		fprintf(stderr, "%s: Found uid %d. Recompile with greater MAXUIDS.\n", progname, uid);
	} else {
		if (uidexists[uid]) {
			fprintf(stderr, "%s: Found uid %d again!\n", progname, uid);
		}
		uidexists[uid] = 1;
	}
}

/*
 * Arguments to fn are:
 * name of file relative to cwd.
 * full name of file from starting directory.
 * pointer to stat structure for that file.
 *	(may be NULL if not stattable)
 */
int
checkowner(name, fullname, stbuf)
char *name;
char *fullname;
struct stat *stbuf;
{
	if (stbuf == NULL) return(0);
	if (stbuf->st_uid >= MAXUIDS || !uidexists[stbuf->st_uid]) {
		puts(fullname);
	}
	return(1);
}

int
coredump(sig)
int sig;
{
	(void) chdir("/tmp");
	(void) signal(sig, SIG_DFL);
	kill(getpid(), sig);
	abort();
}
