/* device.c  -  Device access */

/* Copyright 1992-1995 Werner Almesberger. See file COPYING for details. */


#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"


typedef struct _st_buf {
    struct _st_buf *next;
    struct stat st;
} ST_BUF;


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

    st.next = next;
    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,&st.st) >= 0) {
	    dev->st = st.st;
	    if (S_ISBLK(dev->st.st_mode) && dev->st.st_rdev == number) {
		(void) closedir(dp);
		return 1;
	    }
	    if (S_ISDIR(dev->st.st_mode) && strcmp(dir->d_name,".") &&
	      strcmp(dir->d_name,"..")) {
		for (walk = next; walk; walk = walk->next)
		    if (stat_equal(&walk->st,&st.st)) break;
		if (!walk && scan_dir(&st,dev,parent,number)) {
		    (void) closedir(dp);
		    return 1;
		}
	    }
	}
    }
    (void) closedir(dp);
    *start = 0;
    return 0;
}


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

    if (stat(DEV_DIR,&st.st) < 0) die("stat " DEV_DIR ": %s",strerror(errno));
    st.next = NULL;
    if (dev->delete = !scan_dir(&st,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);
}
