[comp.os.minix] lpr

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