summaryrefslogtreecommitdiff
path: root/sys/src/cmd/lp
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/lp
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/lp')
-rwxr-xr-xsys/src/cmd/lp/LOCK.c57
-rwxr-xr-xsys/src/cmd/lp/ipcopen.c92
-rwxr-xr-xsys/src/cmd/lp/lp.rc212
-rwxr-xr-xsys/src/cmd/lp/lpdaemon.c447
-rwxr-xr-xsys/src/cmd/lp/lpdsend.c438
-rwxr-xr-xsys/src/cmd/lp/lpsend.c320
-rwxr-xr-xsys/src/cmd/lp/lpsend.rc18
-rwxr-xr-xsys/src/cmd/lp/mkfile31
8 files changed, 1615 insertions, 0 deletions
diff --git a/sys/src/cmd/lp/LOCK.c b/sys/src/cmd/lp/LOCK.c
new file mode 100755
index 000000000..c8892d4f5
--- /dev/null
+++ b/sys/src/cmd/lp/LOCK.c
@@ -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, ORDWR, DMEXCL|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/sys/src/cmd/lp/ipcopen.c b/sys/src/cmd/lp/ipcopen.c
new file mode 100755
index 000000000..666f64c4f
--- /dev/null
+++ b/sys/src/cmd/lp/ipcopen.c
@@ -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");
+ }
+ }
+}
diff --git a/sys/src/cmd/lp/lp.rc b/sys/src/cmd/lp/lp.rc
new file mode 100755
index 000000000..92b9ceab2
--- /dev/null
+++ b/sys/src/cmd/lp/lp.rc
@@ -0,0 +1,212 @@
+#!/bin/rc
+# lp - 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=/sys/lib/lp # lp scripts directories and configuration file are here
+LPBIN=/$cputype/bin/aux # lp specific binaries are here
+LPSPOOL=$LPLIB/queue # lp queues
+LPLOGDIR=$LPLIB/log # lp logs
+
+$LPLIB/bin/lpscratch
+x=$status
+if(! ~ $x '') exit $x
+
+# build /bin from the ground up
+bind /$cputype/bin /bin # general compiled binaries
+bind -a /rc/bin /bin # general rc scripts
+# This needs to be fixed for the real thing
+bind -a $LPLIB/bin /bin # lp specific rc scripts
+bind -a $LPBIN /bin # lp specific compiled binaries
+path=(/bin)
+
+if (! test -w /tmp) bind -bc $LPLIB/tmp /tmp
+
+USAGE='usage: lp [-d printer] [-p process] [options] [files]
+ lp [-d printer] -q
+ lp [-d printer] -k jobnos
+
+ options include:
+ -D turn on debugging output
+ -H no header
+ -L landscape mode
+ -M<mach> print on machine <mach>
+ -Q put task only into the queue
+ -R restart printer daemon
+ -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
+ -u<userid> print as <userid>
+ -x<n> x page offset in inches
+ -y<n> y page offset in inches
+'
+
+# umask 000 # this doesn't work in plan 9
+if (~ $#sysname 0)
+ THIS_HOST=plan9
+if not {
+ THIS_HOST=`{ndb/query sys $sysname dom}
+ if(~ $#THIS_HOST 0)
+ THIS_HOST=$sysname
+}
+
+LPMACHID=$THIS_HOST
+THIS_USERID=$user
+LPUSERID=$THIS_USERID
+LPLOC=''
+
+# Set default printer to be output device
+if (~ $#LPDEST 0 && test -f $LPLIB/defdevice) LPDEST=`{cat $LPLIB/defdevice}
+
+# option parameters
+COPIES=1
+FONT=''
+IBIN=''
+KILLFLAG=0
+LAND=''
+LINES=''
+LPQ=0
+MAG=''
+NOHEAD=''
+NPAG=''
+OLIST=''
+POINT=''
+RESET=''
+REVERSE=''
+QONLY=''
+TRAY=''
+XOFF=''
+YOFF=''
+
+# Process options
+flagfmt='D,H,L,Q,R,r,q,M mach,c copies,d printer,f font.size,i src,k jobnos,l lines,m magnify,n lpages,o pages,p proc,u userid,x offset,y offset'
+argv0=lp
+
+if(! ifs=() eval `{aux/getflags $*}) {
+ echo $USAGE
+ exit usage
+}
+if(~ $flagd '?'){
+ awk 'BEGIN {print "device location host class"}
+/^[^#]/ { printf "%-12s %-9s %-22s %s\n", $1, $2, $3, $6 }' $LPLIB/devices
+ exit
+}
+if(~ $flagp '?'){
+ ls $LPLIB/process
+ exit
+}
+
+if (! ~ $#flagD 0) { DEBUG=1; flag x + }; if not { DEBUG=''; flag x - }
+if (! ~ $#flagH 0) NOHEAD=1
+if (! ~ $#flagL 0) LAND=1
+if (! ~ $#flagM 0 && ~ $LPUSERID daemon) LPMACHID=$flagM
+if (! ~ $#flagQ 0) QONLY=1
+if (! ~ $#flagR 0) RESET=1
+if (! ~ $#flagc 0) COPIES=$flagc
+if(! ~ $#flagd 0) 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 (! ~ $#flagl 0) LINES=$flagl
+if (! ~ $#flagm 0) MAG=$flagm
+if (! ~ $#flagn 0) NPAG=$flagn
+if (! ~ $#flago 0) OLIST=-o^$flago
+if (! ~ $#flagp 0) LPPROC=$flagp
+if (! ~ $#flagq 0) LPQ=1
+if (! ~ $#flagr 0) REVERSE=1
+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/'}
+}
+
+# look up device, get info
+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)
+
+if (~ $LPCLASS *nohead*)
+ NOHEAD=1
+if (~ $LPCLASS *duplex*)
+ DUPLEX=1
+
+if (~ $#SCHED 0) SCHED=FIFO # everyone uses FIFO
+if (~ $KILLFLAG 1)
+ switch ($KILL) {
+ case -; echo kill option not available on $LPDEST >[1=2]
+ exit 'kill n/a'
+ case *; bind -b $LPLIB/kill /bin
+ exec $KILL $*
+ exit 'kill command '"$KILL"' not found'
+ }
+if (~ $LPQ 1)
+ switch ($STAT) {
+ case -; echo queue status option not available on $LPDEST >[1=2]
+ exit 'stat option not available'
+ case *; bind -b $LPLIB/stat /bin
+ exec $STAT $* < /dev/null
+ exit 'stat command '"$STAT"' not found'
+ }
+DATE=`{date}
+LPLOG=$LPLOGDIR/$LPDEST
+if (! test -e $LPLOG) {
+ >$LPLOG
+ chmod +rwa $LPLOG >[2]/dev/null
+}
+
+if (~ $RESET '') {
+ switch ($SPOOLER) {
+ case -; echo spooler does not exist for $LPDEST >[1=2]
+ exit 'no spooler'
+ case *; bind -b $LPLIB/spooler /bin
+ if (~ $#* 0) $SPOOLER
+ if not $SPOOLER $*
+ }
+}
+if not {
+ echo restarting daemon for printer $LPDEST >[1=2]
+ UNLOCK $LPSPOOL/$LPDEST
+ sleep 5
+}
+
+# run daemon
+if (~ $QONLY '') {
+ if (! ~ $DAEMON -) {
+ bind -b $LPLIB/daemon /bin
+ $DAEMON $* >>$LPLOG >[2=1] &
+ }
+}
+exit ''
diff --git a/sys/src/cmd/lp/lpdaemon.c b/sys/src/cmd/lp/lpdaemon.c
new file mode 100755
index 000000000..b646a005b
--- /dev/null
+++ b/sys/src/cmd/lp/lpdaemon.c
@@ -0,0 +1,447 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+/* for Plan 9 */
+#ifdef PLAN9
+#define LP "/bin/lp"
+#define TMPDIR "/sys/lib/lp/tmp"
+#define LPDAEMONLOG "/sys/lib/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]);
+ fclose(fp);
+}
+
+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((const char *)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, (const char **)argvals);
+ error("exec failed\n");
+ exit(3);
+ default:
+ while(wait((int *)0) != cpid)
+ ;
+ }
+}
+
+int
+tempfile(void)
+{
+ static 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((const char *)ap);
+ cntrlfd = tempfile();
+ if (readfile(cntrlfd, bsize) < 0) {
+ close(cntrlfd);
+ NAK();
+ return(0);
+ }
+ break;
+ case '\3': /* read data file */
+ bsize = atoi((const char *)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]);
+}
+
+main()
+{
+ unsigned char *ap, *bp, *cp, *savbufpnt;
+ int i, blen, rv, saveflg, savargcnt;
+ struct jobinfo *jinfop;
+
+ 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((const char *)lnbuf)) < 0) {
+ error("readfile failed\n");
+ exit(8);
+ }
+ }
+ forklp(datafd[0]);
+ exit(0);
+}
diff --git a/sys/src/cmd/lp/lpdsend.c b/sys/src/cmd/lp/lpdsend.c
new file mode 100755
index 000000000..308028bef
--- /dev/null
+++ b/sys/src/cmd/lp/lpdsend.c
@@ -0,0 +1,438 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+
+#define REDIALTIMEOUT 15
+#ifdef PLAN9
+#include <Plan9libnet.h>
+#endif
+
+enum {
+ TIMEOUT = 30*60,
+ SBSIZE = 8192,
+};
+
+char tmpfilename[L_tmpnam+1];
+unsigned char sendbuf[SBSIZE];
+
+int alarmstate = 0;
+int debugflag = 0;
+int killflag = 0;
+int statflag = 0;
+
+void
+cleanup(void)
+{
+ unlink(tmpfilename);
+}
+
+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 n, strlength;
+ unsigned char sendbuf[1];
+
+ 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);
+/*
+ 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%d 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, "%d 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", 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)
+ snprintf(addr, sizeof addr, "net!%s!%s", linear, defsrv);
+ else
+ snprintf(addr, sizeof addr, "net!%s", linear);
+ }
+ else {
+ if(defsrv)
+ snprintf(addr, sizeof addr, "%s!%s!%s", defnet, linear, defsrv);
+ else
+ snprintf(addr, sizeof 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;
+}
+
+main(int argc, char *argv[])
+{
+ int c, usgflg = 0, inputfd, printerfd, sendport;
+ char *desthostname, *hnend;
+ char portstr[4];
+
+ 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.
+ */
+ 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 = open(tmpfilename, O_RDWR|O_CREAT, 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
+/* 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/sys/src/cmd/lp/lpsend.c b/sys/src/cmd/lp/lpsend.c
new file mode 100755
index 000000000..8b8bd24e7
--- /dev/null
+++ b/sys/src/cmd/lp/lpsend.c
@@ -0,0 +1,320 @@
+#ifdef plan9
+
+#include <u.h>
+#include <libc.h>
+
+enum {
+ stderr = 2,
+ RDNETIMEOUT = 30*60*1000,
+ WRNETIMEOUT = RDNETIMEOUT,
+};
+#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);
+}
+
+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);
+}
+
+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 tindx = 0;
+ char tmpf[20];
+ int tmpfd;
+
+ sprint(tmpf, "/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, datafd;
+#ifndef plan9
+ void (*oldhandler)();
+#endif
+
+ /* make connection */
+ if (argc != 2) {
+ fprint(stderr, "usage: %s network!destination!service\n",
+ argv[0]);
+ exits("usage");
+ }
+
+ /* 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 ");
+ perror(argv[1]);
+ 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 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 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/sys/src/cmd/lp/lpsend.rc b/sys/src/cmd/lp/lpsend.rc
new file mode 100755
index 000000000..74806cded
--- /dev/null
+++ b/sys/src/cmd/lp/lpsend.rc
@@ -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/sys/src/cmd/lp/mkfile b/sys/src/cmd/lp/mkfile
new file mode 100755
index 000000000..0c40cc9e6
--- /dev/null
+++ b/sys/src/cmd/lp/mkfile
@@ -0,0 +1,31 @@
+</$objtype/mkfile
+
+TARG=lpdsend \
+ lpsend \
+ LOCK \
+ lpdaemon
+
+OFILES=
+
+HFILES=
+
+BIN=/$objtype/bin/aux
+</sys/src/cmd/mkmany
+CFLAGS=-Dplan9
+
+installall:V: /sys/lib/lp/bin/lpsend.rc
+
+/sys/lib/lp/bin/lpsend.rc: lpsend.rc
+ cp $target $prereq
+
+$O.lpdsend: lpdsend.$O
+ pcc -o $target lpdsend.$O
+
+lpdsend.$O: lpdsend.c
+ pcc -c -D_POSIX_SOURCE -D_BSD_EXTENSION -D_NET_EXTENSION -DPLAN9 -'DMAXHOSTNAMELEN=64' lpdsend.c
+
+$O.lpdaemon: lpdaemon.$O
+ pcc -o $target lpdaemon.$O
+
+lpdaemon.$O: lpdaemon.c
+ pcc -c -D_POSIX_SOURCE -DPLAN9 lpdaemon.c