tInitial lp. - plan9port - [fork] Plan 9 from user space
git clone git://src.adamsgaard.dk/plan9port
Log
Files
Refs
README
LICENSE
---
commit 262ecfed9f7e39811f34517d82e848b8ec20f863
parent 2863ba101f0c9fec34756948e263cd534a3634ee
Author: rsc 
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(-)
---
diff --git a/src/cmd/lp/LOCK.c b/src/cmd/lp/LOCK.c
t@@ -0,0 +1,57 @@
+#include 
+#include 
+
+/* 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("");
+}
diff --git a/src/cmd/lp/ipcopen.c b/src/cmd/lp/ipcopen.c
t@@ -0,0 +1,92 @@
+#include 
+#include 
+
+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");
+                }
+        }
+}
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                make  copies
+                -f        specify font and size
+                -i                take media from  input bin
+                -l                print  lines per logical page
+                -m                magnify  times
+                -n                print  logical pages per physical page
+                -o        print only pages i-j and k
+                -r                reverse pages
+                -x                x page offset in inches
+                -y                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 $* $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 ''
diff --git a/src/cmd/lp/lpdaemon.c b/src/cmd/lp/lpdaemon.c
t@@ -0,0 +1,457 @@
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#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 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
diff --git a/src/cmd/lp/lpdsend.c b/src/cmd/lp/lpdsend.c
t@@ -0,0 +1,428 @@
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#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);
+}
diff --git a/src/cmd/lp/lpsend.c b/src/cmd/lp/lpsend.c
t@@ -0,0 +1,341 @@
+#ifdef plan9
+
+#include 
+#include 
+#define         stderr        2
+
+#define RDNETIMEOUT        60000
+#define WRNETIMEOUT        60000
+
+#else
+
+/* not for plan 9 */
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#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 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
+
+}
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
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
+