#! /usr/bin/perl

# Simple named pipe daemon for oki4drv.

# The idea: we read jobs from a named pipe in the filesystem.  We
# simply pipe them into oki4drv.  We define a job format like this:

# %FAKEOKI option=foo
# %FAKEOKI option=bar
# %!
# ....

# This is handy in that I can put the %FAKEOKI clauses in as ps/gs
# args in Foomatic, or anyone else can trivially handle this in any
# other system.

# Available options are not really documented; just read the code
# below...

# If not in path
my $oki4drv = '/usr/bin/oki4drv';

# contents of 8min.rip from distribution.  You could use immediate.rip.
my $sleepstring = '%-98765XBb %-98765X ';

use Getopt::Std;
getopts('f:o:h');
help_and_die() if $opt_h;

my $device = '/dev/lp0';
$device = $opt_o if $opt_o;

# Step 1: mkfifo

my $fifo = '/dev/oki4drv';
$fifo = $opt_f if ($opt_f);

if (! -p $fifo) {
    system("mkfifo $fifo -m0660") == 0
	or die "Unable to make named pipe $fifo";
}

#system("chown lp.sys $fifo; chmod 660 $fifo") == 0
#    or die "Unable to set ownerships/permissions for named pipe $fifo";

# Step 1.5: dissociate, daemonize, &c...

# TODO: Connect stderr/stdout to logger or something similar, and/or
# provide suitable die handler...

fork and exit;

use Sys::Syslog qw(:DEFAULT setlogsock);
setlogsock('inet');             # change to 'inet' for network logging
openlog ('oki4daemon', 'pid,cons,ndelay', 'lpr') 
    or die 'Unable to open syslog';

$SIG{__DIE__} = sub {
    die @_ if $^S;
    syslog 'err', @_;
};

sub death { 
    syslog 'notice', "Oki4daemon for $fifo exiting on signal";
    unlink "/var/run/oki4daemon"; 
    exit (0);
}

$SIG{HUP}  = \&death;
$SIG{INT}  = \&death;
$SIG{QUIT} = \&death;

close STDERR;
close STDOUT;
close STDIN;

open RUN, ">/var/run/oki4daemon"
    or die "Cannon open /var/run/oki4daemon";
print RUN $$, "\n";
close RUN;

syslog 'notice', "Oki4drv daemon running on fifo $fifo";

while(1) {

    # First, make sure the printer is set to suspend properly...
    open LPDEV, "> $device"
	or die "Cannot open $device to write sleepstring";
    print LPDEV $sleepstring;
    close LPDEV
	or die "Cannot close $device after writing sleepstring";

    # Now wait for jobs...
    open FIFO, "< $fifo"
	or die "Unable to open named pipe $fifo";

    my ($papersize, $darkness, $paperweight, $graphics, $manual);

    # override default in oki4drv
    $papersize = 'letter';

    # Step 2: read beginning of job, until no @FAKEOKI options
    my $firstline;
    do {
	$firstline = <FIFO>;

	# could be options
	if ($firstline =~ m!^\%FAKEOKI ([^=]+)=([^=]+)$!) {
	    my ($opt, $val) = ($1, $2);
	    chomp $val;

	    if ($opt eq 'PAPERSIZE') {
		if ($papersize eq 'a4'
		    or $papersize eq 'a5'
		    or $papersize eq 'a6'
		    or $papersize eq 'b5'
		    or $papersize eq 'letter'
		    or $papersize eq 'legal') {

		    # kosher papersize
		    $papersize = $val;
		} else {
		    syslog 'warn', "Bad papersize $val";
		}
	    } elsif ($opt eq 'DARKNESS') {
		if ($val <= 2 and $val >= -2) {
		    $darkness = sprintf("%d", $val);
		} else {
		    syslog 'warn', "Bad darkness $val";
		}
	    } elsif ($opt eq 'PAPERWEIGHT') {
		if ($val <= 2 and $val >= -2) {
		    $paperweight = sprintf("%d", $val);
		} else {
		    syslog 'warn', "Bad weight $val";
		}
	    } elsif ($opt eq 'GRAPHICS') {
		if ($val eq 'ON') {
		    $graphics = 1;
		}
	    } elsif ($opt eq 'MANUALFEED') {
		if ($val eq 'ON') {
		    $manual = 1;
		}
	    }
	}
    } until ($firstline !~ m%^\%FAKEOKI%);
    
    # Step 3: run oki4drv
    my $okicommand = 
	sprintf('%s%s%s%s%s%s%s -',
		$oki4drv,
		" -o $device",
		defined($manual) ? ' -m' : '',
		defined($graphics) ? ' -g' : '',
		defined($papersize) ? " -s $papersize" : '',
		defined($paperweight) ? " -w $paperweight" : '',
		defined($darkness) ? " -d $darkness" : '');

    # print STDERR "running '$okicommand'\n";

    open OKIDRV, "|$okicommand"
	or die "Unable to run '$okicommand'";

    # Step 4: stuff job into oki4drv

    my $linect = 1;

    print OKIDRV $firstline;
    while (<FIFO>) {
	print OKIDRV $_;
	$linect++;
    }

    sleep 5;
    close OKIDRV;
    # should do: or die "Error closing '$okicommand'";
    # ...but this makes the daemon die for bad jobs

    # Repeat...
    close FIFO;
    sleep 5;

}


sub help_and_die {
    print STDERR "Usage $0 [-f fifo_path] [-o device_path]\n";
    exit(1);
}
