#!/usr/bin/perl -w
#-
# Copyright (c) 2001 Dag-Erling Coïdan Smørgrav
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer
#    in this position and unchanged.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. The name of the author may not be used to endorse or promote products
#    derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# $Id: makedev.pl,v 1.1 2004/04/13 18:05:17 des Exp $
#

use strict;
use Fcntl qw(:mode);
use Getopt::Std;
use vars qw(%nodes);

sub usage() {
    print(STDERR "Usage: makedev [-o outfile] [dir]\n");
}

sub stats($) {
    my $name = shift;		# File or directory name

    my @sb;			# File status
    my $type;			# File type
    my $rdev;			# Device number

    @sb = stat($name)
	or die("$name: stat(): $!\n");
    if (S_IFMT($sb[2]) == S_IFCHR) {
	$type = 'c';
	$rdev = $sb[6];
    } elsif (S_IFMT($sb[2]) == S_IFDIR) {
	$type = 'd';
	$rdev = 0;
    } elsif (S_IFMT($sb[2]) == S_IFCHR) {
	$type = 'b';
	$rdev = $sb[6];
    } else {
	die("unsupported type\n");
    }
    return {
	'name'	=> $name,
	'type'	=> $type,
	'mode'	=> $sb[2],
	'uid'	=> $sb[4],
	'gid'	=> $sb[5],
	'rdev'	=> $rdev,
    };
}

sub scan(;$);
sub scan(;$) {
    my $dir = shift || '.';	# Directory to scan

    my $prefix;			# Prefix
    local *DIR;			# Directory handle
    my $entry;			# Directory entry

    if ($dir eq '.') {
	$prefix = "";
    } else {
	$prefix = "$dir/";
    }
    opendir(DIR, $dir)
	or die("$dir: opendir(): $!\n");
    foreach $entry (readdir(DIR)) {
	next if ($entry eq '.' || $entry eq '..');
	$entry = "$prefix$entry";
	if (-l $entry) {
	    warn("$entry is a symlink\n");
	} elsif (-d $entry) {
	    $nodes{$entry} = stats($entry);
	    scan($entry);
	} elsif (-b $entry || -c $entry) {
	    $nodes{$entry} = stats($entry);
	} elsif (-f $entry) {
	    # ignore plain files
	} else {
	    warn("$entry is of unsupported type\n");
	}
    }
    closedir(DIR)
	or die("$dir: closedir(): $!\n");
}

MAIN:{
    my %opts;			# Option hash
    my $node;			# Node
    my $line;			# Line of code
    
    getopts("o:", \%opts)
	or usage();
    if (@ARGV > 1) {
	usage();
    }
    if ($opts{'o'}) {
	open(STDOUT, '>', $opts{'o'})
	    or die("$opts{'o'}: open(): $!\n");
    }
    if (@ARGV) {
	chdir($ARGV[0])
	    or die("$ARGV[0]: chdir(): $!\n");
    }
    scan('.');
    print "/* autogenerated, do not edit */

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

static struct node {
    char        *name;
    char         type;
    mode_t       mode;
    uid_t        uid;
    gid_t        gid;
    dev_t        rdev;
} nodes[] = {
";
    foreach $node (sort(keys(%nodes))) {
	$node = $nodes{$node};
	$line = "    { \"$node->{'name'}\",";
	if (length($line) < 24) {
	    $line = substr($line . ' 'x24, 0, 24);
	}
	$line .= sprintf(" '%s', 0%04o, %8d, %8d, 0x%08x },\n",
			 $node->{'type'}, $node->{'mode'},
			 $node->{'uid'}, $node->{'gid'}, $node->{'rdev'});
	print($line);
    }
    print("    { NULL,              0,   0,            0,        0, 0x00000000 }\n");
    print "};

int
main(void)
{
    struct node *n;
    int r;

    umask(0);
    for (n = nodes; n->name; ++n) {
        switch(n->type) {
        case 'b':
        case 'c':
            r = mknod(n->name, n->mode, n->rdev);
            break;
        case 'd':
            r = mkdir(n->name, n->mode & ~S_IFMT);
            break;
        default:
            exit(1);
        }
        if (r != 0)
            exit(1);
        if (chown(n->name, n->uid, n->gid) != 0)
            exit(1);
    }
    exit(0);
}
";
}
