#! /usr/bin/perl

$me = $0;
$me =~ s,.*/,,;

sub usage {
    print STDERR "usage: $me [ --version ] [ -f, --force ] [ --rename ] [ --pcrename ] [ directory ... ]\n";
    exit(1);
}

sub errornote {
    local($msg) = @_;

    print STDERR "$me: $msg\n";
    ++$errors;
}

sub ppdfile {
    local($file) = @_;

    return $file =~ /\.ppd/i;
}

sub normalizename {
    local($name) = @_;

    $name =~ tr,[ ./],[__%],;

    return $name;
}

sub ppdnames {
    local($dir, $file) = @_;

    if (open(FILE, "<$file")) {
	local($pc, $full);
	local($model, $product, $shortnick);

	while (<FILE>) {
	    chop;

	    if (/^\*PCFileName:\s*"([^.]*)(\.?PPD|\.ppd)?"/) {
		$pc = substr($1, 0, 8);
		$pc =~ tr/[A-Z]/[a-z]/;
	    } elsif (/^\*NickName:\s*"\s*(.*[\S])\s*"/) {
		$full = normalizename($1);
	    } elsif (/^\*ModelName:\s*"\s*(.*[\S])\s*"/) {
		$model = normalizename($1);
	    } elsif (/^\*Product:\s*"\(\s*(.*[\S])\s*\)"/) {
		$product = normalizename($1);
	    } elsif (/^\*ShortNickName:\s*"\s*(.*[\S])\s*"/) {
		$shortnick = normalizename($1);
	    }

	    break if $pc && $full;
	}

	$full = $model unless $full;
	$full = $product unless $full;
	$full = $shortnick unless $full;

	if ($pc && $full) {
	    return ($pc, $full);
	} else {
	    return ('', '', "names cannot be found in \`$dir/$file'");
	}
    } else {
	return ('', '', "cannot open \`$dir/$file'");
    }
}

sub updateppddir {
    local($dir, $rename, $rtime, %ppddir) = @_;

    if (! -d "$dir") {
	errornote("\`$dir' is not a directory");
	return;
    }

    if (-f "$dir/ppd.dir" && ! -w "$dir/ppd.dir") {
	errornote("file \`$dir/ppd.dir' is not writable");
    }

    local($tmpdir) = $ENV{'TMPDIR'};
    $tmpdir = '/tmp' unless $tmpdir;

    $tmpdir .= "/$me.$$.dir";

    if (open(LIST, ">$tmpdir")) {
        local($where) = getcwd;

	if (!chdir($dir)) {
	    errornote("cannot change directory to \`$dir'");
	    unlink($tmpdir);
	    return;
	}

	if (opendir(DIR, ".")) {
	    local($file);

	    while ($file = readdir(DIR)) {
		if (&ppdfile($file)) {
		    local($read) = !$rtime
			|| !$ppddir{$file}{'pc'}
			|| !$ppddir{$file}{'full'};

		    if (!$read) {
                    	local($dev, $ino, $mode, $nlink, $uid, $gid,
			    $rdev, $size, $atime, $mtime) = stat($file);

		        $read = ($mtime > $rtime);
		    }

		    if ($read) {
		        local($pc, $full, $err) = &ppdnames($dir, "$file");

		        if ($err) {
			    errornote($err);
		        } else {
		    	    $ppddir{$file}{'pc'} = $pc;
		    	    $ppddir{$file}{'full'} = $full;
		        }
		    }
		}
	    }

	    closedir DIR;

	    foreach $file (keys %ppddir) {
		if ($rename) {
		    local($new) = "$ppddir{$file}{$rename}.ppd";

		    if ($new eq '.ppd') {
			errornote("cannot find $rename name for"
			    . " \`$dir/$file'");
		    } elsif (rename($file, $new)) {
			print LIST
			    "$ppddir{$file}{'pc'} $ppddir{$file}{'full'}"
		    	    . " $new\n";
			next;
		    } else {
			errornote("cannot rename \`$dir/$file' to"
			    . " \`$dir/$new'");
		    }
		}

		print LIST "$ppddir{$file}{'pc'} $ppddir{$file}{'full'}"
		    . " $file\n";
	    }
	}
	chdir $where;

	close LIST;

	if (-s $tmpdir) {
	    if (system("2>/dev/null mv $tmpdir ppd.dir")) {
	        errornote("cannot move new \`$dir/ppd.dir'");
	        unlink($tmpdir);
	    }
	} else {
	    unlink($tmpdir);

	    if (-f "ppd.dir") {
	        if (!unlink("ppd.dir")) {
		    errornote("cannot remove old \`$dir/ppd.dir'");
	    	}
	    }
	}
    } else {
	errornote("cannot create temporary \`$tmpdir'");
    }
}

sub makeppddir {
    local($dir, $force, $rename) = @_;

    if (-f "$dir/ppd.dir") {
	if (open(PPDDIR, "<$dir/ppd.dir")) {
	    local($modtime);

	    if (!$force) {
	        local($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size,
                    $atime, $mtime) = stat(PPDDIR);

		$modtime = $mtime;
	    }

	    while (<PPDDIR>) {
		chop;
		local($pc, $full, @name) = split;
		local($file) = join(' ', @name);

		if (-f "$dir/$file") {
		    $ppddir{$file}{'pc'} = $pc;
		    $ppddir{$file}{'full'} = $full;
		}
	    }

	    close PPDDIR;

	    return updateppddir($dir, $rename, $modtime, %ppddir);
	} else {
	    return ("cannot open \`$dir/ppd.dir'");
	}
    } else {
	return updateppddir($dir, $rename);
    }

    return '';
}

while ($ARGV[0]) {
    if ($ARGV[0] =~ /--version/) {
	print "$me version 1.0.1,",
	    " by Yves Arrouye <Yves.Arrouye\@marin.fdn.fr>\n";
	exit 0;
    } elsif ($ARGV[0] eq '-f' || $ARGV[0] eq '--force') {
	$force = 1;
    } elsif ($ARGV[0] eq '--rename') {
	$rename = 'full';
    } elsif ($ARGV[0] eq '--pcrename') {
	$rename = 'pc';
    } elsif ($ARGV[0] =~ /^-/) {
	usage;
    } else {
	@dirs = (@dirs, $ARGV[0]);
    }

   shift(@ARGV);
}

if (!@dirs) {
    @dirs = ('.');
}

foreach $dir (@dirs) {
    makeppddir($dir, $force, $rename);
}

if ($errors) {
    exit(2);
}

