schreiner@iravcl.ira.uka.de (09/29/89)
This is a simple version of lpr(1) and lpd(1) that i wrote. It allowes several processes to use the printer via lpr simultaneously. To keep things small i didn't use the directory(3) routines, sorry. MAN pages are included. Ralf Wenk, using a friends account ------------------------ Cut here ---------------------------------------- echo x - Makefile sed 's/^X//' > Makefile << '/' X# Makefile for lpr, lpd, cancel X XPGMS = lpr lpd cancel X Xall : $(PGMS) X X Xlpr : lpr.c X cc -O -o $@ $? X chmem =2000 $@ X Xlpd : lpd.c X cc -O -o $@ $? X chmem =2000 $@ X Xcancel : cancel.c X cc -O -o $@ $? X chmem =2000 $@ X / echo x - cancel.1 sed 's/^X//' > cancel.1 << '/' XCOMMANDS X cancel - line printer job remover X XINVOCATION X cancel file ... X XEXPLANATION X This program unlinks files from the line printer daemon directory X for cancelling the job. X XFILES X /usr/spool/lpd/* X XBUGS X Actual the line printer daemon ins't told when the job is canceled, X so it continues working. X / echo x - cancel.c sed 's/^X//' > cancel.c << '/' X/************************************************************************** X * cancel - line printer job remover X * This program unlinks files from the line printer daemon directory. X * Actual the line printer daemon ins't told when the job is canceled, X * so it continues working. X * X * Ralf Wenk last update: Mon Jul 10 21:12:09 1989 X **************************************************************************/ X X#define NULL ( char *)0 X#define BLOCK 64 X Xchar spool[BLOCK]; X Xextern char *rindex(); X X Xvoid panic ( st1 ) Xchar *st1; X{ X std_err( st1 ); X std_err(\N); X} /* panic */ X X X/************************************************************************** X * remove given file(s) from /usr/spool/lpd X **************************************************************************/ X Xmain(argc, argv) Xint argc; Xchar *argv[]; X{ X register int i, ret; X register char *cptr; X X ret = 0; X if ( argc > 1 ) X { X for ( i = 1; i < argc; i++ ) /* all filenames given */ X { X strcpy( spool, sp_dir ); X if (( cptr = rindex( argv[i],'/')) != NULL ) X strcat( spool, cptr + 1 ); X else X strcat( spool, argv[i] ); X if ( unlink( spool )) X panic( argv[i] ); X } X } X else X ret = 1; X exit( ret ); X} /* main */ X / echo x - lpd.c sed 's/^X//' > lpd.c << '/' X/************************************************************************** X * lpd - the lineprinter daemon X * This program copies files from the line printer daemon directory to X * /dev/lp and then unlinks them. It uses a file in the printer daemon X * directory named 'lock' to show if a daemon is already running. While X * this isn't a real semaphore it is possible that no daemon does the work X * until anotherone is started. If the printer is still quiet see if X * /usr/spool/lpd/lock exists and no daemon is running ( should only occur X * by killing a running daemon ). X * If the daemon must expand tabs define TABS to 0 else to 1. X * Parts of this program are inspirated form the original lpr written X * by Andy Tanenbaum. X * X * Ralf Wenk last update: Mon Jul 10 20:46:04 1989 X **************************************************************************/ X X X#define TABS 1 X X#include <sys/types.h> X#include <sys/dir.h> X#include <errno.h> X X#define NULL ( char *)0 X#define EOS '\0' X#define BLOCK 1024 X#define TRIES 600 /* wait 10 minutes, then cry */ X#define E_SIZE sizeof( struct direct ) X Xextern int errno; X Xchar lock[] = LOCK; Xchar out_buf[BLOCK]; Xchar *i_am; Xint out_count, column; X X Xvoid panic( s1, s2 ) Xchar *s1, *s2; X{ X unlink( lock ); /* remove the lock-file */ X std_err( i_am ); X std_err( s1 ); X if ( s2 ) X std_err( s2 ); X std_err(\N); X exit( 1 ); X} /* panic */ X X X/************************************************************************** X * write the output buffer thru /dev/lp X * handle some errors X **************************************************************************/ X Xvoid flush () X{ X register int n, try; X X try = 0; X if ( out_count ) X { X while (( n = write( 1, out_buf, out_count )) != out_count && try < TRIES ) X { X try++; X sleep(1); X } X if ( try == TRIES ) X { X if ( errno == EIO ) X else X { X perror( i_am ); X } X } X out_count = 0; X } X} /* flush */ X X X/************************************************************************** X * insert a character into the output buffer, flush it if necessary X * perform column counting for expanding tabs X **************************************************************************/ X Xvoid putc ( c ) Xchar c; X{ X out_buf[out_count++] = c; X#if ! TABS X column++; X if ( c == '\n' ) X column = 0; X#endif /* ! TABS */ X if ( out_count == BLOCK ) X flush(); X} /* putc */ X X X/************************************************************************** X * Print a file, adding carriage returns and ifndef'd expanding tabs. X **************************************************************************/ X Xvoid print ( fd ) Xint fd; X{ X register int in_count, pos; X register char c; X char in_buf[BLOCK]; X X out_count = 0; X while ( in_count = read( fd, in_buf, BLOCK )) X for ( pos = 0; pos < in_count; pos++ ) X { X c = in_buf[pos]; X if ( c == '\n' ) X#if TABS X putc('\r'); X putc( c ); X#else X { X putc('\r'); X putc('\n'); X } X else if ( c == '\t' ) X do X putc(' '); X while ( column & 7 ); X else X putc( c ); X#endif /* TABS */ X } X flush(); X} /* print */ X X X/************************************************************************** X * read a directory X * return TRUE if a non reseved file was found X **************************************************************************/ X Xint nextfile ( fd, entry ) Xregister int fd; Xregister struct direct *entry; X{ X register int n; X X lseek( fd, 0, 0 ); X do X n = read( fd, entry, E_SIZE ); X while ( n == E_SIZE && X return( n == E_SIZE ); X} /* nextfile */ X X X/************************************************************************** X * test and terminate if a daemon already exists X * else create the lock and print ervery interesting file found in X * /usr/spool/lpd X **************************************************************************/ X Xmain( argc, argv ) Xint argc; Xchar *argv[]; X{ X register int fd, dfd, pfd; X union X { X char trick[ sizeof( struct direct ) + 1 ]; X struct direct dir; X } entry; X X i_am = argv[0]; /* my name */ X entry.trick[ sizeof( struct direct ) ] = EOS; /* dirty trick to force EOS */ X chdir( spool ); X if ( access( lock, 0 )) X { X if (( fd = creat( lock, 0 )) < 0 ) X close( fd ); X if (( dfd = open( spool, 0 )) < 0 ) X panic( c_open, spool ); X else X { X close( 1 ); X if ( open( device, 1 ) < 0 ) X panic( c_open, device ); X while ( nextfile( dfd, &entry )) X { X if (( pfd = open( entry.dir.d_name, 0 )) < 0 ) X panic( c_open, entry.dir.d_name ); X else X { X print( pfd ); X close( pfd ); X unlink( entry.dir.d_name ); X } X } X unlink( lock ); X close( dfd ); X } X } X exit( 0 ); X} /* main */ X X / echo x - lpr.1 sed 's/^X//' > lpr.1 << '/' XNAME X lpr(1) - copy a file to the line printer X XSYNOPSIS X lpr [ file ] ... X XDESCRIPTION X Each argument is interpreted as a file to be printed. lpr links or X copies each file into /usr/spool/lpd where the line printer daemon X lpd can find and copy them to /dev/lp. It inserts carriage returns X and expands tabs ( compile time option ). The lpd is searched by X lpr in /usr/lib, /etc and /usr/bin. lpd uses a file in the printer X daemon directory named 'lock' to show if a daemon is already X running. While this isn't a real semaphore it is possible that no X daemon does the work until anotherone is started. If the printer is X still quiet see if /usr/spool/lpd/lock exists and no daemon is X running ( should only occur by killing a running daemon ). X XFILES X /usr/lib/lpd X /usr/spool/lpd/* X /usr/spool/lpd/lock X XBUGS X No sequential file numbering is done, so each file name can only X occure once until printet. X / echo x - lpr.c sed 's/^X//' > lpr.c << '/' X/************************************************************************** X * lpr - line printer front end X * This program links or copies files to the line printer daemon directory X * and then calls the daemon to spool them. X * Parts of this program are inspirated form the original lpr written X * by Andy Tanenbaum. X * X * Ralf Wenk last update: Thu Sep 28 22:24:34 1989 X **************************************************************************/ X X#include <signal.h> X#include <sys/stat.h> X X#define EOS '\0' X#define NULL ( char *)0 X#define NAME SPOOL X#define MODE 0400 X#define MASK 0333 X#define BLOCK 1024 X X NULL }; Xchar spool[BLOCK]; X Xextern char *rindex(); X X Xvoid panic ( st1, st2 ) Xchar *st1, *st2; X{ X std_err( st1 ); X if ( st2 ) X std_err( st2 ); X std_err(\N); X} /* panic */ X X X/************************************************************************** X * write std_in into a file with random name X * before spooling stdin collect it into a temporary file, to prevent X * a already running lpd of printing and removing it too eraly. X **************************************************************************/ X Xvoid get_stdin () X{ X register int fd, pid, n; X char tmp_file[32]; X X pid = getpid(); X strcpy( tmp_file, tmp_dir ); X strcat( tmp_file, NAME ); X n = strlen( tmp_file ); X while ( pid ) X { X tmp_file[n++] = ( pid % 10 ) + '0'; X pid /= 10; X } X tmp_file[n] = EOS; X umask( MASK ); X if (( fd = creat( tmp_file, MODE )) < 0 ) X { X panic( c_spool,STDIN); X exit( 1 ); X } X while ( n = read( 0, spool, BLOCK )) X write( fd, spool, n ); X close( fd ); X strcpy( spool, sp_dir ); X strcat( spool, &tmp_file[strlen( tmp_dir )] ); X link( tmp_file, spool ); /* that's why tmp_dir must on the */ X unlink( tmp_file ); /* same device as sp_dir */ X} /* get_stdin */ X X X/************************************************************************** X * insert given file(s) into /usr/spool/lpd and start the daemon X **************************************************************************/ X Xmain(argc, argv) Xint argc; Xchar *argv[]; X{ X register int fd, pid, i; X register char *cptr; X struct stat st, st1; X int ret; X X signal( SIGHUP, SIG_IGN ); /* ignore some signals */ X signal( SIGINT, SIG_IGN ); X signal( SIGQUIT, SIG_IGN ); X signal( SIGTERM, SIG_IGN ); X if ( argc == 1 ) X { X get_stdin(); /* standard input only */ X } X else X { X for ( i = 1; i < argc; i++ ) /* all filenames given */ X { X strcpy( spool, sp_dir ); X if (( cptr = rindex( argv[i],'/')) != NULL ) X strcat( spool, cptr + 1 ); X else X strcat( spool, argv[i] ); X if ( link( argv[i], spool )) X { X pid = -1; X if ( stat( argv[i], &st ) == 0 && ( st.st_mode & S_IFMT ) != S_IFDIR && X ( stat( spool, &st1 ) != 0 || st.st_dev != st1.st_dev || X st.st_ino != st.st_ino ) && ( pid = fork()) == 0 ) X { X panic( c_spool, argv[i] ); X } X else if ( pid != -1 ) X { X wait( &ret ); X if ( ret ) X panic( c_spool, argv[i] ); X chmod( spool, MODE ); X } X else X panic( c_spool, argv[i] ); X } X } X } X if (( pid = fork()) == 0 ) X { X i = 0; X while ( lpd[i] ) X execl( lpd[i++],LPD, NULL ); X exit( 1 ); X } X else if ( pid == -1 ) X { X exit( 1 ); X } X exit( 0 ); X} /* main */ X / echo x - lpstat sed 's/^X//' > lpstat << '/' X#!/bin/sh X# The 'style' of this script is forced by shell problems. X# e.g. shift and $* together with case ... causes the shell to die. X XPATH=/usr/bin:/bin XLPDIR=/usr/spool/lpd XIFSOLD=$IFS XNL=' X' X#set -xv X Xargc=$# Xargv1=$1 Xif test $argc -le 1 ; then X IFS=$NL X for i in $files; do X IFS=$IFSOLD X set $i X shift X done X case $argv1 in X -t) myself=':';; X ) myself=`whoami`;; X *) echo $USAGE; exit 1;; X esac X echo -n $NAMES | fgrep $myself Xelse X echo $USAGE; exit 1 Xfi X Xexit 0 X / exit 0