/* device.c  -  Device access */

/* Written 1992,1993 by Werner Almesberger */


#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "config.h"
#include "common.h"
#include "temp.h"
#include "device.h"


static int scan_dir(DEVICE *dev,char *parent,int number)
{
    DIR *dp;
    struct dirent *dir;
    char *start;

    if ((dp = opendir(parent)) == NULL)
	die("opendir %s: %s",parent,strerror(errno));
    *(start = strchr(parent,0)) = '/';
    while (dir = readdir(dp)) {
	strcpy(start+1,dir->d_name);
	if (stat(parent,&dev->st) < 0)
	    die("stat %s: %s",parent,strerror(errno));
	if (S_ISBLK(dev->st.st_mode) && dev->st.st_rdev == number) return 1;
	if (S_ISDIR(dev->st.st_mode) && strcmp(dir->d_name,".") &&
	  strcmp(dir->d_name,"..") && scan_dir(dev,parent,number)) return 1;
    }
    (void) closedir(dp);
    *start = 0;
    return 0;
}


int dev_open(DEVICE *dev,int number,int flags)
{
    char name[PATH_MAX+1];
    int count;

    if (dev->delete = !scan_dir(dev,strcpy(name,DEV_DIR),number)) {
	for (count = 0; count <= MAX_TMP_DEV; count++) {
	    sprintf(name,TMP_DEV,count);
	    if (stat(name,&dev->st) < 0) break;
	}
	if (count > MAX_TMP_DEV) die("Failed to create a temporary device");
	if (mknod(name,0600 | S_IFBLK,number) < 0)
	    die("mknod %s: %s",name,strerror(errno));
	if (stat(name,&dev->st) < 0)
	    die("stat %s: %s",name,strerror(errno));
	if (verbose > 1)
	    printf("Created temporary device %s (0x%04X)\n",name,number);
	temp_register(name);
    }
    if ((dev->fd = open(name,flags)) < 0)
	die("open %s: %s",name,strerror(errno));
    dev->name = stralloc(name);
    return dev->fd;
}


void dev_close(DEVICE *dev)
{
    if (close(dev->fd) < 0) die("close %s: %s",dev->name,strerror(errno));
    if (dev->delete) {
	if (verbose > 1)
	    printf("Removed temporary device %s (0x%04X)\n",dev->name,
	      dev->st.st_rdev);
	(void) remove(dev->name);
	temp_unregister(dev->name);
    }
    free(dev->name);
}
