#!/usr/bin/perl

# makedev main script v1.1.1

# Copyright (C) 1997 Torsten Duwe <duwe@caldera.de>

# This code is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.

#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

# Setenv DESTDIR to the root of the tree where you want the devices to be
# made or removed. $DESTDIR/etc/makedev.d/ must contain the definitions.
# $DESTDIR/dev/ will be populated.
use Env qw(DESTDIR);

# use "--package <def-file-name>" to operate only on a specific package,
# "--all" to create / remove not only the default but all known device
# names (in conjunction with --package only of that package).
# "--remove" will delete instead of create; all remaining args will be
# matched as perl regexps against the selected packages' device names.
use Getopt::Long;

*SYS_mknod = \14;		# XXX should be "require 'syscall.ph';"
*BLKDEV = \060000;
*CHRDEV = \020000;

$permhash{"cdrom"}	= 0664 . " 0.6";
$permhash{"cons"}	= 0622 . " 0.5";
$permhash{"dialout"}	= 0660 . " 0.14";
$permhash{"disk"}	= 0640 . " 0.11";
$permhash{"isdninfo"}	= 0444 . " 0.0";
$permhash{"kmem"}	= 0640 . " 0.9";
$permhash{"printer"}	= 0660 . " 0.7";
$permhash{"private"}	= 0600 . " 0.0";
$permhash{"public"}	= 0666 . " 0.0";
$permhash{"random"}	= 0644 . " 0.0";
$permhash{"rom"}	= 0400 . " 0.0";
$permhash{"system"}	= 0660 . " 0.3";
$permhash{"tty"}	= 0660 . " 0.5";
$permhash{"games"}	= 0660 . " 0.20";

sub
MkNod_real
{
    ($mode, $name, $type, $major, $minor) = @_;
    if ($name eq "") {return};
    $user = 0;
    $group = 0;

    # translate mode & type if necessary
  SW1: {
	if ( $type eq "b") { $type=$BLKDEV; last SW1; }
	if ( $type eq "c") { $type=$CHRDEV; last SW1; }
    }
  SW2: {
      if ( $mode =~ /([^ ]+) ([^ ]+)/) {
	  ($mode, $ug) = split(" ", $mode);
	  ($user, $group) = split("[.]", $ug);
	  # fall-thru
      }
      if ( $mode =~ /[A-Za-z]+/) { $mode=$permhash{$mode}; redo SW2; }
  }
    umask 0;
    syscall($SYS_mknod, $name, $mode|$type, ($major<<8)|$minor);
    chown $user, $group, $name;
}

sub
SymLink_real
{
    symlink($_[0], $_[1]);
}


sub
Collect			# A dummy MkNod / SymLink to collect all the names
{
    shift;
    $nodename = shift;
    $olddef = $pkg{$nodename};
    if ($olddef ne "") {
	print "Error: $nodename in $pkg already defined by $olddef\n";
	return;
    }
    $pkg{$nodename} = $pkg;
    $idx{$nodename} = $i;
}

sub
Remove
{
    unlink $_[1];
}


$devincdir = "$DESTDIR/etc/makedev.d";

GetOptions("all", "package=s@", "remove");

# "--package" switches on the command line ? Do only those, else all pkgs.
if (@opt_package) {
    @mods =  @opt_package;
} else {
    opendir(DIR, $devincdir) || die "can't opendir $devincdir: $!";
    @mods = grep { /^[a-z].*[^~]$/ && -f "$devincdir/$_" } readdir(DIR);
    closedir DIR;
}

$SymLink = \&Collect;
$MkNod   = \&Collect;

for (@mods) {
	$name = $_;
	do "$devincdir/$_";
}

chdir ("$DESTDIR/dev");

# Collect all the device names we know about
foreach $pkg ( keys(%numdevs)) {
	$sub = $generate{$pkg};
	for ($i=0; $i < $numdevs{$pkg}; $i++) {
		$idx = $i;
		&$sub($i);
	}
}

$SymLink = $opt_remove ? \&Remove : \&SymLink_real;
$MkNod   = $opt_remove ? \&Remove : \&MkNod_real;

# Now do the actual work.
foreach $nodename (keys(%pkg)) {
	$regex = $opt_all ? ".*" :
	    @ARGV ? join("|", @ARGV) : $stddevs{$pkg{$nodename}};

	if ( $nodename =~ /$regex/ ) {
		$pkg = $pkg{$nodename};
		$sub = $generate{$pkg{$nodename}};
		$i = $idx{$nodename};
		&$sub($idx);
	}
}
exit 0;

## a sample device snippet (linux/i386 harddisks)

# $numdevs{$name} = 512;	# maximum number of entries this generates

## The subset that is commonly used:
# $stddevs{$name} = "^(hd[abcd]([1-9]?|1[0-5])|hd[efgh])\$";

# $generate{$name} = sub {
#     my @hd_base_majors = (3, 22, 33, 34);

#     &$MkNod ("disk", 
# 	   sprintf("hd%c%s", 97+$i/64, ($i%64) ? sprintf("%d", $i%64) : ""),
# 	   "b", $hd_base_majors[$i/128], $i%128);
# };
