pc-pap An intelligent Appletalk (CAP) print filter

David J. Hughes bambi at kirk.nmg.bu.oz.au
Thu Feb 14 15:26:50 AEST 1991


An extract from the README file :-


	This program is a replacement PAP based lpd input filter for
	the standard CAP papif.  At the simplest level, it performs 
	the same function as papif (send a printjob to an appletalk
	based laserwriter).  It also provides for definable language
	recognition and filtering based on the contents of the first
	line of the print job (such as a PostScript magic or #! /bin/sh
	in shell scripts).  It also splits PostScript printjobs into
	individual PAP connections.  This is essential if IBM PC
	based applications, such as MS-Word, print via this queue.

 	As most PostScript PC print jobs consist of a procedure set 
 	followed by a document, the print job sent to the laser
 	contains a ^D in the middle.  An Apple Laser will see this 
 	and flush the job untill end of file.  Unfortunately, if the
 	laser is connected to appletalk, it assumes that end of file is
 	when the PAP connection is dropped.  This is why each 
 	PostScript print job is split into it's own PAP connection.


What follows is a shar file containing the source to pc-pap, a README
file and a sample configuration.  It has been tested under Ultrix 3.1
and 4.1 and SunOS 4.1.  If you port it to any other system/OS
combination please let me know.

Please send comments and bug reports to my address listed below.



+----------------------------------------------------------------------------+
| David J. Hughes   (AKA bambi)	 |   bambi at kirk.bu.oz.au                     |
| Systems Programmer		 |   bambi at kirk.bu.oz.au@uunet.uu.net        |
| Comms Development & Operations |   ..!uunet!munnari!kirk.bu.oz.au!bambi    |
| Bond University, Gold Coast    |   Phone : +61 75 951111                   |
| Queensland,  Australia  4229   |   Fax :   +61 75 951456                   |
+----------------------------------------------------------------------------+


--------------------------------- CUT HERE --------------------------------
#!/bin/sh
# to extract, remove the header and type "sh filename"
# wrapped by bambi at kirk.bu.oz.au
if `test ! -s ./Makefile`
then
echo "writing ./Makefile"
cat > ./Makefile << '\End\Of\Shar\'
#CFLAGS= -DDEBUG -g -c
CFLAGS= -O -c
OBJS= pc-pap.o config.o

.c.o:
	cc $(CFLAGS) $*.c

all: pc-pap 

clean:
	rm *.o pc-pap

pc-pap.o : pc-pap.c Makefile
config.o : config.c Makefile

pc-pap : pc-pap.o config.o 
	cc -o pc-pap $(OBJS) -lcap

install:
	cp pc-pap /usr/local/lib/lpdfilters
	chmod 760 /usr/local/lib/lpdfilters/pc-pap
\End\Of\Shar\
else
  echo "will not over write ./Makefile"
fi
if `test ! -s ./README`
then
echo "writing ./README"
cat > ./README << '\End\Of\Shar\'
#      @(#)README  (Bond)  bambi



		Title	: PC-PAP.c
		Author	: David J. Hughes     bambi at kirk.bu.oz.au
		Date	: 27-Jan-1991
		Dept	: Communications Development and Operations
			  Bond University

	
	This program is a replacement PAP based lpd input filter for
	the standard CAP papif.  At the simplest level, it performs 
	the same function as papif (send a printjob to an appletalk
	based laserwriter).  It also provides for definable language
	recognition and filtering based on the contents of the first
	line of the print job (such as a PostScript magic or #! /bin/sh
	in shell scripts).  It also splits PostScript printjobs into
	individual PAP connections.  This is essential if IBM PC
	based applications, such as MS-Word, print via this queue.

 	As most PostScript PC print jobs consist of a procedure set 
 	followed by a document, the print job sent to the laser
 	contains a ^D in the middle.  An Apple Laser will see this 
 	and flush the job untill end of file.  Unfortunately, if the
 	laser is connected to appletalk, it assumes that end of file is
 	when the PAP connection is dropped.  This is why each 
 	PostScript print job is split into it's own PAP connection.

	To configure the language recognition features edit the
	pc-pap.conf file to represent your local site.  Details of the
	file format are outlined in the pc-pap.conf.example file.  When
	the configuration file is completed, it shoulod be installed as
	/usr/local/lib/pc-pap.conf.  To check the configuration file,
	run pc-pap from the commandline.  The first task it performs is
	to parse and load the configuration from file.  Any errors found
	will be reported to the terminal (including location and type
	of error).

	Installation of the pc-pap filter is the same as the
	installation of papif.  Basically, the software uses the program
	name (argv[0]) as the name of the printer to which the data
	should be sent.  To install the filter for a printer called
	admin-laser, create a sym-link to pc-pap called admin-laser.
	Set the if= field in the /etc/printcap file to point to the
	sym-link as the printers input filter.

	The only other file that is used by pc-pap is the
	/etc/cap.printers file.  It expects it to be in the same format
	as is specified in the CAP 5.0 distribution.

	Testing of the filter can be performed by redirecting an input
	file into the sym-link (eg. admin-laser < test.ps).  If an error
	is encountered, it will be logged via syslog.
\End\Of\Shar\
else
  echo "will not over write ./README"
fi
if `test ! -s ./config.c`
then
echo "writing ./config.c"
cat > ./config.c << '\End\Of\Shar\'
/*
 *              Title   : config.c
 *              Author  : David J. Hughes     bambi at kirk.bu.oz.au
 *              Date    : 27-Jan-1991
 *              Dept    : Communications Development and Operations
 *                        Bond University
 *
 *	Routines to parse and load the configuration file
 */


#include <stdio.h>
#include <syslog.h>
#include <strings.h>
#include <malloc.h>
#include "pc-pap.h"

char    	*ps_token[10];                    /* List of tokens */
token_t		*magics[50];
filter_t	*filters[10];
int		magic_offset = 0,
        	ps_offset = 0,
   		filter_offset = 0,
		ps_found = 0,
   		text_found = 0;


/*
** SUBST_ASCII :  Check the string for any embedded ASCII values and
**		  convert them to there actual character codes.
**
*/


char * subst_ascii(str)
	char	*str;
{
	static	char ret_str[80];
	 char	*cp1,
		*cp2,
		ascii[4];

	strcpy(ret_str,"");
	cp1 = str;
	cp2 = ret_str;
	while(*cp1 != NULL)
	{
		if(*cp1 != '\\')
			*cp2 = *cp1;
		else
		{
			ascii[0] = *++cp1;
			ascii[1] = *++cp1;
			ascii[2] = *++cp1;
			ascii[3] = 0;
			*cp2 = (char) atoi(ascii);
		}
		cp1++;
		cp2++;
	}
	*cp2 = 0;
	return(ret_str);
}


/*
** BAD_CONFIG :  Report configuration error.
**
*/

bad_config(line,error)
	int	line;
	char	*error;
{
	fprintf(stderr,"\n\nPC-PAP : Error in configuration file at line %d.",
		line);
	if (error != NULL)
		fprintf(stderr,"\n         %s.\n",error);
	fprintf(stderr,"\nExiting \007\n");
	syslog(LOG_DEBUG,"PC-PAP : Error in config at line %d",line);
	exit(1);
}


/*
** READ_PS  :  load the PostScript magic tokens
**
*/

read_ps(line,line_num)
	char 	*line;
	int	line_num;
{
	char	*token,
		*store;

	if (index(line,':') == NULL)
		bad_config(line_num,"No terminator on PostScript magic token");
	token = strtok(line,":");
	if (token == NULL)
		bad_config(line_num,"No token defined");
	store = (char *) malloc(strlen(token)+1);
	strcpy(store,subst_ascii(token));
	ps_token[ps_offset++] = store;
	ps_found = 1;
}


/*
** STORE_FILTER  :  Write a set filter definition into the filter vector.
**
*/

store_filter(lang,line,line_num)
	char	*lang,
		*line;
	int	line_num;
{
	char		*token,
			error_msg[80];
	filter_t	*tmp_filter;
	int		token_count = 0,
			arg_num = 0;

	tmp_filter = (filter_t *) malloc(sizeof(filter_t));
	if(strlen(lang) >= lang_len)
	{
		free(tmp_filter);
		sprintf(error_msg,"Language id too long.  Max is %d characters",
			lang_len);
		bad_config(line_num,error_msg);
	}
	strcpy(tmp_filter->lang,subst_ascii(lang));

	if((token = strtok(line,":")) == NULL)
	{
		free(tmp_filter);
		bad_config(line_num,"Bad filter definition (2nd Token)");
	}
	if(strlen(token) >= path_len)
	{
		free(tmp_filter);
		sprintf(error_msg,"Filter path too long.  Max is %d characters",
			path_len);
		bad_config(line_num,error_msg);
	}
	strcpy(tmp_filter->path,subst_ascii(token));

	if((token = strtok(NULL,":")) == NULL)
	{
		free(tmp_filter);
		bad_config(line_num,"Bad text filter definition (3nd Token)");
	}
	if(strlen(token) >= name_len)
	{
		free(tmp_filter);
		sprintf(error_msg,"Filter name too long.  Max is %d characters",
			name_len);
		bad_config(line_num,error_msg);
	}
	strcpy(tmp_filter->name,subst_ascii(token));

	
	while((token = strtok(NULL,":")) != NULL)
	{
		if(strlen(token) >= args_len)
		{
			free(tmp_filter);
			sprintf(error_msg,
			   "Filter arguements too long.  Max is %d characters",
				args_len);
			bad_config(line_num,error_msg);
		}
		strcpy(tmp_filter->args[arg_num++],subst_ascii(token));
	}
	while(arg_num < num_filter_args)
		tmp_filter->args[arg_num++][0] = NULL;
	filters[filter_offset++] = tmp_filter;
}


/*
** READ_TEXT  :  load the Text to PostScript filter details
**
*/


read_text(line, line_num)
	char 	*line;
	int	line_num;
{
	char		*token;

	store_filter(TEXT_PS,line,line_num);
	text_found = 1;
}


/*
** READ_FILTERS  :  load the language filter definitions
**
*/

read_filters(line, line_num)
	char 	*line;
	int	line_num;
{
	char	*lang,
		*rest;
	
	lang = strtok(line,":");
	rest = strtok(NULL,"\n");
	store_filter(lang,rest,line_num);
}


/*
** READ_MAGICS  :  load the magic tokens definitions
**
*/

read_magics(line,line_num)
	char 	*line;
{
	char		*token,
			error_msg[80];
	token_t		*tmp_token;
	int		token_count = 0;

	tmp_token = (token_t *) malloc(sizeof(token_t));
	if((token = strtok(line,":")) == NULL)
		bad_config(line_num,"Missing tokens");
	if(strlen(token) >= lang_len)
	{
		free(tmp_token);
		sprintf(error_msg,"Language id too long.  Max is %d characters",
			lang_len);
		bad_config(line_num,error_msg);
	}
	strcpy(tmp_token->lang,subst_ascii(token));

	if((token = strtok(NULL,":")) != NULL)
	{
		if(strlen(token) >= magic_len)
		{
			free(tmp_token);
			sprintf(error_msg,
			   "Magic token too long.  Max is %d characters",
				magic_len);
			bad_config(line_num,error_msg);
		}
		strcpy(tmp_token->token, subst_ascii(token));
	}
	else
		bad_config(line_num,"No magic string supplied.");
	magics[magic_offset++] = tmp_token;
}


/*
** LOAD_CONFIG  :  load the configuration file into internal structures.
**
**      Note :  The internal stat machine has the following states :-
**
**
**                      0.  Starting config read
**                      1.  Reading PostScript section
**                      2.  Reading Text section
**                      3.  Reading Filters section
**			4.  Reading Magics section
*/


load_config()
{
        FILE    *conf;
        char    conf_line[100],
                conf_path[100],
		*token;
        int     state = 0,
                line_num=0;

        sprintf(conf_path,"%s/%s",conf_dir,conf_file);
        conf = fopen(conf_path,"r");
        if (conf == NULL)
        {
                fprintf(stderr,"\007\nPC-PAP :  Could not open configuration");
                fprintf(stderr," file.  Aborting.\n\n");
		syslog(LOG_DEBUG,"PC-PAP : Could not open configuration file");
                exit(1);
        }
        while (!feof(conf))
        {
                fgets(conf_line,99,conf);
		conf_line[strlen(conf_line)-1] = 0;
                line_num++;
                if (!feof(conf))
                {
                        switch(conf_line[0])
                        {
                                case '#':               /* Comment */
                                case '\0':              /* Blank Line */
                                        break;
				case ' ':
				case '\t':
					bad_config(line_num,
						"Leading white space");
					break;

                                case '+':                /* State change */
                                        token = strtok(conf_line+1," \n");
                                        if (strcmp(token,"postscript") == 0)
                                               state = 1;
					else
                                        if (strcmp(token,"text") == 0)
                                                state = 2;
					else
                                        if (strcmp(token,"filters") == 0)
                                               state = 3;
					else
                                        if (strcmp(token,"magics") == 0)
                                               state = 4;
					else
						bad_config(line_num,
						   "Unknown section header");
					break;
                                default:
					switch(state)
					{
						case 0: 
							bad_config(line_num,
							 "Section not defined");
							break;
						case 1: 
							read_ps(conf_line, 
								line_num);
							break;
						case 2: 
							read_text(conf_line,
								line_num);
							break;
						case 3: 
							read_filters(conf_line,
								line_num);
							break;
						case 4: 
							read_magics(conf_line,
								line_num);
							break;
					}
			}
                }
        }
}
\End\Of\Shar\
else
  echo "will not over write ./config.c"
fi
if `test ! -s ./pc-pap.c`
then
echo "writing ./pc-pap.c"
cat > ./pc-pap.c << '\End\Of\Shar\'
/*
 *		Title	: PC-PAP.c
 *		Author	: David J. Hughes     bambi at kirk.bu.oz.au
 *		Date	: 14-June-1990
 *		Dept	: Communications Development and Operations
 *			  Bond University
 *
 *--------------------------------------------------------------------------
 * Edit history
 *
 * 14-June-1990			bambi
 *
 *	Initial development	
 *--------------------------------------------------------------------------
 * 27-Jan-1991			bambi
 *
 *	Remove the hard-coded configuration for language recognition and
 *	use a config file to be read at run-time.  
 *
 *	Fix time-out error when Laser returns a PAP eof flag to us on a 
 *	read.  
 *
 *	Include Job details in the lpq status file so that PC users can see the
 *	name and type of job being printed (only supported in certain 
 *	applications such as Excel, PageMaker, Word for Windows etc.)
 *
 *	Fix up the handling of printer error returns such as "Out of
 *	paper"
 *--------------------------------------------------------------------------
 *
 */


#include <stdio.h>
#include <syslog.h>
#include <varargs.h>
#include <strings.h>
#include <signal.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/file.h>
#include <netat/appletalk.h>
#include <netat/compat.h>
#include "pc-pap.h"

#define  BUF_SIZ	QUANTUM * PAPSegSize


/*
** Global variable definition section
*/

char	printer[20],
	username[20],
	hostname[20],
	acct_file[80],
	*atalk_name,
     	r_buf[100];			/* PAPRead buffer */

int     sock,                           /* Must be global for signal handler */
    	r_comp = 	0,		/* PAPRead completion flag */
    	r_len = 	0,		/* PAPRead data length */
        r_err = 	0,
        r_eof = 	0,
   	CURR_PROC = 	0;



/*
** External variable definition section
*/


extern	char		*ps_token[];
extern	token_t		*magics[];
extern	filter_t	*filters[];
extern	int		magic_offset,
			ps_offset,
			filter_offset;
			

/**************************************************************************
 * Close down the PAP Connection and exit REPRINT
 */

fatal(sig)
	int	sig;
{
	char	sig_name[15],
		proc_name[15];


	switch (sig)
	{
		case 1: strcpy(sig_name,"SIGHUP");
			break;
		case 2: strcpy(sig_name,"SIGINT");
			break;
		case 3: strcpy(sig_name,"SIGQUIT");
			break;
		case 4: strcpy(sig_name,"SIGILL");
			break;
		case 5: strcpy(sig_name,"SIGTRAP");
			break;
		case 6: strcpy(sig_name,"SIGIOT");
			break;
		case 7: strcpy(sig_name,"SIGEMT");
			break;
		case 8: strcpy(sig_name,"SIGFPE");
			break;
		case 9: strcpy(sig_name,"SIGKILL");
			break;
		case 10: strcpy(sig_name,"SIGBUS");
			break;
		case 11: strcpy(sig_name,"SIGSEGV");
			break;
		case 12: strcpy(sig_name,"SIGSYS");
			break;
		case 13: strcpy(sig_name,"SIGPIPE");
			break;
		case 14: strcpy(sig_name,"SIGALRM");
			break;
		case 15: strcpy(sig_name,"SIGTERM");
			break;
		case 16: strcpy(sig_name,"SIGURG");
			break;
		case 17: strcpy(sig_name,"SIGSTOP");
			break;
		case 18: strcpy(sig_name,"SIGSTP");
			break;
		case 19: strcpy(sig_name,"SIGCONT");
			break;
		case 20: strcpy(sig_name,"SIGCHLD");
			break;
	}		
	switch(CURR_PROC)
	{
		case PS_CHILD : strcpy(proc_name,"send_ps()");
			break;
		case FILTER_CHILD : strcpy(proc_name,"do_filter()");
			break;
		case READ_CHILD : strcpy(proc_name,"read_file()");
			break;
		default: strcpy(proc_name,"unknown proc");
	}
	abSleep(4,TRUE);
	PAPClose(sock);
	syslog(LOG_INFO,"PC-PAP : terminated by %s signal in %s",
		sig_name, proc_name);
	exit(LPD_ERROR);
}





/**************************************************************************
 * Setup our signal handlers
 */

set_sigs()
{
	int	sig;

        for ( sig = SIGINT; sig < NSIG; sig++ )
                signal(sig, fatal);
        signal(SIGHUP, SIG_IGN);
	signal(SIGCHLD, SIG_IGN);
}





/**************************************************************************
 * Check out the arguements passed to us by LPD
 */

check_args(argc,argv)
	int	argc;
	char	*argv[];
{
	int	loop;
	char	*argp;

	if((argv[0][0] == '/')||(argv[0][0] == '.'))/* Just want the basename */
	{
		argp = rindex(argv[0],'/');
		strcpy(printer,++argp);
	}
	else
		strcpy(printer,argv[0]);

	for (loop = 1; loop < argc; loop++)
	{
		argp = argv[loop];
		if (argp[0] == '-')
		{
			switch (argp[1])
			{
				case 'n':
					strcpy(username,argv[++loop]);
					break;
			
				case 'h':
					strcpy(hostname,argv[++loop]);
					break;
			}
		}
		else
			strcpy(acct_file,argv[loop]);
	}
}






/**************************************************************************
 * Find the Appletalk laserwriter name from the cap.printers file
 */


char *find_lasername(queue)
	char	*queue;
{
	static	char	buffer[100];
	FILE	*cap_file;
	int	num_bytes;

	cap_file = fopen(CAP_PRINTERS,"r");
	if (cap_file == NULL)
	{
		syslog(LOG_ERR,"PC-PAP : Cannot open Mapping File %s",
			CAP_PRINTERS);
		return(NULL);
	}
	while (!feof(cap_file))
	{
		fgets(buffer,100,cap_file);
		if (!feof(cap_file))
		{
			if (buffer[0] != '#')
				if (strcmp(queue,strtok(buffer,"=")) == 0)
				{
					fclose(cap_file);
					return (strtok(NULL,"\n"));
				}
		}
	}
	fclose(cap_file);
	return(NULL);
}





/**************************************************************************
 * Make a PAP connection to the LaserWriter,
 * 	Retries the connection 5 times then fails.
 */

int connect_to_laser(laser)
	char	*laser;
{
	PAPStatusRec	status;
	AddrBlock 	addr;
	int		retry_minor = 0,	
			retry_major = 0,
			sock,
			comp_stat,
			error;
	char		stat_string[100];

#ifdef DEBUG
	fprintf(stderr,"\nOpening connection to %s\n",laser);
#endif DEBUG
	sprintf(stat_string,"%s : Opening connection to %s",printer,laser);
	write_status(stat_string);
	abSleep(2,TRUE);
	while((error = PAPOpen(&sock,laser,QUANTUM,&status,&comp_stat))!= noErr)
	{
		if (error != -1)   /* Connection Error */
		{
			if (++retry_minor == 10)
			{
				sprintf(stat_string,"%s : Cannot connect to %s",
					printer,laser);
				write_status(stat_string);
				retry_minor = 0;
				if(++retry_major == 5)
					return(0);
			}
		}
	}
	while(comp_stat > 0)    /* incomplete connection */
	{
		addr.net = 0;         	/* sufficient */
		abSleep(4,TRUE);	/* protocol sleep for 1 second */
		if (PAPStatus(laser,&status,&addr) >= 0)
			store_status(status.StatusStr);
	}
	abSleep(4,TRUE);
	return(sock);
}







/**************************************************************************
 * Write PAP status message for the spooler to use
 */

store_status(status)
	char	*status;
{
	int	status_file;
	char	stat_line[200],
		stat[40],
		job[40],
		*cp,
		*fp;

	strcpy(stat,"Unknown Status");
	strcpy(job,"Unknown Job");
	strncpy(stat_line,status + 1, *status);
	cp = stat_line;
	while(*cp != NULL)
	{
		if (strncmp(cp,"status:",7) == 0)
		{
			fp = stat;
			cp += 7;
			while((*cp != NULL) && (*cp != ';'))
			{
				*fp++ = *cp++;
			}
			*fp = NULL;
		}
		else
		if (strncmp(cp,"job:",4) == 0)
		{
			fp = job;
			cp += 5;
			while((*cp != NULL) && (*cp != ';'))
			{
				*fp++ = *cp++;
			}
			*fp = NULL;
		}
		cp++;
	}
	sprintf(stat_line,"%s : Status = %s.  Job = %s.",printer,stat,job);
	unlink("new_status");
	status_file = open("new_status",O_CREAT|O_WRONLY,0644);
	if (status_file == -1)
		return;
	write(status_file,stat_line,strlen(stat_line));
	write(status_file,"\n",1);
	close(status_file);
	rename("new_status","status");
}


/**************************************************************************
 * Write TEXT status message for the spooler to use
 */

write_status(status)
	char	*status;
{
	int	status_file;

	unlink("status");
	status_file = open("status",O_CREAT|O_WRONLY,0644);
	if (status_file == -1)
		return;
	write(status_file,status,strlen(status));
	write(status_file,"\n",1);
	close(status_file);
}





/**************************************************************************
 * Fork and exec the appropriate filter for this data stream.  Also
 * fork a copy of ourself to handle reading the stdin and writing it
 * to stdout.  Filter_pipe goes from the original copy of us to the
 * filter and printer_pipe goes from the stdout of the filter back to
 * new copy of us (send_ps).
 */

exec_filter(filter)
	filter_t	*filter;
{
	char		*args[num_filter_args+1];
	int		index=1;

	args[0] = filter->name;
	while(filter->args[index-1][0] != '\000')
	{
		args[index] = filter->args[index-1];
		index++;
	}
	args[index] = NULL;
	index = 0;
#ifdef 	DEBUG
	fprintf(stderr,"Path = %s\n",filter->path);
#endif	DEBUG
	execv(filter->path,args);
}


do_filter(lang,line,sock)
	char 	*lang,
	    	*line;
	int	sock;
{
	int	filter_pipe[2],
		printer_pipe[2],
		index = 0;
	char	*filter_name;
	char	arg_line[100];

	
	while((strcmp(filters[index]->lang,lang)!=0) && (index<filter_offset))
		index++;

	if (index == filter_offset)
	{
		syslog(LOG_ERR,"PC-PAP : Bad Language %s",lang);
		exit(LPD_ERROR);
	}
	pipe(filter_pipe);
	pipe(printer_pipe);
	if (fork() == 0)	/* Child Process */
	{
		CURR_PROC = FILTER_CHILD;
		dup2(filter_pipe[0],fileno(stdin));	
		dup2(printer_pipe[1],fileno(stdout));
		close(filter_pipe[0]);
		close(filter_pipe[1]);
		close(printer_pipe[0]);
		close(printer_pipe[1]);
		exec_filter(filters[index]);
		syslog(LOG_ERR,"PC-PAP : Bad exec for language %s",lang);
		exit(LPD_ERROR);
	}
	else
	{
		if (fork() == 0) /*Second Child */
		{
			CURR_PROC = PS_CHILD;
			dup2(printer_pipe[0],fileno(stdin));
			close(filter_pipe[0]);
			close(filter_pipe[1]);
			close(printer_pipe[0]);
			close(printer_pipe[1]);
			send_ps(sock,NULL,0);
			sleep(1);
			exit(0);
		}
		else
		{
			CURR_PROC = READ_CHILD;
			dup2(filter_pipe[1],fileno(stdout));
			close(printer_pipe[0]);
			close(printer_pipe[1]);
			close(filter_pipe[0]);
			close(filter_pipe[1]);
			write(fileno(stdout),line,strlen(line));
			read_file();
			wait(0);
			wait(0);
		}
	}
}




/**************************************************************************
 *  Scan and process the incoming file (including PS interpretation)
 */

process_file(sock)
	int	sock;
{
	char	stat_string[80],
		line[100];
	int	index,
		num_bytes;

	

	sprintf(stat_string,"%s : Sending printjob to printer.",printer);
	write_status(stat_string);

	num_bytes = read(fileno(stdin),line,99);
	line[num_bytes] = 0;
	
	/* Is this a PostScript File ? */

	for(index = 0; index < ps_offset; index++)
	{
		if(strncmp(ps_token[index],line,strlen(ps_token[index]))==0)
		{
			send_ps(sock,line,num_bytes);
			return;
		}
	}
	
	/* Can we match any other language on this file ? */

	for(index = 0; index < magic_offset; index++)
	{
		if(strncmp(magics[index]->token,line,
		   strlen(magics[index]->token)) == 0)
		{
			do_filter(magics[index]->lang,line,sock);
			return;
		}
	}
		
	/* O.K. it must be a straight text file */

	do_filter(TEXT_PS,line,sock);
}





/**************************************************************************
 *  Just read stdin and write it to stdout.  This is easier than dup-ing
 *  stdin on the filter as we cannot lose the first X chars we have
 *  already read for the magic recognition.
 */


read_file()
{
	char	buff[BUF_SIZ];
	int	num_bytes;
	int	w_bytes;

	do
	{
		num_bytes = read(fileno(stdin),buff,BUF_SIZ);
		if (num_bytes != 0)
			w_bytes = write(fileno(stdout),buff,num_bytes);
	} while (num_bytes != 0);
	close(fileno(stdout));
	sleep(2);
}



/**************************************************************************
 *  Send the data pointed to by ch to the laser. Initiate a read from
 *  the laser.  Show any change in printer status while we are waiting
 *  for the write to complete.
 */


pap_write(sock,ch,num,eof)
	int	sock;
	char	*ch;
	int	num,
	   	eof;
{
	int	paperr,
		wcomp;
	PAPStatusRec	status;
	AddrBlock	addr;

#ifdef	DEBUG
		fprintf(stderr,"In pap_write() num = %d, eof = %d\n",num,eof);
		write(1,ch,num);
#endif	DEBUG

	if((num > 0) || (eof > 0))
	{
		paperr = PAPWrite(sock,ch,num,eof,&wcomp);
		if (paperr < 0)
		{
			PAPClose(sock);
			exit(LPD_REPRINT);
		}
		while (wcomp > 0)
		{
#ifdef DEBUG
			fprintf(stderr,"\nerror = %d, wcomp = %d, num = %d, eof = %d\n",
				paperr, wcomp, num, eof);
#endif DEBUG
			pap_read(sock);
			abSleep(1,TRUE);
			addr.net = 0;
			if(PAPStatus(atalk_name,&status,&addr) >= 0)
				store_status(status.StatusStr);
		}
	}
#ifdef 	DEBUG
	fprintf(stderr,"Leaving pap_write()\n");
#endif	DEBUG
}



/**************************************************************************
 *  Fudge up a non-blocking read from the laser.  Discard anything we
 *  receive.
 */

pap_read(sock)
	int	sock;
{
	if(r_eof == 1)
		return(0);
	if(r_comp > 0)
		return(0);

	if(r_comp == -1)
		return(-1);

#ifdef	DEBUG
	if(r_len > 0)
		write(1,r_buf,r_len);
#endif	DEBUG
	if(r_eof == 1)
		return(1);
	r_err = PAPRead(sock, r_buf, &r_len, &r_eof, &r_comp);
	if(r_err = 0)
		return(0);
	else
		return(-1);
}

		
/**************************************************************************
 *  Read the output of the filter (stdout of the filter maps to stdin of
 *  this process) and send it to the laserwriter via the PAP connection.
 */

send_ps(sock,line,line_len)
	int	sock;
	char	*line;
	int	line_len;
{

	char	buff[BUF_SIZ],
		*c,
		*c1;
	int	paperr,
		wcomp,
		num_bytes,
		count,
		index;

	if (line != NULL)
	{
		c = line;
		for(index = 0; index <line_len; index++)
			if(*(c+index) == '\004')
				*(c+index) = '\n';
		pap_write(sock,line,line_len,FALSE);
	}
	do
	{
		if (r_eof == 1)
			break;
		num_bytes = read(fileno(stdin),buff,BUF_SIZ - 1);
#ifdef	DEBUG
		fprintf(stderr,"Num_Bytes in read = %d\n", num_bytes);
#endif	DEBUG
		c = buff;
		index = 0;
		count = 0;
		while(count < num_bytes)
		{
			index++;
			count++;
			if (*(c+index) == '\004')
			{
				*(c+index) = '\n';
				pap_write(sock,c,index,TRUE);
				abSleep(4,TRUE);
				c += index;
				index = 0;
				PAPClose(sock);
				sock = connect_to_laser(atalk_name);
				r_comp = 0;
				r_eof = 0;
				r_err = 0;
			}
		}
		pap_write(sock,c,index,FALSE);
		abSleep(4,TRUE);
	}while (num_bytes != 0);
	if (r_eof == 0)
	{
		pap_write(sock,c,0,TRUE);
		pap_read(sock);
	}
	abSleep(2,TRUE);
}







/**************************************************************************
 *  Initialise the appletalk libraries (CAP), form the connection to the
 *  LaserWriter and process the data stream.  Log the job to the printer
 *  log file.
 */



/* ARGS USED */

main(argc,argv)
	int	argc;
	char	*argv[];
{

	char	stat_string[80];

	check_args(argc,argv);
	load_config();
	set_sigs();
	atalk_name = find_lasername(printer);
	if (atalk_name == NULL)
		syslog(LOG_ERR,"PC-PAP : Could not map %s to a printer name",
			printer);
	abInit(FALSE);
	nbpInit();
	PAPInit();
	ATPSetResponseTimeout(sectotick(60)); 		/* 1 Minutes */
	sock = connect_to_laser(atalk_name);
	process_file(sock);
	abSleep(4,TRUE);
	PAPClose(sock);
	sprintf(stat_string,"%s : No Status  (No Active Jobs)",printer);
	write_status(stat_string);
}
\End\Of\Shar\
else
  echo "will not over write ./pc-pap.c"
fi
if `test ! -s ./pc-pap.conf.example`
then
echo "writing ./pc-pap.conf.example"
cat > ./pc-pap.conf.example << '\End\Of\Shar\'
#      @(#)/usr/local/lib/pc-pap.conf  (Bond)   
#
# Language configuration file for PC-PAP   31-Jan-1991    bambi at kirk.bu.oz.au
#
#	Definitions of language recognition tokens, output filters and
#	PostScript magics.
#
# 	There are four sections to the configuration :- postscript, text,
#	filters and magics.  The start of a section is defined by the
#	section name preceded by a + on a line by itself.
#
#	In the postscript section, the only entries are character tokens
#	that define the start of a PostScript file.  The tokens are terminated
#	by a colon.
#
#	The text section contains one definition being the path, name, and
#	commandline options for a text to PostScript filter.  Each field is
#	colon separated.  Up to ten command line options may be defined
#	to represent argv[1] through argv[9] (argv[0] is set to the
#	filter name.
#	
#	The filters section contains a list of filters used in pc-pap to 
#	convert a certain language into PostScript.  The format of an entry is
#	<ID>:<Path to filter>:<Name of filter>:<Arg1>:<Arg2>:<Arg N>:
#
#	The final section, magics, contains the token to filter matchings
#	for the language translations.  The format of an entry is
#	<Filter ID>:<Magic Token>:
#
#	NOTE: The token must appear at the start of the first line of the file
#	      if it is to be recognised.  The \ character is the standard
#             escape and may be followed by any 3 digit decimal ASCII 
#	      code (such as \004 for ^D or \058 for :).
#
#	What appears below is a sample setup used on a host at Bond Uni.
#

+postscript

%!:
\004%!:

+text

/usr/local/lib/translators/t2ps:t2ps::

+filters

C_PS:/usr/local/lib/translators/pps:pps:c:
sh_PS:/usr/local/lib/translators/pps:pps:sh:
csh_PS:/usr/local/lib/translators/pps:pps:csh:
mail_PS:/usr/local/lib/translators/mp:mp::

+magics

C_PS:/*:
C_PS:#include<:
C_PS:#include <:
C_PS:static:
C_PS:#ifdef:

sh_PS:#!/bin/sh:
sh_PS:#! /bin/sh:

csh_PS:#!/bin/csh:
csh_PS:#! /bin/csh:

mail_PS:From :
mail_PS:From\58:
mail_PS:Path\58:

\End\Of\Shar\
else
  echo "will not over write ./pc-pap.conf.example"
fi
if `test ! -s ./pc-pap.h`
then
echo "writing ./pc-pap.h"
cat > ./pc-pap.h << '\End\Of\Shar\'
/*
 *		Title	: PC-PAP.h
 *		Author	: David J. Highes    bambi at kowande.bu.oz.au
 *		Date	: 14-June-1990
 *		Dept	: Network Management Group
 *			  Bond University
 */

#define CAP_PRINTERS    "/etc/cap.printers"     /* Location of CAP file */
#define conf_dir        "/usr/local/lib"	/* Directory of config file */
#define conf_file       "pc-pap.conf"		/* Config file name */
#define lang_len        10			/* Max length of lang ID */
#define path_len        80			/* Max length of filter path */
#define name_len        10			/* Max length of filter name */
#define args_len        20			/* Max length of filter args */
#define magic_len       30			/* Max length of magic tokens */
#define	num_filter_args	10			/* Max number args to filter */
#define STATUS          5                       /* Check Status interval (sec)*/
#define QUANTUM         8                       /* PAP Flow Quantum */
#define PS_CHILD        1			/* Debug */
#define FILTER_CHILD    2			/*       Procedure */
#define READ_CHILD      3                       /*                 Flags */
#define TEXT_PS 	"text_PS"
#define	LPD_OK		0			/* LPD Good Print Exit Code */
#define LPD_REPRINT     1        		/* LPD Reprint Exit Code */
#define	LPD_ERROR	2			/* LPD Abort Job Exit Code */


typedef struct  token_s
{
        char    token[magic_len];
        char    lang[lang_len];
} token_t;


typedef struct  filter_s
{
        char    lang[lang_len];
        char    path[path_len];
        char    name[name_len];
        char    args[10][args_len];
} filter_t;


\End\Of\Shar\
else
  echo "will not over write ./pc-pap.h"
fi
echo "Finished archive 1 of 1"
exit



More information about the Alt.sources mailing list