/*
 * |-----------------------------------------------------------|
 * | Copyright (c) 1990 MIPS Computer Systems, Inc.            |
 * | All Rights Reserved                                       |
 * |-----------------------------------------------------------|
 * |          Restricted Rights Legend                         |
 * | Use, duplication, or disclosure by the Government is      |
 * | subject to restrictions as set forth in                   |
 * | subparagraph (c)(1)(ii) of the Rights in Technical        |
 * | Data and Computer Software Clause of DFARS 52.227-7013.   |
 * |         MIPS Computer Systems, Inc.                       |
 * |         950 DeGuigne Drive                                |
 * |         Sunnyvale, CA 94086                               |
 * |-----------------------------------------------------------|
 */
/* $Header: devnames.c,v 1.5.2.1 92/06/24 13:55:40 marker Exp $ */

#include <sys/types.h>
#include <sys/param.h>
#include <dirent.h>
#include <sys/stat.h>
#include <bsd/sys/types.h>
#include <fcntl.h>

struct name_header {
	long magic;
	long revision;
	int nametablesize;
	};

#define NAME_MAGIC	0x646b5f6d
#define NAME_REVISION_1	0x1

struct minorlist {
	int namelen;
	int nameoffset;
	};

struct minorlist *majorlist[256];

char *nametablehead = NULL;
char *nametable = NULL;
int nametablesize = 1024;
int nametablespace = 0;
int nameoffset;
int namesegment;


static void
nameinit ()
{
	char **next;
	char *oldnametable;
	
	oldnametable = nametable;
	nametable = (char *)malloc (nametablesize);
	if (oldnametable) {
		namesegment++;
		next = (char **)oldnametable;
		*next = (char *)nametable;
	} else {
		namesegment = 0;
		nametablehead = nametable;
		memset (majorlist, 0, sizeof (struct minorlist *) * 256);
	}

	nametablespace = nametablesize - sizeof (char *);
	nameoffset = sizeof (char *);
	next = (char **)nametable;
	memset (next, 0, sizeof (char *));
}

static void
add_entry (name, dev)
	char *name;
	short  dev;
{
	int len;
	int majorno, minorno;
	struct minorlist *minorentry;
	char *nameptr;

	majorno = (dev >> 8);
	minorno = (dev & 0xFF);

	len = strlen (name);

	if (len > nametablesize) {
		printf ("vdisk: name ^s to large for table.\n", name);
	} else if (len > nametablespace) {
		nameinit ();
	}

	if (majorlist[majorno] == (struct minorlist *)NULL) {
		majorlist[majorno] = (struct minorlist *)malloc (sizeof(struct minorlist) * 256);
		memset (majorlist[majorno], 0, sizeof (struct minorlist) * 256);
	}

	minorentry = majorlist[majorno] + minorno;

	minorentry->namelen = len;
	minorentry->nameoffset = nametablesize*namesegment + nameoffset;

	nameptr = nametable + nameoffset;
	strcpy (nameptr, name);
	nameoffset += len;
	nametablespace -= len;
}

char *
get_entry (dev, name)
	short dev;
	char *name;
{
	short majorno, minorno;
	struct minorlist *minorentry;
	int segno;
	int offset;
	char * nameptr, ** tableptr;

	majorno = (dev >> 8);
	minorno = (dev & 0xFF);

	name[0] = NULL;
	if (majorlist[majorno] != (struct minorlist *) NULL) {
		minorentry = majorlist[majorno] + minorno;
		if (minorentry->namelen > 0) {
			segno = minorentry->nameoffset / nametablesize;
			offset = minorentry->nameoffset % nametablesize;
			for (tableptr = (char **)nametablehead; segno > 0; segno--, tableptr = (char **)*tableptr);
			nameptr = (char *)tableptr + offset;
			strncpy (name, nameptr, minorentry->namelen);
			name[minorentry->namelen] = NULL;
		}
	}
	return (name);
}

void
write_savefile (name)
	char *name;
{
	int fd;
	struct minorlist *newmajorlist[256];
	int i, cnt;
	char **tableptr, **nexttable;
	struct name_header nhdr;

	memset (newmajorlist, 0, sizeof (struct minorlist *) * 256);

	if ((fd = open (name, O_CREAT | O_RDWR, 0644)) < 0) {
		perror ("write_savefile");
		return;
	}

	nhdr.magic = NAME_MAGIC;
	nhdr.revision = NAME_REVISION_1;
	nhdr.nametablesize = nametablesize;

	
	if ((cnt = write (fd, &nhdr, sizeof (struct name_header))) != sizeof (struct name_header)) {
		perror ("write_savefile");
		close (fd);
		unlink (name);
		return;
	}

	if ((cnt = write (fd, majorlist, sizeof (struct minorlist *) * 256)) != sizeof (struct minorlist *) * 256) {
		perror ("write_savefile");
		close (fd);
		unlink (name);
		return;
	}

	for (i = 0; i < 256; i++) {
		if (majorlist[i]) {
			if ((cnt = write (fd, majorlist[i], sizeof (struct minorlist) * 256)) != sizeof (struct minorlist) * 256) {
				perror ("write_savefile");
				close (fd);
				unlink (name);
				return;
			}
		}
	}

	for (nexttable = (char **)nametablehead; nexttable; nexttable = (char **)*nexttable) {
		if ((cnt = write (fd, nexttable, nametablesize)) != nametablesize) {
			perror ("write_savefile");
			close (fd);
			unlink (name);
			return;
		}
	}

	close (fd);
}

int
read_savefile (name)
	char *name;
{
	int fd;
	int i, cnt;
	char **tableptr, **nexttable;
	struct name_header nhdr;

	if ((fd = open (name, O_RDONLY, 0644)) < 0) {
		perror ("read_savefile");
		return 0;
	}

	if ((cnt = read (fd, &nhdr, sizeof (struct name_header))) != sizeof (struct name_header)) {
		perror ("read_savefile");
		close (fd);
		return 0;
	}

	if ((nhdr.magic != NAME_MAGIC) || (nhdr.revision != NAME_REVISION_1)) {
		close (fd);
		return 0;
	}

	nametablesize = nhdr.nametablesize;

	nameinit ();

	if ((cnt = read (fd, majorlist, sizeof (struct minorlist *) * 256)) != sizeof (struct minorlist *) * 256) {
		perror ("read_savefile");
		close (fd);
		return 0;
	}

	for (i = 0; i < 256; i++) {
		if (majorlist[i]) {
			majorlist[i] = (struct minorlist *)malloc (sizeof(struct minorlist) * 256);

			if ((cnt = read (fd, majorlist[i], sizeof (struct minorlist) * 256)) != sizeof (struct minorlist) * 256) {
				perror ("read_savefile");
				close (fd);
				return 0;
			}
		}
	}

	if ((cnt = read (fd, nametable, nametablesize)) != nametablesize) {
		perror ("read_savefile");
		close (fd);
		return 0;
	}

	nexttable = (char **)nametable;
	while (*nexttable) {
		nameinit ();

		if ((cnt = read (fd, nametable, nametablesize)) != nametablesize) {
			perror ("read_savefile");
			close (fd);
			return 0;
		}
		nexttable = (char **)nametable;
	}

	close (fd);
	return 1;
}

void
add_dir (name, savefile)
	char *name;
	char *savefile;
{
	DIR *dirp;
	struct dirent *entry;
	struct stat buf, savebuf;
	char path[MAXPATHLEN];
	int pathlen;

	if (savefile == NULL) {
		goto readit;
	}

	if (stat (name, &buf) < 0) {
		goto readit;
	}

	if (stat (savefile, &savebuf) < 0) {
		goto readit;
	}

	if (savebuf.st_mtime > buf.st_mtime) {
		if (read_savefile (savefile)) {
			return;
		} else {
			goto readit;
		}
	}

readit:
	strcpy (path, name);
	strcat (path, "/");
	pathlen = strlen (path);
	dirp = opendir (name);

	while ((entry = readdir(dirp)) != NULL) {
		strncpy (&path[pathlen], entry->d_name, entry->d_reclen);
		path[pathlen + entry->d_reclen] = NULL;
		if (stat (path, &buf) < 0) {
			continue;
		}
		if (((buf.st_mode & S_IFMT) != S_IFCHR) &&
		    ((buf.st_mode & S_IFMT) != S_IFBLK)) {
			continue;
		}
		add_entry (&path[pathlen], buf.st_rdev);
	}

	closedir (dirp);

	if (savefile) {
		write_savefile (savefile);
	}
}
