/*
 * Copyright (c) 1990,1994 Regents of The University of Michigan.
 * All Rights Reserved.  See COPYRIGHT.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */

#include <sys/types.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <netatalk/endian.h>
#include <netatalk/at.h>
#include <errno.h>
#include <atalk/atp.h>
#include <atalk/pap.h>
#include <atalk/nbp.h>
#include <atalk/util.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif /* HAVE_FCNTL_H */
#include <stdio.h>
#include <string.h>
#include <string.h>
#include <stdlib.h>

#define FUCKED

#define _PATH_PAPRC	".paprc"

#define IMAGEWRITER "ImageWriter"
#define IMAGEWRITER_LQ "LQ"

/* Forward Declarations */
static void updatestatus(char *s, int len);
static int send_file(int fd, ATP atp, int lastfile, int is_imagewriter);

static void usage(char *path)
{
    char	*p;

    if (( p = strrchr( path, '/' )) == NULL ) {
	p = path;
    } else {
	p++;
    }
    fprintf( stderr,
	"Usage:\t%s [ -A address ] [ -c ] [ -d ] [ -e ] [ -E ] [ -p printer ]\n"
	"    [ -s statusfile ] [ -w ] [ -W ] [ FILES ]\n"
	"  -A address    - printer Appletalk address\n"
	"  -c            - take cuts (lie about wait time)\n"
	"  -d            - enable debug\n"
	"  -e            - send stdout to stderr\n"
	"  -E            - don't wait for EOF from printer\n"
	"  -p printer    - printer name\n"
	"  -s statusfile - put current printer status in statusfile\n"
	"  -w            - wait for printer status = 'waiting'\n"
	"  -W            - wait for printer status = 'idle'\n"
	"  FILES         - send FILES to printer\n"
	, p );
    exit( 2 );
}

static char *
paprc(void)
{
    static char	s[ 32 + 1 + 32 + 1 + 32 ];
    char	*name = NULL;
    FILE	*f;

    if (( f = fopen( _PATH_PAPRC, "r" )) == NULL ) {
	if ( errno == ENOENT ) {
	    return( NULL );
	} else {
	    perror( _PATH_PAPRC );
	    exit( 2 );
	}
    }
    while ( fgets( s, sizeof( s ), f ) != NULL ) {
	s[ strlen( s ) - 1 ] = '\0';	/* remove trailing newline */
	if ( *s == '#' ) {
	    continue;
	}
	name = s;
	break;
    }
    fclose( f );
    return( name );
}

static char			*printer = NULL;
static char			*status = NULL;
static int			noeof = 0;
static int			waitforprinter = 0;

static unsigned char		connid, quantum, oquantum = PAP_MAXQUANTUM;
static struct sockaddr_at	sat;

static char			cbuf[ 8 ];
static struct nbpnve		nn;
static ATP			satp;

static char		fbuf[ PAP_MAXQUANTUM ][ 4 + PAP_MAXDATA ];
static struct iovec	rfiov[ PAP_MAXQUANTUM ] = {
    { fbuf[ 0 ] + 4,	0 },
    { fbuf[ 1 ] + 4,	0 },
    { fbuf[ 2 ] + 4,	0 },
    { fbuf[ 3 ] + 4,	0 },
    { fbuf[ 4 ] + 4,	0 },
    { fbuf[ 5 ] + 4,	0 },
    { fbuf[ 6 ] + 4,	0 },
    { fbuf[ 7 ] + 4,	0 },
};

static struct iovec	sniov[ PAP_MAXQUANTUM ] = {
    { fbuf[ 0 ],	0 },
    { fbuf[ 1 ],	0 },
    { fbuf[ 2 ],	0 },
    { fbuf[ 3 ],	0 },
    { fbuf[ 4 ],	0 },
    { fbuf[ 5 ],	0 },
    { fbuf[ 6 ],	0 },
    { fbuf[ 7 ],	0 },
};

static char		nbuf[ PAP_MAXQUANTUM ][ 4 + PAP_MAXDATA ];
static struct iovec	rniov[ PAP_MAXQUANTUM ] = {
    { nbuf[ 0 ],	0 },
    { nbuf[ 1 ],	0 },
    { nbuf[ 2 ],	0 },
    { nbuf[ 3 ],	0 },
    { nbuf[ 4 ],	0 },
    { nbuf[ 5 ],	0 },
    { nbuf[ 6 ],	0 },
    { nbuf[ 7 ],	0 },
};

static struct iovec	sfiov[ PAP_MAXQUANTUM ] = {
    { nbuf[ 0 ] + 4,	0 },
    { nbuf[ 1 ] + 4,	0 },
    { nbuf[ 2 ] + 4,	0 },
    { nbuf[ 3 ] + 4,	0 },
    { nbuf[ 4 ] + 4,	0 },
    { nbuf[ 5 ] + 4,	0 },
    { nbuf[ 6 ] + 4,	0 },
    { nbuf[ 7 ] + 4,	0 },
};

static int debug;

int main( int ac, char	**av)
{
    ATP			atp;
    struct atp_block	atpb;
    int			c, err = 0, fd, cuts = 0;
    char		*obj = NULL, *type = "LaserWriter", *zone = "*";
    struct timeval	stv, tv;
    char		rbuf[ ATP_MAXDATA ];
    struct iovec	iov;
    unsigned short	waiting, result;
    int			connattempts = 10;
    int			waitforidle = 0;
    struct at_addr      addr;

    extern char		*optarg;
    extern int		optind;

    int        is_imagewriter;

    memset(&addr, 0, sizeof(addr));
    while (( c = getopt( ac, av, "dWwcep:s:EA:" )) != EOF ) {
	switch ( c ) {
#ifdef FUCKED
	case 'w' :
	    waitforprinter = 1;
	    break;

	case 'W' :
	    waitforidle = 1;
	    break;
#endif /* FUCKED */

	/* enable debugging */
	case 'd' :
	    debug++;
	    break;

	case 'c' :
	    cuts++;
	    break;

	case 'e' :	/* send stdout to stderr */
	    dup2( 2, 1 );
	    break;

	case 'p' :
	    printer = optarg;
	    break;

	case 's' :
	    status = optarg;
	    break;

	case 'E' :
	    noeof = 1;
	    break;
	   
	case 'A':
	    if (!atalk_aton(optarg, &addr)) {
	      fprintf(stderr, "Bad address.\n");
	      exit(1);
	    }
	    break;

	default :
	    err++;
	}
    }
    if ( err ) {
	usage( *av );
    }
    if ( printer == NULL && (( printer = paprc()) == NULL )) {
	fprintf( stderr, "No printer specified and ./.paprc not found.\n" );
	exit( 2 );
    }

    /*
     * Open connection.
     */
    if ( nbp_name( printer, &obj, &type, &zone ) < 0 ) {
	fprintf( stderr, "%s: Bad name\n", printer );
	exit( 2 );
    }
    if ( obj == NULL ) {
	fprintf( stderr, "%s: Bad name\n", printer );
	exit( 2 );
    }

    if ( nbp_lookup( obj, type, zone, &nn, 1, &addr ) <= 0 ) {
	if ( errno != 0 ) {
	    perror( "nbp_lookup" );
	    exit( 2 );
	}
	fprintf( stderr, "%s:%s@%s: NBP Lookup failed\n", obj, type, zone );
	exit( 1 );
    }

    if ( isatty( 1 )) {
	printf( "Trying %u.%d:%d ...\n", ntohs( nn.nn_sat.sat_addr.s_net ),
		nn.nn_sat.sat_addr.s_node, nn.nn_sat.sat_port );
    }

    if (( atp = atp_open( ATADDR_ANYPORT, &addr )) == NULL ) {
	perror( "atp_open" );
	exit( 2 );
    }
    if (( satp = atp_open( ATADDR_ANYPORT, &addr )) == NULL ) {
	perror( "atp_open" );
	exit( 2 );
    }

    is_imagewriter = (0 == strcmp(IMAGEWRITER, type) ||
        0 == strcmp(IMAGEWRITER_LQ, type)  );

    while ( waitforidle ) {
	char	st_buf[ 1024 ];	/* XXX too big */

	cbuf[ 0 ] = 0;
	cbuf[ 1 ] = PAP_SENDSTATUS;
	cbuf[ 2 ] = cbuf[ 3 ] = 0;
	atpb.atp_saddr = &nn.nn_sat;
	atpb.atp_sreqdata = cbuf;
	atpb.atp_sreqdlen = 4;	/* bytes in SendStatus request */
	atpb.atp_sreqto = 2;		/* retry timer */
	atpb.atp_sreqtries = 5;		/* retry count */
	if ( atp_sreq( satp, &atpb, 1, 0 ) < 0 ) {
	    perror( "atp_sreq" );
	    exit( 1 );
	}

	if(debug){ printf( "SENDSTATUS >\n" ), fflush( stdout );}

	atpb.atp_saddr = &nn.nn_sat;
	rniov[ 0 ].iov_len = PAP_MAXDATA + 4;
	atpb.atp_rresiov = rniov;
	atpb.atp_rresiovcnt = 1;
	if ( atp_rresp( satp, &atpb ) < 0 ) {
	    perror( "atp_rresp" );
	    continue;
	}

#ifndef NONZEROSTATUS
	/*
	 * The stinking LaserWriter IINTX puts crap in this
	 * field.
	 */
	if ( ((char *)rniov[ 0 ].iov_base)[ 0 ] != 0 ) {
	    fprintf( stderr, "Bad status response!\n" );
	    exit( 1 );
	}
#endif /* NONZEROSTATUS */

	if ( ((char *)rniov[ 0 ].iov_base)[ 1 ] != PAP_STATUS ||
		atpb.atp_rresiovcnt != 1 ) {
	    fprintf( stderr, "Bad status response!\n" );
	    exit( 1 );
	}

	if(debug){ printf( "< STATUS\n" ), fflush( stdout );}

	memcpy( st_buf, (char *) rniov[ 0 ].iov_base + 9, 
		((char *)rniov[ 0 ].iov_base)[ 8 ] );
	st_buf[ (int) ((char *)rniov[ 0 ].iov_base)[ 8 ]] = '\0';
	if ( strstr( st_buf, "idle" ) != NULL ) {
	    waitforidle = 0;
	} else {
	    updatestatus( (char *) rniov[ 0 ].iov_base + 9,
		    ((char *)rniov[ 0 ].iov_base)[ 8 ] );
	    sleep( 5 );
	}
    }

    cbuf[ 0 ] = connid = getpid() & 0xff;
    cbuf[ 1 ] = PAP_OPEN;
    cbuf[ 2 ] = cbuf[ 3 ] = 0;
    cbuf[ 4 ] = atp_sockaddr( atp )->sat_port;
    cbuf[ 5 ] = oquantum;	/* flow quantum */
    if ( gettimeofday( &stv, NULL ) < 0 ) {
	perror( "gettimeofday" );
	exit( 2 );
    }
    for (;;) {
	if ( cuts ) {
	    waiting = 0xffff;
	} else {
	    if ( gettimeofday( &tv, NULL ) < 0 ) {
		perror( "gettimeofday" );
		exit( 2 );
	    }
	    waiting = htons( tv.tv_sec - stv.tv_sec );
	}
	memcpy(cbuf +  6, &waiting, sizeof( waiting ));

	atpb.atp_saddr = &nn.nn_sat;
	atpb.atp_sreqdata = cbuf;
	atpb.atp_sreqdlen = 8;		/* bytes in OpenConn request */
	atpb.atp_sreqto = 2;		/* retry timer */
	atpb.atp_sreqtries = 5;		/* retry count */
	if ( atp_sreq( atp, &atpb, 1, ATP_XO ) < 0 ) {
	    perror( "atp_sreq" );
	    exit( 1 );
	}

	if(debug){ printf( "OPEN >\n" ), fflush( stdout );}

	iov.iov_base = rbuf;
	iov.iov_len = sizeof( rbuf );
	atpb.atp_rresiov = &iov;
	atpb.atp_rresiovcnt = 1;
	if ( atp_rresp( atp, &atpb ) < 0 ) {
	    perror( "atp_rresp" );
	    if ( connattempts-- <= 0 ) {
		fprintf( stderr, "Can't connect!\n" );
		exit( 1 );
	    }
	    continue;
	}

	/* sanity */
	if ( iov.iov_len < 8 || (unsigned char)rbuf[ 0 ] != connid ||
		rbuf[ 1 ] != PAP_OPENREPLY ) {
	    fprintf( stderr, "Bad response!\n" );
	    continue;	/* This is weird, since TIDs must match... */
	}

	if(debug){ printf( "< OPENREPLY\n" ), fflush( stdout );}

	if ( isatty( 1 )) {
	    printf( "%.*s\n", (int)iov.iov_len - 9, (char *) iov.iov_base + 9 );
	}
	updatestatus( (char *) iov.iov_base + 9, iov.iov_len - 9 );

	memcpy( &result, rbuf +  6,  sizeof( result ));
	if ( result != 0 ) {
	    sleep( 2 );
	} else {
	    memcpy( &sat, &nn.nn_sat, sizeof( struct sockaddr_at ));
	    sat.sat_port = rbuf[ 4 ];
	    quantum = rbuf[ 5 ];
	    break;
	}
    }

    if ( isatty( 1 )) {
	printf( "Connected to %.*s:%.*s@%.*s.\n",
		nn.nn_objlen, nn.nn_obj,
		nn.nn_typelen, nn.nn_type,
		nn.nn_zonelen, nn.nn_zone );
    }

    if ( optind == ac ) {
	send_file( 0, atp, 1 , is_imagewriter);
    } else {
	for (; optind < ac; optind++ ) {
	    if ( strcmp( av[ optind ], "-" ) == 0 ) {
		fd = 0;
	    } else if (( fd = open( av[ optind ], O_RDONLY )) < 0 ) {
		perror( av[ optind ] );
		continue;
	    }
	    send_file( fd, atp, ( optind == ac - 1 ) ? 1 : 0 , is_imagewriter);
	    if ( fd != 0 ) {
		close( fd );
	    }
	}
    }

    /*
     * Close connection.
     */
    cbuf[ 0 ] = connid;
    cbuf[ 1 ] = PAP_CLOSE;
    cbuf[ 2 ] = cbuf[ 3 ] = 0;

    atpb.atp_saddr = &sat;
    atpb.atp_sreqdata = cbuf;
    atpb.atp_sreqdlen = 4;		/* bytes in CloseConn request */
    atpb.atp_sreqto = 2;		/* retry timer */
    atpb.atp_sreqtries = 5;		/* retry count */
    if ( atp_sreq( atp, &atpb, 1, ATP_XO ) < 0 ) {
	perror( "atp_sreq" );
	exit( 1 );
    }

	if(debug){ printf( "CLOSE >\n" ), fflush( stdout );}

    iov.iov_base = rbuf;
    iov.iov_len = sizeof( rbuf );
    atpb.atp_rresiov = &iov;
    atpb.atp_rresiovcnt = 1;
    if ( atp_rresp( atp, &atpb ) < 0 ) {
	perror( "atp_rresp" );
	exit( 1 );
    }

    /* sanity */
    if ( iov.iov_len != 4 || rbuf[ 1 ] != PAP_CLOSEREPLY ) {
	fprintf( stderr, "Bad response!\n" );
	exit( 1 );
    }

#ifndef ZEROCONNID
    /*
     * The AGFA Viper Rip doesn't have the connection id in the close request.
     */
    if ((unsigned char)rbuf[ 0 ] != connid ) {
	fprintf( stderr, "Bad connid in close!\n" );
	exit( 1 );
    }
#endif /* ZEROCONNID */

	if(debug){ printf( "< CLOSEREPLY\n" ), fflush( stdout );}

    if ( isatty( 1 )) {
	printf( "Connection closed.\n" );
    }
    exit( 0 );
}

static int		data = 0;
static unsigned char	port;
static uint16_t	seq = 0;

static int send_file( int fd, ATP atp, int lastfile, int is_imagewriter)
{
    struct timeval	stv, tv;
    struct sockaddr_at	ssat;
    struct atp_block	atpb;
    fd_set		fds;
    int			fiovcnt = 0, eof = 0, senteof = 0, to = 0;
    int			cc, i;
    unsigned short	netseq;

    if ( gettimeofday( &stv, NULL ) < 0 ) {
	perror( "gettimeofday" );
	exit( 2 );
    }

    /*
     * Ask for more data.
     */
    cbuf[ 0 ] = connid;
    cbuf[ 1 ] = PAP_READ;
    if ( ++seq == 0 ) seq = 1;
    netseq = htons( seq );
    memcpy( cbuf +  2, &netseq, sizeof( netseq ));
    atpb.atp_saddr = &sat;
    atpb.atp_sreqdata = cbuf;
    atpb.atp_sreqdlen = 4;		/* bytes in SendData request */
    atpb.atp_sreqto = 15;		/* retry timer */
    atpb.atp_sreqtries = -1;		/* retry count */
    if ( atp_sreq( atp, &atpb, oquantum, ATP_XO ) < 0 ) {
	perror( "atp_sreq" );
	exit( 1 );
    }

	if(debug){ printf( "READ %d >\n", seq ), fflush( stdout );}

    for (;;) {
	if ( gettimeofday( &tv, NULL ) < 0 ) {
	    perror( "gettimeofday" );
	    exit( 2 );
	}

	if (( tv.tv_sec - stv.tv_sec ) >= 60 ) {
	    stv = tv;

	    /*
	     * Send a tickle.
	     */
	    cbuf[ 0 ] = connid;
	    cbuf[ 1 ] = PAP_TICKLE;
	    cbuf[ 2 ] = cbuf[ 3 ] = 0;
	    atpb.atp_saddr = &sat;
	    atpb.atp_sreqdata = cbuf;
	    atpb.atp_sreqdlen = 4;		/* bytes in Tickle request */
	    atpb.atp_sreqto = 0;		/* retry timer */
	    atpb.atp_sreqtries = 1;		/* retry count */
	    if ( atp_sreq( satp, &atpb, 0, 0 ) < 0 ) {
		perror( "atp_sreq" );
		exit( 1 );
	    }

	if(debug){ printf( "TICKLE >\n" ), fflush( stdout );}
	}

	tv.tv_sec = stv.tv_sec + 60 - tv.tv_sec;
	tv.tv_usec = 0;

	FD_ZERO( &fds );
	if ( !waitforprinter && !eof && fiovcnt == 0 ) {
	    FD_SET( fd, &fds );
	}
	FD_SET( atp_fileno( atp ), &fds );

	if (( cc = select( FD_SETSIZE, &fds, NULL, NULL, &tv )) < 0 ) {
	    perror( "select" );
	    exit( 2 );
	}

	/*
	 * A timeout has occured. Keep track of it.
	 */
	if ( cc == 0 ) {
	    if ( to++ > 2 ) {
		fprintf( stderr, "Connection timed out.\n" );
		exit( 1 );
	    }
	    continue;
	}

	/*
	 * Read data.
	 */
	if ( !fiovcnt && FD_ISSET( fd, &fds )) {
	    for ( i = 0; i < quantum; i++ ) {
		rfiov[ i ].iov_len = PAP_MAXDATA;
	    }
	    if (( cc = readv( fd, rfiov, quantum )) < 0 ) {
		perror( "readv" );
		exit( 2 );
	    }
	    if ( cc == 0 ) {
		eof = 1;
	    }
	    fiovcnt = cc / PAP_MAXDATA + ( cc % PAP_MAXDATA > 0 );
	    for ( i = 0; cc > 0; i++ ) {
		rfiov[ i ].iov_len = ( cc > PAP_MAXDATA ) ? PAP_MAXDATA : cc;
		cc -= ( cc > PAP_MAXDATA ) ? PAP_MAXDATA : cc;
	    }
	}

	if ( FD_ISSET( atp_fileno( atp ), &fds )) {
	    ssat = sat;
	    ssat.sat_port = ATADDR_ANYPORT;
	    switch( atp_rsel( atp, &ssat, ATP_TRESP | ATP_TREQ )) {
	    case ATP_TREQ :
		atpb.atp_saddr = &ssat;
		atpb.atp_rreqdata = cbuf;
		atpb.atp_rreqdlen = sizeof( cbuf );
		if ( atp_rreq( atp, &atpb ) < 0 ) {
		    perror( "atp_rreq" );
		    exit( 1 );
		}

		if ( (unsigned char)cbuf[ 0 ] != connid ) {
		    break;
		}

		/* reset timeout counter for all valid requests */
		to = 0;

		switch ( cbuf[ 1 ] ) {
		case PAP_READ :
		    memcpy( cbuf +  2, &netseq, sizeof( netseq ));
	if(debug){ printf( "< READ %d\n", ntohs( netseq )), fflush( stdout );}
#ifdef notdef
		    if ( netseq != 0 ) {
			if ( rseq != ntohs( netseq )) {
	if(debug){ printf( "| DUP %d\n", rseq ), fflush( stdout );}
			    break;
			}
			if ( rseq++ == 0xffff ) rseq = 1;
		    }
#endif /* notdef */

		    data = 1;
		    port = ssat.sat_port;
		    break;

		case PAP_CLOSE :

	if(debug){ printf( "< CLOSE\n" ), fflush( stdout );}

		    /*
		     * Respond to the close request, and fail.
		     */
		    sniov[ 0 ].iov_len = 4;
		    ((char *)sniov[ 0 ].iov_base)[ 0 ] = connid;
		    ((char *)sniov[ 0 ].iov_base)[ 1 ] = PAP_CLOSEREPLY;
		    ((char *)sniov[ 0 ].iov_base)[ 2 ] =
			    ((char *)sniov[ 0 ].iov_base)[ 3 ] = 0;
		    atpb.atp_sresiov = sniov;
		    atpb.atp_sresiovcnt = 1;
		    if ( atp_sresp( atp, &atpb ) < 0 ) {
			perror( "atp_sresp" );
			exit( 1 );
		    }

	if(debug){ printf( "CLOSEREPLY >\n" ), fflush( stdout );}

		    fprintf( stderr, "Connection closed by foreign host.\n" );
		    exit( 1 );

		case PAP_TICKLE :

	if(debug){ printf( "< TICKLE\n" ), fflush( stdout );}

		    break;
		default :
		    fprintf( stderr, "Bad PAP request!\n" );
		    exit( 1 );
		}
		break;

	    case ATP_TRESP :
		/* reset timeout counter for all valid requests */
		to = 0;

		atpb.atp_saddr = &ssat;
		for ( i = 0; i < oquantum; i++ ) {
		    rniov[ i ].iov_len = PAP_MAXDATA + 4;
		}
		atpb.atp_rresiov = rniov;
		atpb.atp_rresiovcnt = oquantum;
		if ( atp_rresp( atp, &atpb ) < 0 ) {
		    perror( "atp_rresp" );
		    exit( 1 );
		}

#ifndef ZEROCONNID
		/*
		 * The HP LJIIISI w/ BridgePort LocalTalk card sends
		 * zero instead of the connid.
		 */
		if ( ((unsigned char *)rniov[ 0 ].iov_base)[ 0 ] != connid ) {
		    fprintf( stderr, "Bad data response!\n" );
		    exit( 1 );
		}
#endif /* ZEROCONNID */
		if ( ((char *)rniov[ 0 ].iov_base)[ 1 ] != PAP_DATA ) {
		    fprintf( stderr, "Bad data response!\n" );
		    exit( 1 );
		}

		for ( cc = 0, i = 0; i < atpb.atp_rresiovcnt; i++ ) {
		    sfiov[ i ].iov_len = rniov[ i ].iov_len - 4;
		    cc += sfiov[ i ].iov_len;
		}
		if ( cc && writev( 1, sfiov, atpb.atp_rresiovcnt ) < cc ) {
		    perror( "writev" );
		    exit( 2 );
		}

		/* eof */
		if ( ((char *)rniov[ 0 ].iov_base)[ 2 ] ) {

	if(debug){ printf( "< DATA (eof)\n" ), fflush( stdout );}

		    return( 0 );
		}

	if(debug){ printf( "< DATA\n" ), fflush( stdout );}


		/*
		 * Ask for more data.
		 */
		cbuf[ 0 ] = connid;
		cbuf[ 1 ] = PAP_READ;
		if ( ++seq == 0 ) seq = 1;
		netseq = htons( seq );
		memcpy( cbuf +  2, &netseq, sizeof( netseq ));
		atpb.atp_saddr = &sat;
		atpb.atp_sreqdata = cbuf;
		atpb.atp_sreqdlen = 4;		/* bytes in SendData request */
		atpb.atp_sreqto = 15;		/* retry timer */
		atpb.atp_sreqtries = -1;	/* retry count */
		if ( atp_sreq( atp, &atpb, oquantum, ATP_XO ) < 0 ) {
		    perror( "atp_sreq" );
		    exit( 1 );
		}

	if(debug){ printf( "READ %d >\n", seq ), fflush( stdout );}

		break;

	    case 0:

	if(debug){ printf( "| RETRANS\n" ), fflush( stdout );}

		break;

	    default:
		perror( "atp_rsel" );
		exit( 1 );
	    }
	}

	/*
	 * Send whatever is pending.
	 */
	if ( !waitforprinter && !senteof && data && ( fiovcnt || eof )) {
	    ssat.sat_port = port;
	    atpb.atp_saddr = &ssat;
	    if ( fiovcnt ) {
		for ( i = 0; i < fiovcnt; i++ ) {
		    sniov[ i ].iov_len = rfiov[ i ].iov_len + 4;
		    ((char *)sniov[ i ].iov_base)[ 0 ] = connid;
		    ((char *)sniov[ i ].iov_base)[ 1 ] = PAP_DATA;
		    senteof = ((char *)sniov[ i ].iov_base)[ 2 ] = eof;
		    ((char *)sniov[ i ].iov_base)[ 3 ] = 0;
		}
	    } else {
		sniov[ 0 ].iov_len = 4;
		((char *)sniov[ 0 ].iov_base)[ 0 ] = connid;
		((char *)sniov[ 0 ].iov_base)[ 1 ] = PAP_DATA;
		senteof = ((char *)sniov[ 0 ].iov_base)[ 2 ] = eof;
		((char *)sniov[ 0 ].iov_base)[ 3 ] = 0;
	    }
	    atpb.atp_sresiov = sniov;
	    atpb.atp_sresiovcnt = fiovcnt ? fiovcnt : 1;
	    if ( atp_sresp( atp, &atpb ) < 0 ) {
		perror( "atp_sresp" );
		exit( 1 );
	    }
	    data = fiovcnt = 0;

	if(debug){ printf( "DATA %s\n", eof ? "(eof) >" : ">" ), fflush( stdout );}

	    /*
	     * The Apple LaserWriter IIf, the HP LWIIISi, and IV, don't
	     * seem to send us an EOF on large jobs.  To work around
	     * this heinous protocol violation, we won't wait for their
	     * EOF before closing.
	     */
	    if ( eof && noeof && lastfile ) {
		return( 0 );
	    }
	} else {
	    /*
	     * If we can't send data right now, go ahead and get the
	     * status. This is cool, because we get here reliably
	     * if there is a problem.
	     */
	    cbuf[ 0 ] = 0;
	    cbuf[ 1 ] = PAP_SENDSTATUS;
	    cbuf[ 2 ] = cbuf[ 3 ] = 0;
	    atpb.atp_saddr = &nn.nn_sat;
	    atpb.atp_sreqdata = cbuf;
	    atpb.atp_sreqdlen = 4;	/* bytes in SendStatus request */
	    atpb.atp_sreqto = 2;		/* retry timer */
	    atpb.atp_sreqtries = 5;		/* retry count */
	    if ( atp_sreq( satp, &atpb, 1, 0 ) < 0 ) {
		perror( "atp_sreq" );
		exit( 1 );
	    }

	if(debug){ printf( "SENDSTATUS >\n" ), fflush( stdout );}

	    atpb.atp_saddr = &nn.nn_sat;
	    rniov[ 0 ].iov_len = PAP_MAXDATA + 4;
	    atpb.atp_rresiov = rniov;
	    atpb.atp_rresiovcnt = 1;
	    if ( atp_rresp( satp, &atpb ) < 0 ) {
		perror( "atp_rresp" );
		continue;
	    }

#ifndef NONZEROSTATUS
        /*
         * The stinking LaserWriter IINTX puts crap in this
         * field.
         * 
         * The Imagewriter II doesn't handle this well, either
         */
        if ( ! is_imagewriter &&  ((char *)rniov[ 0 ].iov_base)[ 0 ] != 0 ) {
        fprintf( stderr, "Bad status response!\n" );
        exit( 1 );
        }
#endif /* NONZEROSTATUS */

	    if ( ! is_imagewriter && ((char *)rniov[ 0 ].iov_base)[ 1 ] != PAP_STATUS ||
		    atpb.atp_rresiovcnt != 1 ) {
		fprintf( stderr, "Bad status response!\n" );
		exit( 1 );
	    }

	if(debug){ printf( "< STATUS\n" ), fflush( stdout );}

#ifdef FUCKED
	    if ( waitforprinter ) {
		char	st_buf[ 1024 ];	/* XXX too big */

		memcpy( st_buf, (char *) rniov[ 0 ].iov_base + 9, 
			((char *)rniov[ 0 ].iov_base)[ 8 ] );
		st_buf[ (int) ((char *)rniov[ 0 ].iov_base)[ 8 ]] = '\0';
		if ( strstr( st_buf, "waiting" ) != NULL ) {
		    waitforprinter = 0;
		}
	    }
#endif /* FUCKED */

	    updatestatus( (char *) rniov[ 0 ].iov_base + 9,
		    ((char *)rniov[ 0 ].iov_base)[ 8 ] );
	}
    }
}

static void updatestatus(char *s, int len)
{
    int			fd = -1;
    struct iovec	iov[ 3 ];

    if ( status ) {
	if (( fd = open( status, O_WRONLY|O_TRUNC )) < 0 ) {
	    perror( status );
	    status = NULL;
	}
    }

    if ( fd < 0 ) {
	fd = 2;
    }

    iov[ 0 ].iov_base = "%%[ ";
    iov[ 0 ].iov_len = 4;
    iov[ 1 ].iov_base = s;
    iov[ 1 ].iov_len = len;
    iov[ 2 ].iov_base = " ]%%\n";
    iov[ 2 ].iov_len = 5;

    if (writev(fd, iov, 3) < 0)
        fprintf(stderr, "could not write to fd (%s)\n", strerror(errno));
    if ( status ) {
	close( fd );
    }
}
