

/*
 * $Id: rdev.frag,v 1.6 96/01/09 08:27:48 abe Exp $
 */


/*
 * To use this code fragment:
 *
 * 1. Define DIRTYPE as:
 *
 *	  #define DIRTYPE direct
 *    or  #define DIRTYPE dirent
 *
 * 2. Make the following function prototype declaration:
 *
 *	  _PROTOTYPE(static void stkdir,(char ***d, int *n, int *x, char *p));
 *
 * 3. Define HASDNAMLEN if struct DIRTYPE has a d_namlen element, giving
 *    the length of d_name.
 *
 * 4. Define the RDEV_EXPDEV macro to apply special handling to device
 *    numbers, as required.  For example, for EP/IX 2.1.1:
 *
 *	#define RDEV_EXPDEV(n)	expdev(n)
 *
 *    to use the expdev() function to expand device numbers.  If
 *    no RDEV_EXPDEV macro is defined, it defaults to:
 *
 *	#define RDEV_EXPDEV(n)	(n)
 */


#if	!defined(RDEV_EXPDEV)
#define	RDEV_EXPDEV(n)		(n)
#endif	/* !defined(RDEV_EXPDEV) */


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

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

	DIR *dfp;
	int dn = 0;
	struct DIRTYPE *dp;
	char **dstk = NULL;
	int dx = 0;
	int err = 0;
	int i = 0;

#if	!defined(HASDNAMLEN)
	STRNCPY_L dnamlen;
#endif	/* !defined(HASDNAMLEN) */

	MALLOC_S nl;
	char path[MAXNAMLEN+1];
	int pl;
	struct stat sb;

	if (Sdev)
		return;

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

	(void) stkdir(&dstk, &dn, &dx, "/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	defined(HASDNAMLEN)
			if ((nl = pl + dp->d_namlen) >= sizeof(path))
#else	/* !defined(HASDNAMLEN) */
			dnamlen = strlen(dp->d_name);
			if ((nl = pl + dnamlen) >= sizeof(path))
#endif	/* defined(HASDNAMLEN) */

			{
				(void) fprintf(stderr,
					"%s: /dev entry name too long: %s\n",
					Pn, dp->d_name);
				exit(1);
			}
			(void) strncpy(&path[pl], dp->d_name,

#if	defined(HASDNAMLEN)
				(STRNCPY_L)dp->d_namlen
#else	/* !defined(HASDNAMLEN) */
				dnamlen
#endif	/* !defined(HASDNAMLEN) */

			);

			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(&dstk, &dn, &dx, 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 = RDEV_EXPDEV(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(d, n, x, p)
	char ***d;		/* array of directory pointers */
	int *n;			/* number of pointers */
	int *x;			/* current index */
	char *p;		/* directory path */
{
	if (*d == NULL) {

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

stkdir_nospace:

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

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