URI: 
       tInitial lp. - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 262ecfed9f7e39811f34517d82e848b8ec20f863
   DIR parent 2863ba101f0c9fec34756948e263cd534a3634ee
  HTML Author: rsc <devnull@localhost>
       Date:   Wed, 31 Aug 2005 02:18:29 +0000
       
       Initial lp.
       
       Diffstat:
         A src/cmd/lp/LOCK.c                   |      57 +++++++++++++++++++++++++++++++
         A src/cmd/lp/ipcopen.c                |      92 +++++++++++++++++++++++++++++++
         A src/cmd/lp/lp.rc                    |     224 +++++++++++++++++++++++++++++++
         A src/cmd/lp/lpdaemon.c               |     457 +++++++++++++++++++++++++++++++
         A src/cmd/lp/lpdsend.c                |     428 +++++++++++++++++++++++++++++++
         A src/cmd/lp/lpsend.c                 |     341 +++++++++++++++++++++++++++++++
         A src/cmd/lp/lpsend.rc                |      18 ++++++++++++++++++
         A src/cmd/lp/mkfile                   |      30 ++++++++++++++++++++++++++++++
       
       8 files changed, 1647 insertions(+), 0 deletions(-)
       ---
   DIR diff --git a/src/cmd/lp/LOCK.c b/src/cmd/lp/LOCK.c
       t@@ -0,0 +1,57 @@
       +#include <u.h>
       +#include <libc.h>
       +
       +/* MAXHOSTNAMELEN is in sys/param.h */
       +#define MAXHOSTNAMELEN        64
       +
       +char lockstring[MAXHOSTNAMELEN+8];
       +
       +void
       +main(int argc, char *argv[]) {
       +        char *lockfile;
       +        int fd, ppid, ssize;
       +        struct Dir *statbuf;
       +
       +        if (argc != 4) {
       +                fprint(2, "usage: LOCK lockfile hostname ppid\n");
       +                exits("lock failed on usage");
       +        }
       +        lockfile = argv[1];
       +        if ((fd=create(lockfile, OLOCK|ORDWR, 0666)) < 0) {
       +                exits("lock failed on create");
       +        }
       +        ppid = atoi(argv[3]);
       +        ssize = sprint(lockstring, "%s %s\n", argv[2], argv[3]);
       +        if (write(fd, lockstring, ssize) != ssize) {
       +                fprint(2, "LOCK:write(): %r\n");
       +                exits("lock failed on write to lockfile");
       +        }
       +
       +        switch(fork()) {
       +        default:
       +                exits("");
       +        case 0:
       +                break;
       +        case -1:
       +                fprint(2, "LOCK:fork(): %r\n");
       +                exits("lock failed on fork");
       +        }
       +
       +        for(;;) {
       +                statbuf = dirfstat(fd);
       +                if(statbuf == nil)
       +                        break;
       +                if (statbuf->length == 0){
       +                        free(statbuf);
       +                        break;
       +                }
       +                free(statbuf);
       +                if (write(fd, "", 0) < 0)
       +                        break;
       +                sleep(3000);
       +        }
       +
       +        close(fd);
       +        postnote(PNGROUP, ppid, "kill");
       +        exits("");
       +}
   DIR diff --git a/src/cmd/lp/ipcopen.c b/src/cmd/lp/ipcopen.c
       t@@ -0,0 +1,92 @@
       +#include <u.h>
       +#include <libc.h>
       +
       +int ppid;
       +
       +/*
       + * predefined
       + */
       +void pass(int from, int to);
       +
       +
       +/*
       + *  Connect to given datakit port
       + */
       +main(int argc, char *argv[])
       +{
       +        int fd0, fd1;
       +        int cpid;
       +        char c;
       +        char *cp, *devdir, *buf;
       +
       +        if (argc != 4) {
       +                fprint(2, "usage: %s destination network service\n", argv[0]);
       +                exits("incorrect number of arguments");
       +        }
       +        if(!(cp = malloc((long)(strlen(argv[1])+strlen(argv[2])+strlen(argv[3])+8)))) {
       +                perror("malloc");
       +                exits("malloc failed");
       +        }
       +        sprint(cp, "%s!%s!%s", argv[2], argv[1], argv[3]);
       +        if (dial(cp, &devdir, 0) < 0) {
       +                fprint(2, "dialing %s\n", cp);
       +                perror("dial");
       +                exits("can't dial");
       +        }
       +
       +        /*
       +         * Initialize the input fd, and copy bytes.
       +         */
       +
       +        if(!(buf = malloc((long)(strlen(devdir)+6)))) {
       +                perror("malloc");
       +                exits("malloc failed");
       +        }
       +        sprint(buf, "%s/data", devdir);
       +        fd0=open(buf, OREAD);
       +        fd1=open(buf, OWRITE);
       +        if(fd0<0 || fd1<0) {
       +                print("can't open", buf);
       +                exits("can't open port");
       +        }
       +        ppid = getpid();
       +        switch(cpid = fork()){
       +        case -1:
       +                perror("fork failed");
       +                exits("fork failed");
       +        case 0:
       +                close(0);
       +                close(fd1);
       +                pass(fd0, 1);        /* from remote */
       +                hangup(fd0);
       +                close(1);
       +                close(fd0);
       +                exits("");
       +        default:
       +                close(1);
       +                close(fd0);
       +                pass(0, fd1);        /* to remote */
       +                hangup(fd1);
       +                close(0);
       +                close(fd1);
       +                exits("");
       +        }
       +}
       +
       +void
       +pass(int from, int to)
       +{
       +        char buf[1024];
       +        int ppid, cpid;
       +        int n, tot = 0; 
       +
       +        while ((n=read(from, buf, sizeof(buf))) > 0) {
       +                if (n==1 && tot==0 && *buf=='\0')
       +                        break;
       +                tot += n;
       +                if (write(to, buf, n)!=n) {
       +                        perror("pass write error");
       +                        exits("pass write error");
       +                }
       +        }
       +}
   DIR diff --git a/src/cmd/lp/lp.rc b/src/cmd/lp/lp.rc
       t@@ -0,0 +1,224 @@
       +#!/usr/local/plan9/bin/rc
       +# This program enqueues the file to be printed and starts the daemon, when necessary.
       +# Make changes to /sys/src/cmd/lp/lp.rc.  Changes made directly to /rc/bin/lp will be lost.
       +
       +# rfork en        # so that environment and name space are not polluted
       +# 
       +# put 'fn sigexit { rm /tmp/lpcrap; exit interrupted }' into processes that create /tmp/lpcrap.
       +
       +ifs='         
       +'                # set ifs in case it is munged in user's environment
       +
       +LPLIB=$PLAN9/lp                # lp scripts directories and configuration file are here
       +LPBIN=$PLAN9/bin/lpbin                # lp specific binaries are here
       +LPSPOOL=$LPLIB/queue                # lp queues
       +LPLOGDIR=$LPLIB/log                # lp logs
       +
       +# $LPLIB/bin/lpscratch || exit $status
       +LPTMP=/var/tmp
       +
       +path=($PLAN9/bin /usr/local/bin /usr/bin /bin $LPLIB/bin $LPBIN)
       +
       +USAGE='usage:        lp [-d printer] [-p process] [options] [files]
       +                lp [-d printer] -q
       +                lp [-d printer] -k jobnos
       +
       +                options include:
       +                -H                no header
       +                -L                landscape mode
       +                -c<n>                make <n> copies
       +                -f<font.size>        specify font and size
       +                -i<src>                take media from <src> input bin
       +                -l<n>                print <n> lines per logical page
       +                -m<n>                magnify <n> times
       +                -n<n>                print <n> logical pages per physical page
       +                -o<i-j,k>        print only pages i-j and k
       +                -r                reverse pages
       +                -x<n>                x page offset in inches
       +                -y<n>                y page offset in inches'
       +
       +umask 000        # this doesn't work in plan 9
       +THIS_HOST=$sysname
       +if(~ $#THIS_HOST 0)
       +        THIS_HOST=`{hostname | sed 's/\..*//'}
       +if(~ $#THIS_HOST 0)
       +        THIS_HOST=gnot
       +
       +# Helpers for scripts
       +
       +# Run a program from a /sys/lib/lp subdirectory.
       +fn lpsub { 
       +        _LPSUB=$1
       +        shift
       +        _LPCMD=$1
       +        shift
       +        @{path=($LPLIB/$_LPSUB $path); $LPLIB/$_LPSUB/$_LPCMD $*}
       +}
       +
       +# Run a command with standard input from file $1.
       +# If $1 is '', use the current standard input.
       +fn lpinput {
       +        _LPFILE=$1
       +        shift
       +        if(~ $_LPFILE '') $*
       +        if not $* < $_LPFILE
       +}
       +
       +LPMACHID=$THIS_HOST
       +THIS_USERID=$user
       +LPUSERID=$THIS_USERID
       +LPLOC=''
       +
       +# Set default printer to be output device
       +if (~ $#LPDEST 0 && test -f $LPLIB/default)
       +        LPDEST=`{cat $LPLIB/default}
       +
       +# Parse option parameters
       +
       +XOFF=''
       +YOFF=''
       +POINT=''
       +FONT=''
       +LINES=''
       +LAND=''
       +COPIES=''
       +MAG=''
       +NPAG=''
       +OLIST=''
       +IBIN=''
       +REVERSE=''
       +NOHEAD=''
       +TRAY=''
       +# remove FLAGS from environment
       +FLAGD=();FLAGH=();FLAGL=();FLAGM=();FLAGQ=();FLAGc=();FLAGd=();FLAGf=()
       +FLAGi=();FLAGk=();FLAGl=();FLAGm=();FLAGn=();FLAGo=();FLAGp=();FLAGq=()
       +FLAGr=();FLAGt=();FLAGu=();FLAGx=();FLAGy=()
       +# Process options
       +eval `{getflags DHLM:1QRc:1d:1f:1i:1kl:1m:1n:1o:1p:1qrt:1u:1x:1y:1 $*}
       +if (! ~ $status '') exit $status
       +if (! ~ $#FLAGD 0) { DEBUG=1; flag x + }; if not { DEBUG=''; flag x - }
       +if (! ~ $#FLAGH 0) NOHEAD=1; if not NOHEAD=''
       +if (! ~ $#FLAGL 0) LAND=1; if not LAND=''
       +# originating machine id (for information only)
       +if (! ~ $#FLAGM 0 && ~ $LPUSERID daemon) LPMACHID=$FLAGM
       +if (! ~ $#FLAGQ 0) QONLY=1; if not QONLY=''
       +if (! ~ $#FLAGR 0) RESET=1; if not RESET=''
       +if (! ~ $#FLAGc 0) COPIES=$FLAGc; if not COPIES=1
       +if (! ~ $#FLAGd 0) {
       +        switch ($FLAGd) {
       +        case '?'; exec awk 'BEGIN{printf "device       location  host                 class\n"}
       +                        /^[^#]/        {printf "%-12s %-9s %-20s %s\n", $1, $2, $3, $6 }' $LPLIB/devices
       +        case *; LPDEST=$FLAGd
       +        }
       +}
       +if (! ~ $#FLAGf 0) eval `{echo $FLAGf | sed -e 's/([^.]*)\.([0-9.]*)/FONT=\1;POINT=\2;/'}
       +if (! ~ $#FLAGi 0) IBIN=$FLAGi
       +if (! ~ $#FLAGk 0) KILLFLAG=1; if not KILLFLAG=0
       +if (! ~ $#FLAGl 0) LINES=$FLAGl
       +if (! ~ $#FLAGm 0) MAG=$FLAGm
       +if (! ~ $#FLAGn 0) NPAG=$FLAGn
       +if (! ~ $#FLAGo 0) OLIST=-o$FLAGo
       +if (! ~ $#FLAGp 0) {
       +        switch (FLAGp) {
       +        case '?';exec ls $LPLIB/process
       +        case *;        LPPROC=$FLAGp
       +        }
       +}
       +if (! ~ $#FLAGq 0) LPQ=1; if not LPQ=0
       +if (! ~ $#FLAGr 0) {
       +        switch ($REVERSE) {
       +        case '';REVERSE=1
       +        case 1;        REVERSE=''
       +        }
       +}
       +if (! ~ $#FLAGt 0) TRAY=$FLAGt
       +# originating user id
       +if (! ~ $#FLAGu 0) LPUSERID=$FLAGu
       +if (! ~ $#FLAGx 0) XOFF=$FLAGx
       +if (! ~ $#FLAGy 0) YOFF=$FLAGy
       +
       +if (~ $#LPDEST 0) {
       +        echo 'Set environment variable LPDEST or use the
       +''-d printer'' option to set the destination.' >[1=2]
       +        exit 'LPDEST not set'
       +}
       +if (~ $LPDEST */*) {        # handles MHCC destinations like mh/lino
       +        LPLOC=`{echo $LPDEST|sed 's/^(.*)\/(.*)/\1/'}
       +        LPDEST=`{echo $LPDEST|sed 's/^(.*)\/(.*)/\2/'}
       +}
       +
       +# Fetch device info from devices file.
       +
       +LPDLINE=`{grep '^'$LPDEST'[         ]' $LPLIB/devices}
       +if (! ~ $status '') {
       +        echo 'device '$LPDEST' is not in '$LPLIB'/devices' >[1=2]
       +        exit 'LPDEST is bad'
       +}
       +LOC=$LPDLINE(2)
       +DEST_HOST=$LPDLINE(3)
       +OUTDEV=$LPDLINE(4)
       +SPEED=$LPDLINE(5)
       +LPCLASS=$LPDLINE(6)
       +if (~ $#LPPROC 0) LPPROC=$LPDLINE(7)
       +SPOOLER=$LPDLINE(8)
       +STAT=$LPDLINE(9)
       +KILL=$LPDLINE(10)
       +DAEMON=$LPDLINE(11)
       +SCHED=$LPDLINE(12)
       +
       +# On to the actual command-line processing.
       +
       +# lp -k
       +if (~ $KILLFLAG 1)
       +        switch ($KILL) {
       +        case -
       +                echo kill option not available on $LPDEST >[1=2]
       +                exit 'kill n/a'
       +        case *
       +                lpsub kill $KILL $*
       +                exit $status
       +        }
       +
       +# lp -q
       +if (~ $LPQ 1)
       +        switch ($STAT) {
       +        case -
       +                echo queue status option not available on $LPDEST >[1=2]
       +                exit 'stat option not available'
       +        case *
       +                lpsub stat $STAT $* </dev/null
       +                exit $status
       +        }
       +
       +# lp
       +DATE=`{date}
       +LPLOG=$LPLOGDIR/$LPDEST
       +if (! test -e $LPLOG) {
       +        >$LPLOG
       +        chmod +rwa $LPLOG >[2]/dev/null
       +}
       +
       +if (~ $RESET '') {        # lp file
       +        switch ($SPOOLER) {
       +        case -;        echo spooler does not exist for $LPDEST >[1=2]
       +                exit 'no spooler'
       +        case *;        path=($LPLIB/spooler $path)
       +                if (~ $#* 0) $SPOOLER
       +                if not $SPOOLER $*
       +        }
       +}
       +
       +if not {        # lp -R
       +        echo restarting daemon for printer $LPDEST >[1=2]
       +        echo `{date} restarting daemon >>$LPLOG
       +        UNLOCK $LPSPOOL/$LPDEST
       +        sleep 5
       +}
       +
       +# run daemon
       +if (~ $QONLY '') {        # not lp -Q
       +        if (! ~ $DAEMON -) {
       +                lpsub daemon $DAEMON $* >>$LPLOG >[2=1] &
       +        }
       +}
       +exit ''
   DIR diff --git a/src/cmd/lp/lpdaemon.c b/src/cmd/lp/lpdaemon.c
       t@@ -0,0 +1,457 @@
       +#include <u.h>
       +#include <sys/types.h>
       +#include <unistd.h>
       +#include <stdlib.h>
       +#include <sys/wait.h>
       +#include <fcntl.h>
       +#include <stdlib.h>
       +#include <stdio.h>
       +#include <signal.h>
       +#include <errno.h>
       +#include <time.h>
       +#include <string.h>
       +#include <stdarg.h>
       +#include <libc.h>
       +
       +#undef ctime
       +#undef wait
       +
       +/* for Plan 9 */
       +#ifdef PLAN9
       +#define LP        "/bin/lp"
       +#define TMPDIR "/var/tmp"
       +#define LPDAEMONLOG        unsharp("#9/lp/log/lpdaemonl")
       +#endif
       +/* for Tenth Edition systems */
       +#ifdef V10
       +#define LP        "/usr/bin/lp"
       +#define TMPDIR "/tmp"
       +#define LPDAEMONLOG        "/tmp/lpdaemonl"
       +#endif
       +/* for System V or BSD systems */
       +#if defined(SYSV) || defined(BSD)
       +#define LP        "/v/bin/lp"
       +#define TMPDIR "/tmp"
       +#define LPDAEMONLOG        "/tmp/lpdaemonl"
       +#endif
       +
       +#define ARGSIZ 4096
       +#define NAMELEN 30
       +
       +unsigned char argvstr[ARGSIZ];                /* arguments after parsing */
       +unsigned char *argvals[ARGSIZ/2+1];        /* pointers to arguments after parsing */
       +int ascnt = 0, argcnt = 0;        /* number of arguments parsed */
       +/* for 'stuff' gleened from lpr cntrl file */
       +struct jobinfo {
       +        char user[NAMELEN+1];
       +        char host[NAMELEN+1];
       +} *getjobinfo();
       +
       +#define MIN(a,b)        ((a<b)?a:b)
       +
       +#define        CPYFIELD(src, dst)        { while (*(src)!=' ' && *(src)!='\t' && *(src)!='\r' && *(src)!='\n' && *(src)!='\0') *(dst)++ = *(src)++; }
       +
       +#define        ACK()        write(1, "", 1)
       +#define NAK()        write(1, "\001", 1)
       +
       +#define LNBFSZ        4096
       +unsigned char lnbuf[LNBFSZ];
       +
       +#define        RDSIZE 512
       +unsigned char jobbuf[RDSIZE];
       +
       +int datafd[400], cntrlfd = -1;
       +
       +int dbgstate = 0;
       +char *dbgstrings[] = {
       +        "",
       +        "sendack1",
       +        "send",
       +        "rcvack",
       +        "sendack2",
       +        "done"
       +};
       +
       +void
       +error(char *s1, ...)
       +{
       +        FILE *fp;
       +        long thetime;
       +        char *chartime;
       +        va_list ap;
       +        char *args[8];
       +        int argno = 0;
       +
       +        if((fp=fopen(LPDAEMONLOG, "a"))==NULL) {
       +                fprintf(stderr, "cannot open %s in append mode\n", LPDAEMONLOG);
       +                return;
       +        }
       +        time(&thetime);
       +        chartime = ctime(&thetime);
       +        fprintf(fp, "%.15s [%5.5d] ", &(chartime[4]), getpid());
       +        va_start(ap, s1);
       +        while((args[argno++] = va_arg(ap, char*)) && argno<8);
       +        va_end(ap);
       +        fprintf(fp, s1, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
       +        fflush(fp);
       +        fclose(fp);
       +        return;
       +}
       +
       +void
       +forklp(int inputfd)
       +{
       +        int i, cpid;
       +        unsigned char *bp, *cp;
       +        unsigned char logent[LNBFSZ];
       +
       +        /* log this call to lp */
       +        cp = logent;
       +        for (i=1; i<argcnt; i++) {
       +                bp = argvals[i];
       +                if (cp+strlen((const char *)bp)+1 < logent+LNBFSZ-1) {
       +                        CPYFIELD(bp, cp);
       +                        *cp++ = ' ';
       +                }
       +        }
       +        *--cp = '\n';
       +        *++cp = '\0';
       +        error(logent);
       +        switch((cpid=fork())){
       +        case -1:
       +                error("fork error\n");
       +                exit(2);
       +        case 0:
       +                if (inputfd != 0)
       +                        dup2(inputfd, 0);
       +                dup2(1, 2);
       +                lseek(0, 0L, 0);
       +                execvp(LP, (char**)argvals);
       +                error("exec failed\n");
       +                exit(3);
       +        default:
       +                while(wait((int *)0) != cpid);
       +        }
       +}
       +
       +int
       +tempfile(void)
       +{
       +        int tindx = 0;
       +        char tmpf[sizeof(TMPDIR)+64];
       +        int crtfd, tmpfd;
       +
       +        sprintf(tmpf, "%s/lp%d.%d", TMPDIR, getpid(), tindx++);
       +        if((crtfd=creat(tmpf, 0666)) < 0) {
       +                error("cannot create temp file %s\n", tmpf);
       +                NAK();
       +                exit(3);
       +        }
       +        if((tmpfd=open(tmpf, 2)) < 0) {
       +                error("cannot open temp file %s\n", tmpf);
       +                NAK();
       +                exit(3);
       +        }
       +        close(crtfd);
       +        unlink(tmpf);        /* comment out for debugging */
       +        return(tmpfd);
       +}
       +
       +int
       +readfile(int outfd, int bsize)
       +{
       +        int rv;
       +
       +        dbgstate = 1;
       +        alarm(60);
       +        ACK();
       +        dbgstate = 2;
       +        for(; bsize > 0; bsize -= rv) {
       +                alarm(60);
       +                if((rv=read(0, jobbuf, MIN(bsize,RDSIZE))) < 0) {
       +                        error("error reading input, %d unread\n", bsize);
       +                        exit(4);
       +                } else if (rv == 0) {
       +                        error("connection closed prematurely\n");
       +                        exit(4);
       +                } else if((write(outfd, jobbuf, rv)) != rv) {
       +                        error("error writing temp file, %d unread\n", bsize);
       +                        exit(5);
       +                }
       +        }
       +        dbgstate = 3;
       +        alarm(60);
       +        if (((rv=read(0, jobbuf, 1))==1) && (*jobbuf=='\0')) {
       +                alarm(60);
       +                ACK();
       +                dbgstate = 4;
       +                alarm(0);
       +                return(outfd);
       +        }
       +        alarm(0);
       +        error("received bad status <%d> from sender\n", *jobbuf);
       +        error("rv=%d\n", rv);
       +        NAK();
       +        return(-1);
       +}
       +
       +/* reads a line from the input into lnbuf
       + * if there is no error, it returns
       + *   the number of characters in the buffer
       + * if there is an error and there where characters
       + *   read, it returns the negative value of the
       + *   number of characters read
       + * if there is an error and no characters were read,
       + *   it returns the negative value of 1 greater than
       + *   the size of the line buffer
       + */
       +int
       +readline(int inpfd)
       +{
       +        unsigned char *ap;
       +        int i, rv;
       +
       +        ap = lnbuf;
       +        lnbuf[0] = '\0';
       +        i = 0;
       +        alarm(60);
       +        do {
       +                rv = read(inpfd, ap, 1);
       +        } while (rv==1 && ++i && *ap != '\n' && ap++ && (i < LNBFSZ - 2));
       +        alarm(0);
       +        if (i != 0 && *ap != '\n') {
       +                *++ap = '\n';
       +                i++;
       +        }
       +        *++ap = '\0';
       +        if (rv < 0) {
       +                error("read error; lost connection\n");
       +                if (i==0) i = -(LNBFSZ+1);
       +                else i = -i;
       +        }
       +        return(i);
       +}
       +
       +int
       +getfiles(void)
       +{
       +        unsigned char *ap;
       +        int filecnt, bsize, rv;
       +
       +        filecnt = 0;
       +        /* get a line, hopefully containing a ctrl char, size, and name */
       +        for(;;) {
       +                ap = lnbuf;
       +                if ((rv=readline(0)) < 0) NAK();
       +                if (rv <= 0) {
       +                        return(filecnt);
       +                }
       +                switch(*ap++) {
       +                case '\1':                /* cleanup - data sent was bad (whatever that means) */
       +                        break;
       +                case '\2':                /* read control file */
       +                        bsize = atoi(ap);
       +                        cntrlfd = tempfile();
       +                        if (readfile(cntrlfd, bsize) < 0) {
       +                                close(cntrlfd);
       +                                NAK();
       +                                return(0);
       +                        }
       +                        break;
       +                case '\3':                /* read data file */
       +                        bsize = atoi(ap);
       +                        datafd[filecnt] = tempfile();
       +                        if (readfile(datafd[filecnt], bsize) < 0) {
       +                                close(datafd[filecnt]);
       +                                NAK();
       +                                return(0);
       +                        }
       +                        filecnt++;
       +                        break;
       +                default:
       +                        error("protocol error <%d>\n", *(ap-1));
       +                        NAK();
       +                }
       +        }
       +        return(filecnt);
       +}
       +
       +struct jobinfo *
       +getjobinfo(int fd)
       +{
       +        unsigned char *ap;
       +        int rv;
       +        static struct jobinfo info;
       +
       +        if (fd < 0) error("getjobinfo: bad file descriptor\n");
       +        if (lseek(fd, 0L, 0) < 0) {
       +                error("error seeking in temp file\n");
       +                exit(7);
       +        }
       +        /* the following strings should be < NAMELEN or else they will not
       +         * be null terminated.
       +         */
       +        strncpy(info.user, "daemon", NAMELEN);
       +        strncpy(info.host, "nowhere", NAMELEN);
       +        /* there may be a space after the name and host.  It will be filtered out
       +         * by CPYFIELD.
       +         */
       +        while ((rv=readline(fd)) > 0) {
       +                ap = lnbuf;
       +                ap[rv-1] = '\0';        /* remove newline from string */
       +                switch (*ap) {
       +                case 'H':
       +                        if (ap[1] == '\0')
       +                                strncpy(info.host, "unknown", NAMELEN);
       +                        else
       +                                strncpy(info.host, (const char *)&ap[1], NAMELEN);
       +                        info.host[strlen(info.host)] = '\0';
       +                        break;
       +                case 'P':
       +                        if (ap[1] == '\0')
       +                                strncpy(info.user, "unknown", NAMELEN);
       +                        else
       +                                strncpy(info.user, (const char *)&ap[1], NAMELEN);
       +                        info.user[strlen(info.user)] = '\0';
       +                        break;
       +                }
       +        }
       +        return(&info);
       +}
       +
       +void
       +alarmhandler(int sig) {
       +        signal(sig, alarmhandler);
       +        error("alarm at %d - %s\n", dbgstate, dbgstrings[dbgstate]);
       +}
       +
       +void
       +main(int argc, char **argv)
       +{
       +        unsigned char *ap, *bp, *cp, *savbufpnt;
       +        int i, blen, rv, saveflg, savargcnt;
       +        struct jobinfo *jinfop;
       +
       +        USED(argc);
       +        USED(argv);
       +        
       +        signal(SIGHUP, SIG_IGN);
       +        signal(SIGALRM, alarmhandler);
       +        cp = argvstr;
       +        /* setup argv[0] for exec */
       +        argvals[argcnt++] = cp;
       +        for (bp = (unsigned char *)LP, i = 0; (*bp != '\0') && (i < ARGSIZ-1); *cp++ = *bp++, i++);
       +        *cp++ = '\0';
       +        /* get the first line sent and parse it as arguments for lp */
       +        if ((rv=readline(0)) < 0)
       +                exit(1);
       +        bp = lnbuf;
       +        /* setup the remaining arguments */
       +        /* check for BSD style request */
       +        /* ^A, ^B, ^C, ^D, ^E (for BSD lpr) */
       +        switch (*bp) {
       +        case '\001':
       +        case '\003':
       +        case '\004':
       +                bp++;        /* drop the ctrl character from the input */
       +                argvals[argcnt++] = cp;
       +                *cp++ = '-'; *cp++ = 'q'; *cp++ = '\0';                /* -q */
       +                argvals[argcnt++] = cp;
       +                *cp++ = '-'; *cp++ = 'd';                         /* -d */
       +                CPYFIELD(bp, cp);                                /* printer */
       +                *cp++ = '\0';
       +                break;
       +        case '\002':
       +                bp++;        /* drop the ctrl character from the input */
       +                argvals[argcnt++] = cp;
       +                *cp++ = '-'; *cp++ = 'd';                         /* -d */
       +                CPYFIELD(bp, cp);                                /* printer */
       +                *cp++ = '\0';
       +                ACK();
       +                savargcnt = argcnt;
       +                savbufpnt = cp;
       +                while ((rv=getfiles())) {
       +                        jinfop = getjobinfo(cntrlfd);
       +                        close(cntrlfd);
       +                        argcnt = savargcnt;
       +                        cp = savbufpnt;
       +                        argvals[argcnt++] = cp;
       +                        *cp++ = '-'; *cp++ = 'M';                         /* -M */
       +                        bp = (unsigned char *)jinfop->host;
       +                        CPYFIELD(bp, cp);                                /* host name */
       +                        *cp++ = '\0';
       +                        argvals[argcnt++] = cp;
       +                        *cp++ = '-'; *cp++ = 'u';                         /* -u */
       +                        bp = (unsigned char *)jinfop->user;
       +                        CPYFIELD(bp, cp);                                /* user name */
       +                        *cp++ = '\0';
       +                        for(i=0;i<rv;i++)
       +                                forklp(datafd[i]);
       +                }
       +                exit(0);
       +        case '\005':
       +                bp++;        /* drop the ctrl character from the input */
       +                argvals[argcnt++] = cp;
       +                *cp++ = '-'; *cp++ = 'k'; *cp++ = '\0';                /* -k */
       +                argvals[argcnt++] = cp;
       +                *cp++ = '-'; *cp++ = 'd';                         /* -d */
       +                CPYFIELD(bp, cp);                                /* printer */
       +                *cp++ = '\0';
       +                argvals[argcnt++] = cp;
       +                *cp++ = '-'; ap = cp; *cp++ = 'u';                 /* -u */
       +                CPYFIELD(bp, cp);                                /* username */
       +
       +                /* deal with bug in lprng where the username is not supplied
       +                 */
       +                if (ap == (cp-1)) {
       +                        ap = (unsigned char *)"none";
       +                        CPYFIELD(ap, cp);
       +                }
       +
       +                *cp++ = '\0';
       +                datafd[0] = tempfile();
       +                blen = strlen((const char *)bp);
       +                if (write(datafd[0], bp, blen) != blen) {
       +                        error("write error\n");
       +                        exit(6);
       +                }
       +                if (write(datafd[0], "\n", 1) != 1) {
       +                        error("write error\n");
       +                        exit(6);
       +                }
       +                break;
       +        default:
       +                /* otherwise get my lp arguments */
       +                do {
       +                        /* move to next non-white space */
       +                        while (*bp==' '||*bp=='\t')
       +                                ++bp;
       +                        if (*bp=='\n') continue;
       +                        /* only accept arguments beginning with -
       +                         * this is done to prevent the printing of
       +                         * local files from the destination host
       +                         */
       +                        if (*bp=='-') {
       +                                argvals[argcnt++] = cp;
       +                                saveflg = 1;
       +                        } else
       +                                saveflg = 0;
       +                        /* move to next white space copying text to argument buffer */
       +                        while (*bp!=' ' && *bp!='\t' && *bp!='\n'
       +                            && *bp!='\0') {
       +                                *cp = *bp++;
       +                                cp += saveflg;
       +                        }
       +                        *cp = '\0';
       +                        cp += saveflg;
       +                } while (*bp!='\n' && *bp!='\0');
       +                if (readline(0) < 0) exit(7);
       +                datafd[0] = tempfile();
       +                if(readfile(datafd[0], atoi((char *)lnbuf)) < 0) {
       +                        error("readfile failed\n");
       +                        exit(8);
       +                }
       +        }
       +        forklp(datafd[0]);
       +        exit(0);
       +}
   DIR diff --git a/src/cmd/lp/lpdsend.c b/src/cmd/lp/lpdsend.c
       t@@ -0,0 +1,428 @@
       +#include <u.h>
       +#include <sys/types.h>
       +#include <unistd.h>
       +#include <sys/stat.h>
       +#include <sys/param.h>
       +#include <stdlib.h>
       +#include <fcntl.h>
       +#include <stdio.h>
       +#include <string.h>
       +#include <signal.h>
       +#include <libc.h>
       +
       +#define        REDIALTIMEOUT        15
       +#define TIMEOUT 600
       +
       +char tmpfilename[L_tmpnam+1];
       +int alarmstate = 0;
       +int debugflag = 0;
       +int killflag = 0;
       +int statflag = 0;
       +
       +void
       +cleanup(void) {
       +        unlink(tmpfilename);
       +}
       +
       +#define SBSIZE 8192
       +unsigned char sendbuf[SBSIZE];
       +
       +void
       +debug(char *str) {
       +        if (debugflag)
       +                fprintf(stderr, "%s", str);
       +}
       +
       +void
       +alarmhandler(int sig) {
       +        fprintf(stderr, "timeout occurred, check printer.\n");
       +        exit(2);
       +}
       +
       +/* send a message after each WARNPC percent of data sent */
       +#define WARNPC        5
       +
       +int
       +copyfile(int in, int out, long tosend) {
       +        int n;
       +        int sent = 0;
       +        int percent = 0;
       +
       +        if (debugflag)
       +                fprintf(stderr, "lpdsend: copyfile(%d,%d,%ld)\n",
       +                        in, out, tosend);
       +        while ((n=read(in, sendbuf, SBSIZE)) > 0) {
       +                if (debugflag)
       +                        fprintf(stderr, "lpdsend: copyfile read %d bytes from %d\n",
       +                                n, in);
       +                alarm(TIMEOUT); alarmstate = 1;
       +                if (write(out, sendbuf, n) != n) {
       +                        alarm(0);
       +                        fprintf(stderr, "write to fd %d failed\n", out);
       +                        return(0);
       +                }
       +                alarm(0);
       +                if (debugflag)
       +                        fprintf(stderr, "lpdsend: copyfile wrote %d bytes to %d\n",
       +                                n, out);
       +                sent += n;
       +                if (tosend && ((sent*100/tosend)>=(percent+WARNPC))) {
       +                        percent += WARNPC;
       +                        fprintf(stderr, ": %5.2f%% sent\n", sent*100.0/tosend);
       +                }
       +        }
       +        if (debugflag)
       +                fprintf(stderr, "lpdsend: copyfile read %d bytes from %d\n",
       +                        n, in);
       +        return(!n);
       +}
       +
       +char  strbuf[120];
       +char hostname[MAXHOSTNAMELEN], *username, *printername, *killarg;
       +char *inputname;
       +char filetype = 'o';        /* 'o' is for PostScript */
       +int seqno = 0;
       +char *seqfilename;
       +
       +void
       +killjob(int printerfd) {
       +        int strlength;
       +        if (printername==0) {
       +                fprintf(stderr, "no printer name\n");
       +                exit(1);
       +        }
       +        if (username==0) {
       +                fprintf(stderr, "no user name given\n");
       +                exit(1);
       +        }
       +        if (killarg==0) {
       +                fprintf(stderr, "no job to kill\n");
       +                exit(1);
       +        }
       +        sprintf(strbuf, "%c%s %s %s\n", '\5', printername, username, killarg);
       +        strlength = strlen(strbuf);
       +        if (write(printerfd, strbuf, strlength) != strlength) {
       +                fprintf(stderr, "write(printer) error\n");
       +                exit(1);
       +        }
       +        copyfile(printerfd, 2, 0L);
       +}
       +
       +void
       +checkqueue(int printerfd) {
       +        int strlength;
       +
       +        sprintf(strbuf, "%c%s\n", '\4', printername);
       +        strlength = strlen(strbuf);
       +        if (write(printerfd, strbuf, strlength) != strlength) {
       +                fprintf(stderr, "write(printer) error\n");
       +                exit(1);
       +        }
       +        copyfile(printerfd, 2, 0L);
       +/*
       +{        int n;
       +        unsigned char sendbuf[1];
       +        while ((n=read(printerfd, sendbuf, 1)) > 0) {
       +                write(2, sendbuf, n);
       +        }
       +}
       +*/
       +}
       +
       +void
       +getack(int printerfd, int as) {
       +        char resp;
       +        int rv;
       +
       +        alarm(TIMEOUT); alarmstate = as;
       +        if ((rv=read(printerfd, &resp, 1)) != 1 || resp != '\0') {
       +                fprintf(stderr, "getack failed: read returned %d, read value (if any) %d, alarmstate=%d\n",
       +                        rv, resp, alarmstate);
       +                exit(1);
       +        }
       +        alarm(0);
       +}
       +
       +/* send control file */
       +void
       +sendctrl(int printerfd) {
       +        char cntrlstrbuf[256];
       +        int strlength, cntrlen;
       +
       +        sprintf(cntrlstrbuf, "H%s\nP%s\n%cdfA%3.3d%s\n", hostname, username, filetype, seqno, hostname);
       +        cntrlen = strlen(cntrlstrbuf);
       +        sprintf(strbuf, "%c%d cfA%3.3d%s\n", '\2', cntrlen, seqno, hostname);
       +        strlength = strlen(strbuf);
       +        if (write(printerfd, strbuf, strlength) != strlength) {
       +                fprintf(stderr, "write(printer) error\n");
       +                exit(1);
       +        }
       +        getack(printerfd, 3);
       +        if (write(printerfd, cntrlstrbuf, cntrlen) != cntrlen) {
       +                fprintf(stderr, "write(printer) error\n");
       +                exit(1);
       +        }
       +        if (write(printerfd, "\0", 1) != 1) {
       +                fprintf(stderr, "write(printer) error\n");
       +                exit(1);
       +        }
       +        getack(printerfd, 4);
       +}
       +
       +/* send data file */
       +void
       +senddata(int inputfd, int printerfd, long size) {
       +        int strlength;
       +
       +        sprintf(strbuf, "%c%ld dfA%3.3d%s\n", '\3', size, seqno, hostname);
       +        strlength = strlen(strbuf);
       +        if (write(printerfd, strbuf, strlength) != strlength) {
       +                fprintf(stderr, "write(printer) error\n");
       +                exit(1);
       +        }
       +        getack(printerfd, 5);
       +        if (!copyfile(inputfd, printerfd, size)) {
       +                fprintf(stderr, "failed to send file to printer\n");
       +                exit(1);
       +        }
       +        if (write(printerfd, "\0", 1) != 1) {
       +                fprintf(stderr, "write(printer) error\n");
       +                exit(1);
       +        }
       +        fprintf(stderr, "%ld bytes sent, status: waiting for end of job\n", size);
       +        getack(printerfd, 6);
       +}
       +
       +void
       +sendjob(int inputfd, int printerfd) {
       +        struct stat statbuf;
       +        int strlength;
       +
       +        if (fstat(inputfd, &statbuf) < 0) {
       +                fprintf(stderr, "fstat(%s) failed\n", inputname);
       +                exit(1);
       +        }
       +        sprintf(strbuf, "%c%s\n", '\2', printername);
       +        strlength = strlen(strbuf);
       +        if (write(printerfd, strbuf, strlength) != strlength) {
       +                fprintf(stderr, "write(printer) error\n");
       +                exit(1);
       +        }
       +        getack(printerfd, 2);
       +        debug("send data\n");
       +        senddata(inputfd, printerfd, statbuf.st_size);
       +        debug("send control info\n");
       +        sendctrl(printerfd);
       +        fprintf(stderr, "%ld bytes sent, status: end of job\n", (long)statbuf.st_size);
       +}
       +
       +/*
       + *  make an address, add the defaults
       + */
       +char *
       +netmkaddr(char *linear, char *defnet, char *defsrv)
       +{
       +        static char addr[512];
       +        char *cp;
       +
       +        /*
       +         *  dump network name
       +         */
       +        cp = strchr(linear, '!');
       +        if(cp == 0){
       +                if(defnet==0){
       +                        if(defsrv)
       +                                sprintf(addr, "net!%s!%s", linear, defsrv);
       +                        else
       +                                sprintf(addr, "net!%s", linear);
       +                }
       +                else {
       +                        if(defsrv)
       +                                sprintf(addr, "%s!%s!%s", defnet, linear, defsrv);
       +                        else
       +                                sprintf(addr, "%s!%s", defnet, linear);
       +                }
       +                return addr;
       +        }
       +
       +        /*
       +         *  if there is already a service, use it
       +         */
       +        cp = strchr(cp+1, '!');
       +        if(cp)
       +                return linear;
       +
       +        /*
       +         *  add default service
       +         */
       +        if(defsrv == 0)
       +                return linear;
       +        sprintf(addr, "%s!%s", linear, defsrv);
       +
       +        return addr;
       +}
       +
       +void
       +main(int argc, char *argv[]) {
       +        int c, usgflg = 0;
       +        char *desthostname;
       +        int printerfd;
       +        int inputfd;
       +        int sendport;
       +        char portstr[4];
       +
       +        desthostname = nil;
       +        if (signal(SIGALRM, alarmhandler) == SIG_ERR) {
       +                fprintf(stderr, "failed to set alarm handler\n");
       +                exit(1);
       +        }
       +        while ((c = getopt(argc, argv, "Dd:k:qs:t:H:P:")) != -1)
       +                switch (c) {
       +                case 'D':
       +                        debugflag = 1;
       +                        debug("debugging on\n");
       +                        break;
       +                case 'd':
       +                        printername = optarg;
       +                        break;
       +                case 'k':
       +                        if (statflag) {
       +                                fprintf(stderr, "cannot have both -k and -q flags\n");
       +                                exit(1);
       +                        }        
       +                        killflag = 1;
       +                        killarg = optarg;
       +                        break;
       +                case 'q':
       +                        if (killflag) {
       +                                fprintf(stderr, "cannot have both -q and -k flags\n");
       +                                exit(1);
       +                        }        
       +                        statflag = 1;
       +                        break;
       +                case 's':
       +                        seqno = strtol(optarg, NULL, 10);
       +                        if (seqno < 0 || seqno > 999)
       +                                seqno = 0;
       +                        break;
       +                case 't':
       +                        switch (filetype) {
       +                        case 'c':
       +                        case 'd':
       +                        case 'f':
       +                        case 'g':
       +                        case 'l':
       +                        case 'n':
       +                        case 'o':
       +                        case 'p':
       +                        case 'r':
       +                        case 't':
       +                        case 'v':
       +                        case 'z':
       +                                filetype = optarg[0];
       +                                break;
       +                        default:
       +                                usgflg++;
       +                                break;
       +                        }
       +                        break;
       +                case 'H':
       +                        strncpy(hostname, optarg, MAXHOSTNAMELEN);
       +                        break;
       +                case 'P':
       +                        username = optarg;
       +                        break;
       +                default:
       +                case '?':
       +                        fprintf(stderr, "unknown option %c\n", c);
       +                        usgflg++;
       +                }
       +        if (argc < 2) usgflg++;
       +        if (optind < argc) {
       +                desthostname = argv[optind++];
       +        } else
       +                usgflg++;
       +        if (usgflg) {
       +                fprintf(stderr, "usage: to send a job - %s -d printer -H hostname -P username [-s seqno] [-t[cdfgklnoprtvz]] desthost [filename]\n", argv[0]);
       +                fprintf(stderr, "     to check status - %s -d printer -q desthost\n", argv[0]);
       +                fprintf(stderr, "       to kill a job - %s -d printer -P username -k jobname desthost\n", argv[0]);
       +                exit(1);
       +        }
       +
       +/* make sure the file to send is here and ready
       + * otherwise the TCP connection times out.
       + */
       +         inputfd = -1;
       +        if (!statflag && !killflag) {
       +                if (optind < argc) {
       +                        inputname = argv[optind++];
       +                        debug("open("); debug(inputname); debug(")\n");
       +                        inputfd = open(inputname, O_RDONLY);
       +                        if (inputfd < 0) {
       +                                fprintf(stderr, "open(%s) failed\n", inputname);
       +                                exit(1);
       +                        }
       +                } else {
       +                        inputname = "stdin";
       +                        tmpnam(tmpfilename);
       +                        debug("using stdin\n");
       +                        if ((inputfd = create(tmpfilename, ORDWR, 0600)) < 0) {
       +                                fprintf(stderr, "open(%s) failed\n", tmpfilename);
       +                                exit(1);
       +                        }
       +                        atexit(cleanup);
       +                        debug("copy input to temp file ");
       +                        debug(tmpfilename);
       +                        debug("\n");
       +                        if (!copyfile(0, inputfd, 0L)) {
       +                                fprintf(stderr, "failed to copy file to temporary file\n");
       +                                exit(1);
       +                        }
       +                        if (lseek(inputfd, 0L, 0) < 0) {
       +                                fprintf(stderr, "failed to seek back to the beginning of the temporary file\n");
       +                                exit(1);
       +                        }
       +                }
       +        }
       +
       +        sprintf(strbuf, "%s", netmkaddr(desthostname, "tcp", "printer"));
       +        fprintf(stderr, "connecting to %s\n", strbuf);
       +        for (sendport=721; sendport<=731; sendport++) {
       +                sprintf(portstr, "%3.3d", sendport);
       +                fprintf(stderr, " trying from port %s...", portstr);
       +                debug(" dial("); debug(strbuf); debug(", "); debug(portstr); debug(", 0, 0) ...");
       +                printerfd = dial(strbuf, portstr, 0, 0);
       +                if (printerfd >= 0) {
       +                        fprintf(stderr, "connected\n");
       +                        break;
       +                }
       +                fprintf(stderr, "failed\n");
       +                sleep(REDIALTIMEOUT);
       +        }
       +        if (printerfd < 0) {
       +                fprintf(stderr, "Cannot open a valid port!\n");
       +                fprintf(stderr, "-  All source ports [721-731] may be busy.\n");
       +                fprintf(stderr, "-  Is recipient ready and online?\n");
       +                fprintf(stderr, "-  If all else fails, cycle the power!\n");
       +                exit(1);
       +        }
       +/*        hostname[8] = '\0'; */
       +#ifndef PLAN9
       +        if (gethostname(hostname, sizeof(hostname)) < 0) {
       +                perror("gethostname");
       +                exit(1);
       +        }
       +#endif
       +/*        char *hnend;
       +        if ((hnend = strchr(hostname, '.')) != NULL)
       +                *hnend = '\0';
       + */
       +        if (statflag) {
       +                checkqueue(printerfd);
       +        } else if (killflag) {
       +                killjob(printerfd);
       +        } else {
       +                sendjob(inputfd, printerfd);
       +        }
       +        exit(0);
       +}
   DIR diff --git a/src/cmd/lp/lpsend.c b/src/cmd/lp/lpsend.c
       t@@ -0,0 +1,341 @@
       +#ifdef plan9
       +
       +#include <u.h>
       +#include <libc.h>
       +#define         stderr        2
       +
       +#define RDNETIMEOUT        60000
       +#define WRNETIMEOUT        60000
       +
       +#else
       +
       +/* not for plan 9 */
       +#include <stdio.h>
       +#include <errno.h>
       +#include <time.h>
       +#include <fcntl.h>
       +#include <signal.h>
       +
       +#define        create        creat
       +#define        seek        lseek
       +#define        fprint        fprintf
       +#define        sprint        sprintf
       +#define        exits        exit
       +
       +#define        ORDWR        O_RDWR
       +#define        OTRUNC        O_TRUNC
       +#define        ORCLOSE        0
       +
       +#define RDNETIMEOUT        60
       +#define WRNETIMEOUT        60
       +
       +#endif
       +
       +#define MIN(a,b)        ((a<b)?a:b)
       +
       +#define        ACK(a)        write(a, "", 1)
       +#define NAK(a)        write(a, "\001", 1)
       +
       +#define LPDAEMONLOG        "/tmp/lpdaemonl"
       +
       +#define LNBFSZ        4096
       +char lnbuf[LNBFSZ];
       +int dbgstate = 0;
       +char *dbgstrings[] = {
       +        "",
       +        "rcvack1",
       +        "send",
       +        "rcvack2",
       +        "response",
       +        "done"
       +};
       +
       +#ifdef plan9
       +
       +void
       +error(int level, char *s1, ...)
       +{
       +        va_list ap;
       +        long thetime;
       +        char *chartime;
       +        char *args[8];
       +        int argno = 0;
       +
       +        if (level == 0) {
       +                time(&thetime);
       +                chartime = ctime(thetime);
       +                fprint(stderr, "%.15s ", &(chartime[4]));
       +        }
       +        va_start(ap, s1);
       +        while(args[argno++] = va_arg(ap, char*));
       +        va_end(ap);
       +        fprint(stderr, s1, *args);
       +        return;
       +}
       +
       +int
       +alarmhandler(void *foo, char *note) {
       +        USED(foo);
       +        if(strcmp(note, "alarm")==0) {
       +                fprint(stderr, "alarm at %d - %s\n", dbgstate, dbgstrings[dbgstate]);
       +                return(1);
       +        } else return(0);
       +}
       +
       +#else
       +
       +void
       +error(int level, char *s1, ...)
       +{
       +        time_t thetime;
       +        char *chartime;
       +
       +        if (level == 0) {
       +                time(&thetime);
       +                chartime = ctime(&thetime);
       +                fprintf(stderr, "%.15s ", &(chartime[4]));
       +        }
       +        fprintf(stderr, s1, (&s1+1));
       +        return;
       +}
       +
       +void
       +alarmhandler() {
       +        fprintf(stderr, "alarm at %d - %s\n", dbgstate, dbgstrings[dbgstate]);
       +}
       +
       +#endif
       +
       +/* get a line from inpfd using nonbuffered input.  The line is truncated if it is too
       + * long for the buffer.  The result is left in lnbuf and the number of characters
       + * read in is returned.
       + */
       +int
       +readline(int inpfd)
       +{
       +        register char *ap;
       +        register int i;
       +
       +        ap = lnbuf;
       +        i = 0;
       +        do {
       +                if (read(inpfd, ap, 1) != 1) {
       +                        error(0, "read error in readline, fd=%d\n", inpfd);
       +                        break;
       +                }
       +        } while ((++i < LNBFSZ - 2) && *ap++ != '\n');
       +        if (i == LNBFSZ - 2) {
       +                *ap = '\n';
       +                i++;
       +        }
       +        *ap = '\0';
       +        return(i);
       +}
       +
       +#define        RDSIZE 512
       +char jobbuf[RDSIZE];
       +
       +int
       +pass(int inpfd, int outfd, int bsize)
       +{
       +        int bcnt = 0;
       +        int rv = 0;
       +
       +        for(bcnt=bsize; bcnt > 0; bcnt -= rv) {
       +                alarm(WRNETIMEOUT);        /* to break hanging */
       +                if((rv=read(inpfd, jobbuf, MIN(bcnt,RDSIZE))) < 0) {
       +                        error(0, "read error during pass, %d remaining\n", bcnt);
       +                        break;
       +                } else if((write(outfd, jobbuf, rv)) != rv) {
       +                        error(0, "write error during pass, %d remaining\n", bcnt);
       +                        break;
       +                }
       +        }
       +        alarm(0);
       +        return(bcnt);
       +}
       +        
       +/* get whatever stdin has and put it into the temporary file.
       + * return the file size.
       + */
       +int
       +prereadfile(int inpfd)
       +{
       +        int rv, bsize;
       +
       +        bsize = 0;
       +        do {
       +                if((rv=read(0, jobbuf, RDSIZE))<0) {
       +                        error(0, "read error while making temp file\n");
       +                        exits("read error while making temp file");
       +                } else if((write(inpfd, jobbuf, rv)) != rv) {
       +                        error(0, "write error while making temp file\n");
       +                        exits("write error while making temp file");
       +                }
       +                bsize += rv;
       +        } while (rv!=0);
       +        return(bsize);
       +}
       +
       +int
       +tempfile(void)
       +{
       +        static int tindx = 0;
       +        char tmpf[20];
       +        int tmpfd;
       +
       +        sprint(tmpf, "/var/tmp/lp%d.%d", getpid(), tindx++);
       +        if((tmpfd=create(tmpf,
       +
       +#ifdef plan9
       +
       +                                                ORDWR|OTRUNC,
       +
       +#endif
       +
       +                                                                                                0666)) < 0) {
       +                error(0, "cannot create temp file %s\n", tmpf);
       +                exits("cannot create temp file");
       +        }
       +        close(tmpfd);
       +        if((tmpfd=open(tmpf, ORDWR
       +
       +#ifdef plan9
       +
       +                                                |ORCLOSE|OTRUNC
       +
       +#endif
       +
       +                                                                        )) < 0) {
       +                error(0, "cannot open temp file %s\n", tmpf);
       +                exits("cannot open temp file");
       +        }
       +        return(tmpfd);
       +}
       +
       +int
       +recvACK(int netfd)
       +{
       +        int rv;
       +
       +        *jobbuf = '\0';
       +        alarm(RDNETIMEOUT);
       +        if (read(netfd, jobbuf, 1)!=1 || *jobbuf!='\0') {
       +                error(0, "failed to receive ACK, ");
       +                if (*jobbuf == '\0')
       +                        error(1, "read failed\n");
       +                else
       +                        error(1, "received <0x%x> instead\n", *jobbuf);
       +                rv = 0;
       +        } else rv = 1;
       +        alarm(0);
       +        return(rv);
       +}
       +
       +void
       +main(int argc, char *argv[])
       +{
       +        char *devdir;
       +        int i, rv, netfd, bsize;
       +        int datafd;
       +
       +#ifndef plan9
       +
       +        void (*oldhandler)();
       +
       +#endif
       +
       +        devdir = nil;
       +        /* make connection */
       +        if (argc != 2) {
       +                fprint(stderr, "usage: %s network!destination!service\n", argv[0]);
       +                exits("incorrect number of arguments");
       +        }
       +
       +        /* read options line from stdin into lnbuf */
       +        i = readline(0);
       +
       +        /* read stdin into tempfile to get size */
       +        datafd = tempfile();
       +        bsize = prereadfile(datafd);
       +
       +        /* network connection is opened after data is in to avoid timeout */
       +        if ((netfd=dial(argv[1], 0, 0, 0)) < 0) {
       +                fprint(stderr, "dialing %s\n", devdir);
       +                perror("dial");
       +                exits("can't dial");
       +        }
       +
       +        /* write out the options we read above */
       +        if (write(netfd, lnbuf, i) != i) {
       +                error(0, "write error while sending options\n");
       +                exits("write error while sending options");
       +        }
       +
       +        /* send the size of the file to be sent */
       +        sprint(lnbuf, "%d\n", bsize);
       +        i = strlen(lnbuf);
       +        if ((rv=write(netfd, lnbuf, i)) != i) {
       +                perror("write error while sending size");
       +                error(0, "write returned %d\n", rv);
       +                exits("write error while sending size");
       +        }
       +
       +        if (seek(datafd, 0L, 0) < 0) {
       +                error(0, "error seeking temp file\n");
       +                exits("seek error");
       +        }
       +        /* mirror performance in readfile() in lpdaemon */
       +
       +#ifdef plan9
       +
       +        atnotify(alarmhandler, 1);
       +
       +#else
       +
       +        oldhandler = signal(SIGALRM, alarmhandler);
       +
       +#endif
       +
       +        dbgstate = 1;
       +        if(!recvACK(netfd)) {
       +                error(0, "failed to receive ACK before sending data\n");
       +                exits("recv ack1 failed");
       +        }
       +        dbgstate = 2;
       +        if ((i=pass(datafd, netfd, bsize)) != 0) {
       +                NAK(netfd);
       +                error(0, "failed to send %d bytes\n", i);
       +                exits("send data failed");
       +        }
       +        ACK(netfd);
       +        dbgstate = 3;
       +        if(!recvACK(netfd)) {
       +                error(0, "failed to receive ACK after sending data\n");
       +                exits("recv ack2 failed");
       +        }
       +
       +        /* get response, as from lp -q */
       +        dbgstate = 4;
       +        while((rv=read(netfd, jobbuf, RDSIZE)) > 0) {
       +                if((write(1, jobbuf, rv)) != rv) {
       +                        error(0, "write error while sending to stdout\n");
       +                        exits("write error while sending to stdout");
       +                }
       +        }
       +        dbgstate = 5;
       +
       +#ifdef plan9
       +
       +        atnotify(alarmhandler, 0);
       +        /* close down network connections and go away */
       +        exits("");
       +
       +#else
       +
       +        signal(SIGALRM, oldhandler);
       +        exit(0);
       +
       +#endif
       +
       +}
   DIR diff --git a/src/cmd/lp/lpsend.rc b/src/cmd/lp/lpsend.rc
       t@@ -0,0 +1,18 @@
       +#!/bin/rc
       +if (! ~ $DEBUG '') { flag x + }
       +if (test -e /net/tcp/clone) {
       +        dialstring=`{ndb/query sys $1 dom}
       +        network=tcp
       +        if (~ $#dialstring 0 || ! ~ $dialstring '') {
       +                dialstring=$1
       +        }
       +        if(lpsend $network^!^$dialstring^!printer) exit ''
       +        rv='tcp failed'
       +}
       +if not rv='no tcp'
       +
       +
       +if (! ~ $dialstring '')
       +        exit 'lpsend: no dialstring'
       +if not
       +        exit 'lpsend: '^$rv
   DIR diff --git a/src/cmd/lp/mkfile b/src/cmd/lp/mkfile
       t@@ -0,0 +1,30 @@
       +<$PLAN9/src/mkhdr
       +
       +TARG=\
       +        lpdsend \
       +        lpsend \
       +        LOCK \
       +        lpdaemon
       +
       +OFILES=
       +
       +HFILES=
       +
       +BIN=$PLAN9/lp/bin
       +<$PLAN9/src/mkmany
       +CFLAGS=-Dplan9
       +
       +install:V:        $PLAN9/lp/bin/lpsend.rc $PLAN9/bin/lp
       +
       +$PLAN9/lp/bin/lpsend.rc:        lpsend.rc
       +        cp $prereq $target
       +
       +lpdsend.$O:        lpdsend.c
       +        $CC $CFLAGS -D_POSIX_SOURCE -D_BSD_EXTENSION -D_NET_EXTENSION -DPLAN9 -'DMAXHOSTNAMELEN=64' lpdsend.c
       +
       +lpdaemon.$O:        lpdaemon.c
       +        $CC $CFLAGS -D_POSIX_SOURCE -DPLAN9  lpdaemon.c
       +
       +$PLAN9/bin/lp:        lp.rc
       +        cp $prereq $target
       +