/*
 * ddev.c - AIX device support functions for lsof
 */


/*
 * Copyright 1994 Purdue Research Foundation, West Lafayette, Indiana
 * 47907.  All rights reserved.
 *
 * Written by Victor A. Abell
 *
 * This software is not subject to any license of the American Telephone
 * and Telegraph Company or the Regents of the University of California.
 *
 * Permission is granted to anyone to use this software for any purpose on
 * any computer system, and to alter it and redistribute it freely, subject
 * to the following restrictions:
 *
 * 1. Neither the authors nor Purdue University are responsible for any
 *    consequences of the use of this software.
 *
 * 2. The origin of this software must not be misrepresented, either by
 *    explicit claim or by omission.  Credit to the authors and Purdue
 *    University must appear in documentation and sources.
 *
 * 3. Altered versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 *
 * 4. This notice may not be removed or altered.
 */

#ifndef lint
static char copyright[] =
"@(#) Copyright 1994 Purdue Research Foundation.\nAll rights reserved.\n";
static char *rcsid = "$Id: ddev.c,v 1.11 95/09/01 08:27:28 abe Exp $";
#endif


#include "lsof.h"

_PROTOTYPE(static void stkdir,(char *p));


/*
 * Local static values
 */

int Dn = 0;				/* directory stack entries allocated */
char **Dstk = NULL;			/* directory stack */
int Dx = 0;				/* directory stack index */


/*
 * getchan() - get channel from file path name
 */

int
getchan(p)
	char *p;			/* file path name */
{
	int ch;
	char *s;

	if ((s = strrchr(p, '/')) == NULL) 
		return(-1);
	if (*(++s) == '\0')
		return(-1);
	for (ch = 0; *s; s++) {

#if	defined(__STDC__)
		if ( ! isdigit(*s))
#else
		if ( ! isascii(*s) || ! isdigit(*s))
#endif	/* __STDC__ */

			return(-1);
		ch = (ch * 10) + *s - '0';
	}
	return(ch);
}


/*
 * printchdevname() - print character device name
 */

int
printchdevname(rdev, f)
	dev_t *rdev;			/* device */
	int f;				/* 1 = follow with '\n' */
{
	struct l_dev *dp;

	if ((dp = lkupdev(rdev, 1)) != (struct l_dev *)NULL) {
		if (Lf->ch < 0) {
			if (f)
				(void) puts(dp->name);
			else
				(void) fputs(dp->name, stdout);
		} else {
			(void) printf("%s/%d", dp->name, Lf->ch);
			if (f)
				putchar('\n');
		}
		return(1);
	}
	return(0);
}


/*
 * readdev() - read names, modes and device types of everything in /dev
 */

void
readdev()
{
#if	defined(HASDCACHE)
	int dcrd;
#endif	/* defined(HASDCACHE) */

	DIR *dfp;
	struct dirent *dp;
	int err = 0;
	static int first = 1;
	int i = 0;
	MALLOC_S nl;
	char path[MAXNAMLEN+1];
	int pl;
	struct stat sb;

	if (!first)
		return;
	first = 0;

#if	defined(HASDCACHE)
/*
 * Read device cache, as directed.
 */
	if (DCstate == 2 || DCstate == 3) {
		if ((dcrd = read_dcache()) == 0)
			return;
	}
#endif	/* defined(HASDCACHE) */

	(void) stkdir("/dev");
/*
 * Unstack the next /dev or /dev/<subdirectory> directory.
 */
	while (--Dx >= 0) {
		(void) strcpy(path, Dstk[Dx]);
		if ((dfp = opendir(path)) == NULL) {

#if	defined(WARNDEVACCESS)
			if (!Fwarn)
				(void) fprintf(stderr,
					"%s: WARNING: can't open %s\n",
					Pn, path);
#endif	/* defined(WARNDEVACCESS) */

			continue;
		}
		(void) strcat(path, "/");
		pl = strlen(path);
		(void) free((FREE_P *)Dstk[Dx]);
		Dstk[Dx] = NULL;
	/*
	 * Scan the directory.
	 */
		for (dp = readdir(dfp); dp; dp = readdir(dfp)) {
			if (dp->d_ino == 0 || dp->d_name[0] == '.')
				continue;
		/*
		 * Form the full path name and get its status.
		 */
			if ((nl = pl + dp->d_namlen) >= sizeof(path)) {
				(void) fprintf(stderr,
					"%s: /dev entry name too long: %s\n",
					Pn, dp->d_name);
				exit(1);
			}
			(void) strncpy(&path[pl], dp->d_name,
				(STRNCPY_L)dp->d_namlen);
			path[nl++] = '\0';
			if (lstat(path, &sb) != 0) {
				(void) fprintf(stderr,
					"%s: can't lstat %s: %s\n",
					Pn, path, strerror(errno));
				err = 1;
				continue;
			}
		/*
		 * If it's a subdirectory, stack its name for later
		 * processing.
		 */
			if ((sb.st_mode & S_IFMT) == S_IFDIR) {
				(void) stkdir(path);
				continue;
			}
		/*
		 * Skip all but character devices.
		 */
			if ((sb.st_mode & S_IFMT) != S_IFCHR)
				continue;
		/*
		 * Make room for another Devtp[] entry.
		 */
			if (i >= Ndev) {
				Ndev += DEVINCR;
				if (Devtp == NULL)
				    Devtp = (struct l_dev *)malloc(
					(MALLOC_S)(sizeof(struct l_dev)*Ndev));
				else
				    Devtp = (struct l_dev *)realloc(
					(MALLOC_P *)Devtp,
					(MALLOC_S)(sizeof(struct l_dev)*Ndev));
				if (Devtp == NULL) {
					(void) fprintf(stderr,
						"%s: no space for devices\n",
						Pn);
					exit(1);
				}
			}
		/*
		 * Store the device number, inode number, and name in
		 * the Devtp[] entry.
		 */
			Devtp[i].rdev = sb.st_rdev;
			Devtp[i].inode = sb.st_ino;
			if ((Devtp[i].name = (char *)malloc(nl)) == NULL) {
				(void) fprintf(stderr,
					"%s: no space for /dev/%s\n",
					Pn, dp->d_name);
				exit(1);
			}
			(void) strcpy(Devtp[i].name, path);
			i++;
		}
		(void) closedir(dfp);
	}
/*
 * Free any directory stack space.
 */
	if (Dstk != NULL)
		(void) free((FREE_P *)Dstk);
/*
 * Reduce the Devtp[] table to its minimum size.
 */
	if (Ndev > i) {
		Ndev = i;
		Devtp = (struct l_dev *)realloc((MALLOC_P *)Devtp,
		      (MALLOC_S)(sizeof(struct l_dev) * Ndev));
	}
	if (err)
		exit(1);
/*
 * Allocate sorting pointers and sort them by Devtp[] device number.
 */
	if ((Sdev = (struct l_dev **)malloc((MALLOC_S)(sizeof(struct l_dev *)
	    * Ndev)))
	== NULL) {
		(void) fprintf(stderr, "%s: no space for device pointers\n",
			Pn);
		exit(1);
	}
	for (i = 0; i < Ndev; i++) {
		Sdev[i] = &Devtp[i];
	}
	(void) qsort((QSORT_P *)Sdev, (size_t)Ndev,
		(size_t)sizeof(struct l_dev *), compdev);

#if	defined(HASDCACHE)
/*
 * Write device cache file, as required.
 */
	if (DCstate == 1 || (DCstate == 3 && dcrd))
		write_dcache();
#endif	/* defined(HASDCACHE) */

}


/*
 * stkdir() - stack directory name
 */

static void
stkdir(p)
	char *p;		/* directory path */
{
	if (Dstk == NULL) {

	/*
	 * Allocate first entry.
	 */
		if ((Dstk = (char **)malloc(sizeof(char *))) == NULL) {

stkdir_nospace:

			(void) fprintf(stderr,
				"%s: no space for directory stack at %s\n",
				Pn, p);
			exit(1);
		}
		Dn = 1;
		Dx = 0;
	} else if (Dx >= Dn) {

	/*
	 * Allocate additional space as required.
	 */
		Dn++;
		if ((Dstk = (char **)realloc((MALLOC_P *)Dstk,
		          (MALLOC_S)(Dn * sizeof(char *))))
		== NULL)
			goto stkdir_nospace;
	}
/*
 * Allocate space for the name, copy it there and put its pointer on the stack.
 */
	if ((Dstk[Dx] = (char *)malloc((MALLOC_S)(strlen(p) + 1))) == NULL) {
		(void) fprintf(stderr, "%s: no space for %s\n", Pn, p);
		exit(1);
	}
	(void) strcpy(Dstk[Dx], p);
	Dx++;
}


/*
 * find_ch_ino() comes from ../common/fchi.frag.
 * lkupdev() comes from ../common/lkud.frag.
 * read_dcache() and write_dcache() come from ../common/dvch.frag.
 */
