[comp.sources.amiga] printspool

ain@j.cc.purdue.edu (Patrick White) (06/13/88)

Submitted by:	gagnon@larry.mcrcim.mcgill.edu  (Francois Gagnon)
Summary:	A printer spooler.
Poster Boy:	Patrick White	(ain@j.cc.purdue.edu)
Archive Name:	sources/amiga/volume5/printspool.d.sh.Z binaries/amiga/volume6/printspool.d.sh.Z
Tested.
 
NOTES:
   I redid the binaries shar to extract the docs, but didn't touch the
sources shar.
 
 
-- Pat White   (co-moderator comp.sources/binaries.amiga)
ARPA/UUCP: j.cc.purdue.edu!ain  BITNET: PATWHITE@PURCCVM  PHONE: (317) 743-8421
U.S.  Mail:  320 Brown St. apt. 406,    West Lafayette, IN 47906
[How do you get to heaven?   Go to Pluto and hang a left.]
 
========================================
 
#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	Print_Spool.doc
#	Print.hlp
#	Tabs.dat
# This archive created: Fri Jun  3 13:20:36 1988
# By:	Patrick White (PUCC Land, USA)
echo shar: extracting Print_Spool.doc '(18032 characters)'
cat << \SHAR_EOF > Print_Spool.doc

	Print_Spool.doc    V1.0.0    27-Mar-1988    ) Frangois Gagnon


	Contents

		1. Copyright Notice
		2. Introduction
		3. Technical Support
		4. Spool Program Startup
		5. Spool Program Execution
		6. Print Program Operation
		   6.1 Action Requests
		   6.2 File Requests
		   6.3 Printing Options
		7. Print and Spool Error Messages
		   7.1 Spool Internal Errors
		   7.2 Print Internal Errors
		   7.3 Print External Errors
		8. Restrictions and Improvements
		9. Character Tabulation


	[1m1. Copyright Notice[0m

	Copyright ) 1988  Frangois Gagnon,  All Rights Reserved.

	The author does not make any warranty expressed or implied,
	or assumes any liability or responsiblity for the use of this
	software.

	Permission is hereby granted to copy, reproduce, redistribute
	or otherwise use this software as long as it is for non-profit.
	This notice and the above copyright notice must remain intact
	and appear on all copies.

	Permission is also granted to correct any problems with this
	software, but modifications and improvements are reserved by
	the author.


	[1m2. Introduction[0m

	The purpose of the Print_Spool package is to provide the Amiga
	with a printer spooler for ASCII files. It is made of two programs
	working in a master-slave relationship. The Print program is the
	master and is called by the user whenever he wants to interact
	with the spooler. The Spool program is the slave and executes in
	the background printing files and awaiting the masters requests.

	The Spool program provides two major facilities to the user. The
	first one is the handling of a printing queue which is used to
	schedule multiple files in advance. The second one is the ability
	to specify individual printing characteristics for each file.
	These include the paper format, character type and size to use
	and the ability to add page headers and line numbers to the file.


	[1m3. Technical Support[0m

	I will be happy to receive any comments you may have on the
	behaviors and the performances of both programs. Any improvements
	will be evaluated according to their complexity, their usefulness
	to all users, the available time and my own needs of the programs.

	If other programmers want to include an interface in their public
	domain program with the Spool program, it will become an asset to
	the Amiga community.


			Frangois Gagnon
			668 Paul VI
			Terrbonne, Quebec
			J6W 1W1, Canada

		Usenet: gagnon@zap.uucp  or  gagnon@cae.uucp


	[1m4. Spool Program Startup[0m

	Although the Spool program can be started at any time, it is
	suggested that it be started in your Startup-Sequence. The program
	does not tie any resources except memory while it has no file to
	print. This makes it a suitable memory resident program.

	This program as only one argument which is the name of the file
	to use for logging the printing operation. The default is a file
	called RAM:Spool.log but could be any valid file that can be
	opened in "append" mode including NIL:.

		> run Spool RAM:Spool.log

	The log file does not remain opened while the program is running,
	instead it is opened in "append" mode every time there is an
	information to add. This allows the user to look at file as
	needed and even to delete it if it becomes to large.

	The Spool program looks at the preference information for some
	initial values. These only include the paper formats: (refer to
	the CHANGE request)

		narrow (8 inches)    <-->  wide (13 inches)
		short  (8.5 inches)  <-->  long (11 inches)


	[1m5. Spool Program Execution[0m

	The Spool program follows a finite automaton to perform its
	operation. This means that the program can only be in a finite
	number of state during all of its execution (see the REPORT
	request). You should refer to the source code of the Spool
	program for more details on the relationship between the states
	and the requests.

	Spool: Waiting for a file to print.

	While in this state, the printing queue is empty and the program
	is waiting for an INSERT request which will specify a new file to
	print. In the mean time, the printer device can be used by other
	program.

	Spool: Waiting for a change of paper.

	The Spool program goes into this state when the paper format for
	the next file to print does not match the one currently in the
	printer. Upon startup, the program expect the paper in the printer
	to be of the format specified in prerefence. The Spool program
	must be notify using the CHANGE request when the paper matches
	the format required by the next file.

	Spool: Waiting to access the printer.

	The Spool program goes into this state when the printer is not
	available to print a new file. This usualy means that the printer
	is being used by another program. The Spool program will retry
	every 5 seconds until it succeeds.

	Spool: Printing a requested file.

	While in this state, the program is printing one of the requested
	files. The printing options used throught the file are the ones
	specified when it started to print the file. Those options could
	be changed for the current file using the UPDATE request but will
	only be used for the following copies, if there some left to do
	for the file.

	Spool: Printing but will pause after the file.

	While in this state, the program is also printing one of the
	requested files. The rules of the previous state still apply but
	the Spool program will suspend itself after the current copy of
	a file is printed.

	Spool: Pausing between two files.

	While in this state, the Spool program is suspended and is waiting
	for a RESUME request to start printing a new file if there is one.
	The printer device is available for use by other programs.

	Spool: Pausing in the middle of a file.

	The Spool program goes into this state when a FREEZE request is
	received and it is currently printing a file. The access to the
	printer device is still active but no new data is sent. The
	printer will normally empty its own buffer before truly stopping.
	The Spool program can be resumed using the RESUME or the FINISH
	request.


	[1m6. Print Program Operation[0m

	The Print program can be used at any time and even multiple time
	simultaneously. Its purpose it to analyze the command line for
	arguments, send the corresponding requests to the Spool program,
	wait for the results, and display them to the user.

	There are two types of arguments in a command line. A command
	always starts with the minus sign while all other arguments are
	file specifiers. Lower case and upper case are equivalent for
	all arguments and abbreviation are accepted in command names.
	The '?' and '*' are the only wild character accepted by the
	program but there is a maximum of ten '*' per file specifier.

	There are three kinds of commands supported by the Print program.
	The first group are requests that will be immediately executed by
	the Spool program. The second group define the operation to
	perform when a file specifiers appear on the command line. The
	last group are commands which modifies the options that are used
	by the Spool program to print a file.

	If no argument appears on the command line, the REPORT request
	will be automatically executed. The two following command lines
	are equivalent.

		> Print
		> Print -report


	[1m6.1 Action Requests[0m

	The following commands are used to control the Spool program
	activity.

	-report

	This command requests that the status of Spool program and the
	contents of print queue be displayed on the screen. The options
	of the files are included in the list.

	-return

	This command resquests that the flag telling the Spool program
	if it must terminate be toggled. The program only terminates
	when there are no more files to print and the flag is set. The
	program will continue to accept requests until it does terminate.

	-change

	This command tells the Spool program that the paper format on the
	printer matches the requirements of the next file in the queue.

	-freeze

	This command requests that the Spool program suspends its activity
	even if it is currently printing a file. All the internal
	information are preserved allowing the printing to be resumed from
	the point it was frozen.

	-finish

	This command requests that the Spool program suspends its activity
	after it finishes printing the current file. The operation will
	be immediate if it is not printing a file. This request is useful
	if you need to use the printer for some other activity.

	-reset

	This command requests that the Spool program stops printing
	the current file but does not record it as printed. The Spool
	program will also become suspended awaiting new requests. This
	request is useful if you did not properly set the option before
	asking it to print a file. Yous can update the option and then
	resume the printing.

	-cancel

	This command requests that the Spool program stops printing
	the current file and does record it as printed. The Spool
	program will also become suspended awaiting new requests.

	-resume

	This command requests that the Spool program resumes its
	activity after being suspended by one of the previous requests.


	[1m6.2 File Requests[0m

	If none of these three commands appear on the command line
	before a file specifier is used, the INSERT request will be
	used automatically. The two following commands are equivalent.

		> Print *.c
		> Print -insert *.c


	-insert 

	For every file specifier following this command, the specified
	directory is searched for matching files. The resulting file
	list is sent to the Spool program for insertion at the end of
	the queue. The options used for all files of a specifier are
	the ones current when the specifier is encountered on the line.

		> Print -insert Print.hlp

	-update

	For every file specifier following this command, the specified
	directory is searched for matching files. The resulting file
	list is sent to the Spool program to be used as a search list
	for updating the queue. The list is then reported back with a
	flag indicating if the file was found in the queue. The new
	options used for all files of a specifier are the ones current
	when the specifier is encountered on the line.

		> Print -update -2 *.h -cpi12 -1 *.c

	-remove

	For every file specifier following this command, the file
	specifier is sent as is to the Spool program. It is used as
	a pattern to search the queue for matching files which are
	immediately removed and reported back to the user.

		> Print -remove *.h *Print_Spool/*


	[1m6.3 Printing Options[0m

	The initial values of most of the printing options are extracted
	from the preference information. They include paper formats, text
	density and text style. All other initial values are hardcoded in
	the Print program.

	These options remain effective for the duration of the Print
	program execution or until they are changed by other commands.
	All of the option apply to a complete file but your file may
	contain printer commands. However, it is not recommanded that
	the page length or the line density be changed.

	-cpi10	    -cpi12	-cpi17

	These three commands allow you to specify the character density.
	The number indicates the number of character per inch on a line.

	-lpi6	    -lpi8

	These two commands allow you to specify the line density. The
	number indicates the number of lines per inch on a page.

	-narrow     -wide

	These two commands allow you to specify the width of the
	required paper. Before starting to print a file, those values
	are verified (see the CHANGE request). You can select between
	8 inches and 13 inches.

	-short	    -long

	These two commands allow you to specify the length of the
	required paper. Before starting to print a file, those values
	are verified (see the CHANGE request). You can select between
	8.5 inches and 11 inches.

	-draft	    -letter

	These two commands allow you to specify the character quality
	to use for printing the file.

	-header

	This command controls a toggle which indicates if the Spool
	program must add page headers to the file. The header comtains
	the date the file was created, the full name of the file and
	the current page number. The headers are added by default.

	-number

	This command controls a toggle which indicates if the Spool
	program must add line numbers to the file. This field is
	exactly 8 characters to preserve the tabulation. The numbers
	are added by default.

	-####

	This command is used to indicate the number of copies to print
	of a file. The #### represents the number you type in and
	obviously the minus sign is not part of the number. A value of
	zero is valid and cause the file not to be printed when its
	turn comes.

	-keep	    -delete

	These two commands allow you to specify what to do with the
	file when the last copy has been printed. The file will be
	kept by default.


	[1m7. Print and Spool Error Messages[0m

	This section describes all the possible error messages from both
	programs. The reason and a solution are also given when possible.


	[1m7.1 Spool Internal Errors[0m

	Spool: Unable to access log file.

	This message means that the Spool program could not access the
	specified log file. The file name could be illegal or the media
	could be of wrong type or write protected.

	Spool: Spool is already running.

	This message means that another copy of the Spool program is
	already executing or more precisely that a port already exists
	with the Spool program communication port name.

	Spool: Unable to create a port.

	This message means that the Spool program could not create the
	required communication port. Verify if you are not running out
	of memory.


	[1m7.2 Print Internal Errors[0m

	Print: Unable to find Spool port.

	This message means that the Print program could not access the
	Spool program communication port. Usually, it means that the
	Spool program is not currently running and need to be started.
	It could also mean that the Print program was used to soon after
	starting up the Spool program.

	Print: Mismatched protocol versions.

	This message means the Print and the Spool program do not use
	the same version of the communication protocol. Verify if you
	use the most recent version of both programs.

	Print: Unable to create Print port.

	This message means that the Print program could not create the
	port that will be used to receive the results of the requests.
	Verify if you are not running out of memory.

	Print: Unable to allocate memory.

	This message means that you are running out of memory.


	[1m7.3 Print External Errors[0m

	Print: Unknown or ambiguous command =

	This message means that the specified command is unknown to the
	Print program. Verify the spelling and the list of commands or
	supply more characters.

	Print: File not found =

	This message means that the specified file could not be found.
	Verify the spelling and the specified path.

	Print: Wrong file type =

	This message means that the specified file is a directory and
	is not suitable for printing. If you want to print the contents
	of a directory, execute you favorite directory command and send
	the result to the RAM disk. Then use the print command with the
	DELETE option and the file will be deleted as soon as it is
	printed.

	Print: File name too long =

	This message means that the resulting full file name is too long
	for the internal buffer. You will have to rename the file or
	directory names in the path to reduce the resulting length.


	[1m8. Restrictions and Improvements[0m

	The protocol between the two programs restricts the full name
	of a file to 96 characters but only 42 can appear on a page
	header. This could be changed by adding a second line in the
	header for the file name if many users request it.

	The Spool program does not currently use full asynchronous
	operation with the file system and the printer device. This
	means that the program can only check the communication port
	between printing lines. Delays in response time for the Print
	program could be introduced by the Spool program and the file
	system.

	The Spool program does not execute as a completely independant
	process. This means that the program still keeps the three
	default files (stdin, stdout and stderr) opened as provided
	by the process that starts the program. There is new "detach"
	declaration provided with MANX 3.6a which I will probably
	investigate.

	The full file name is used internally to specify a file to be
	printed. The file can be deleted or replaced without the Spool
	program knowing it (this only applies if the program is not
	currently printing that file). If the file is replaced, the
	size that is displayed for information and the date that appears
	in the page header will be invalid.

	The Print program does not currently have a Workbench interface.
	I will welcome all well designed and well written programs that
	would provide such an interface to the Spool program. The Print
	and the Spool programs can be used as reference for the complete
	definition of the protocol.


	[1m9. Character Tabulation[0m

	Some printers do not set the default tabulation to every eight
	columns. If yours does not, you may have some problems printing
	file that contains such character.

	If you are not sure about your printer, you can verify using the
	"Tabs.dat" file provided with this package. The last two lines
	of the file should printout identical if the tabs are properly
	set. You can simply use one the following commands to test your
	printer alone.

		> copy Tabs.dat par:
		> copy Tabs.dat ser:

	You should also print the file using the Print and Spool programs
	to make sure the tabs are properly initialized by the printer
	driver. (This only works if the file has not been modify by any
	communication programs)

		> run Spool
		> wait 5
		> Print -header -number Tabs.dat

	The Spool program currently sends the "aTBSALL" printer commands
	to set the default tabs. If that does not work for you, you will
	have to modify the Spool program at the place that it initializes
	the printer.
SHAR_EOF
if test 18032 -ne "`wc -c Print_Spool.doc`"
then
echo shar: error transmitting Print_Spool.doc '(should have been 18032 characters)'
fi
echo shar: extracting Print.hlp '(912 characters)'
cat << \SHAR_EOF > Print.hlp

	Print.hlp    V1.0.0    27-Mar-1988    ) Frangois Gagnon

	*	    ?			    = Pattern matching characters

	-cpi10	    -cpi12	-cpi17      = Characters per inch
	-lpi6	    -lpi8		    = Lines per inch
	-narrow     -wide		    = Width of the paper
	-short	    -long		    = Length of the paper

	-draft	    -letter		    = Character quality
	-header 			    = Toggle for page headers
	-number 			    = Toggle for line numbers
	-####				    = Number of copies
	-keep	    -delete		    = File completion facility

	-insert     -update	-remove     = Operation on file specifier
	-report 			    = Report spooler status
	-return 			    = Spooler must terminate
	-change 			    = The paper format was changed

	-freeze 			    = Suspend immediately
	-finish 			    = Suspend after the file
	-reset				    = Suspend to restart the file
	-cancel 			    = Suspend and cancel the file
	-resume 			    = Resume the printing operation
SHAR_EOF
if test 912 -ne "`wc -c Print.hlp`"
then
echo shar: error transmitting Print.hlp '(should have been 912 characters)'
fi
echo shar: extracting Tabs.dat '(255 characters)'
cat << \SHAR_EOF > Tabs.dat
         1         2         3         4         5         6         7         8
12345678901234567890123456789012345678901234567890123456789012345678901234567890
        X       X       X       X       X       X       X       X       X
	X	X	X	X	X	X	X	X	X
SHAR_EOF
if test 255 -ne "`wc -c Tabs.dat`"
then
echo shar: error transmitting Tabs.dat '(should have been 255 characters)'
fi
#	End of shell archive
exit 0

ain@j.cc.purdue.edu (Patrick White) (06/14/88)

Submitted by:	gagnon@larry.mcrcim.mcgill.edu  (Francois Gagnon)
Summary:	A printer spooler.
Poster Boy:	Patrick White	(ain@j.cc.purdue.edu)
Archive Name:	sources/amiga/volume5/printspool.s.sh.Z
Tested.
 
NOTES:
   I redid the binaries shar to extract the docs, but didn't touch the
sources shar.
 
 
-- Pat White   (co-moderator comp.sources/binaries.amiga)
ARPA/UUCP: j.cc.purdue.edu!ain  BITNET: PATWHITE@PURCCVM  PHONE: (317) 743-8421
U.S.  Mail:  320 Brown St. apt. 406,    West Lafayette, IN 47906
[How do you get to heaven?   Go to Pluto and hang a left.]
 
========================================
 
#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	makefile
#	stdefs.h
#	Print_Spool.h
#	Print.c
#	Spool.c
# This archive created: Mon Mar 28 21:46:51 1988
# By:	Francois Gagnon (CAE Electronics Ltd.)
cat << \SHAR_EOF > makefile
#
# Print_Spool    V1.0    27-Mar-88    ) Francois Gagnon
#
# Options are for Manx Aztec-C V3.6a
#
makeall : Print Spool

Print : Print.o
	ln Print -lc

Spool : Spool.o
	ln Spool -lc

Print.o : Print.c Print_Spool.h
	  cc -E150 +x5 Print

Spool.o : Spool.c Print_Spool.h
	  cc Spool
SHAR_EOF
cat << \SHAR_EOF > stdefs.h

/* stdefs.h    V1.0.0    27-Mar-1988    ) Frangois Gagnon */

#define local			static
#define public

#define byte			unsigned char
#define bool			byte

#define	NUL			'\0'
#define	remove			unlink

#define toupper( c)		(islower(c) ? ((c) - ('a'-'A')) : (c))

#define memcpy(  d, s, l)	movmem(  s, d, l)
SHAR_EOF
cat << \SHAR_EOF > Print_Spool.h

/* Print_Spool.h    V1.0.0    27-Mar-1988    ) Frangois Gagnon */


/*
   Copyright ) 1988  Frangois Gagnon,  All Rights Reserved.

   The author does not make any warranty expressed or implied,
   or assumes any liability or responsiblity for the use of
   this software.

   Permission is hereby granted to copy, reproduce, redistribute
   or otherwise use this software as long as it is for non-profit.
   This notice and the above copyright notice must remain intact
   and appear on all copies.

   Permission is also granted to correct any problems with this
   software, but modifications and improvements are reserved by
   the author.
*/

/*****************************************/
/* Definition of the Command information */
/*****************************************/

#define	PS_Insert	11	/* Insert a name in the list	*/
/* This command inserts a list of names into the print queue. If the	*/
/* spooler is in the Wait_State, it will go to Exec_State and will	*/
/* start to print the first file.					*/

#define PS_Update	12	/* Update names from the list	*/
/* This command updates the options and flags values of the files that	*/
/* match the specified pattern. The file being printed will be affected */
/* by this command but the value will only be used if multiple copies	*/
/* of the file are to be printed. A copy of the modified entries are	*/
/* sent back to the requesting program. 				*/

#define PS_Remove	13	/* Remove names from the list	*/
/* This command removes the files that match the specified pattern. The */
/* file being printed will be affected by this command and the affected */
/* entries are sent back to the requesting program.			*/

#define PS_Report	14	/* Report the names of the list */
/* This command requests that the status of the spooler with a copy of	*/
/* the print queue be sent to the requesting program.			*/

#define PS_Return	15	/* Return when all completed	*/
/* This command toggles the flag indicating wheter the program must	*/
/* terminate its execution after the last file is printed. It will	*/
/* normally wait for a new file to print instead of terminating. It is	*/
/* still possible to modify the print queue even if the flag is set.	*/
/* Great care nust be taken not to loose messages.			*/

#define PS_Change	16	/* Change the paper format	*/
/* This command indicates that the format of the paper has been changed */
/* to the format required for the next file.				*/
^L
#define PS_Freeze	21	/* Freeze the printing activity */
/* This command requests that the spooler stops printing the current	*/
/* file immediately and waits for further commands. The printing can be */
/* restarted from the point it was stopped. This command will only be	*/
/* executed if the spooler is waiting for a file name or printing one.	*/
/* It will also overide a PS_Finish command by forgeting it was ever	*/
/* executed.								*/

#define PS_Finish	22	/* Finish the printing activity */
/* This command requests that the spooler stops after printing the	*/
/* current file and waits for further commands. This command will	*/
/* override a PS_Freeze command by resuming the printing.		*/

#define PS_Resets	23	/* Resets the printing activity */
/* This command requests that the spooler stops printing the current	*/
/* file immediately and waits for further commands. The File_Copy	*/
/* counter is not decremented. This command will override the PS_Freeze */
/* command.								*/

#define PS_Cancel	24	/* Cancel the printing activity */
/* This command requests that the spooler stops printing the current	*/
/* file immediately and waits for further commands. The File_Copy	*/
/* counter is decremented as if the file was completed.	It will 	*/
/* override the PS_Freeze command.					*/

#define PS_Resume	25	/* Resume the printing activity	*/
/* This command restarts the spooler activity after being stopped by	*/
/* one of the others control commands. The command will also cancel a	*/
/* PS_Finish which as not yet completed for the current file.		*/

/*****************************************/
/* Definition of the Options information */
/*****************************************/

#define Line_Density	0x03	/* 10, 12 or 17 chars per inch	*/
#define Line_Formats	0x04	/* 8 or 13 inches page wide	*/
#define Page_Density	0x08	/* 6 or 8 lines per inch	*/
#define Page_Formats	0x10	/* 8.5 or 11 inches page long	*/
#define Opts_Headers	0x20	/* Add headers to pages  */
#define Opts_Numbers	0x40	/* Add numbers to lines  */

#define Flag_Quality	0x01	/* Type of char printing */
#define Flag_Deleted	0x02	/* Delete after pinting  */

#define Line_10 	0x00	/* 10 chars per inch */
#define Line_12 	0x01	/* 12 chars per inch */
#define Line_17 	0x02	/* 17 chars per inch */
#define Line_8		0x00	/* 8 inchs wide  */
#define Line_13 	0x04	/* 13 inchs wide */

#define Page_6		0x00	/* 6 lines per inch  */
#define Page_8		0x08	/* 8 lines per inch  */
#define Page_85 	0x00	/* 8.5 inchs long */
#define Page_11 	0x10	/* 11 inchs long  */

#define Flag_Updated	0x80	/* If update was successful */
#define Flag_Partial	0x80	/* If the result is partial */
^L
/*************************************/
/* Defintion of the File information */
/*************************************/

#define Name_Size	96	/* Maximum number of character	*/
#define Date_Size	20	/* Exact number of character	*/

typedef struct	Detail_File
	{ struct Detail_File   *File_Next;
	  char			File_Name[Name_Size];
	  short  		File_Copy;
	  byte			File_Opts;
	  byte			File_Flag;
	  char			File_Date[Date_Size];
	  long			File_Size;
	} File;

/*****************************************/
/* Definition of the Spooler information */
/*****************************************/

#define Wait_State	0	/* Waiting for a file to print     */
#define Page_State	1	/* Waiting to change the paper     */
#define Open_State	2	/* Waiting to access the printer   */
#define Exec_State	3	/* Is currently printing a file	   */
#define File_State	4	/* Will be stopped after the file  */
#define Susp_State	5	/* Suspended between two files	   */
#define Stop_State	6	/* Stopped in the middle of a file */

typedef struct	Status_Info
	{ byte	Vers_State; /* Current version of spooler   */
	  byte	Prog_State; /* Current state of the spooler */
	  byte	Opts_State; /* Current options for printing */
	  bool	Flag_State; /* If must terminate execution  */
	  long	Char_Print; /* Number of chars printed */
	  long	Line_Print; /* Number of lines printed */
	  long	Page_Print; /* Number of pages printed */
	  File *File_Print; /* List of files to print  */
	} Info;

/*************************************/
/* Definition of Control information */
/*************************************/

#define	SPOOLNAME	"SPOOL"
#define	PROTOCOL	1

union	Details { File *Insert, *Update;
		  char	Remove[Name_Size];
		  Info  Status;
		};

struct	Inform	{ struct Message Packet;
		  short 	 Number, Action;
		  union  Details Detail;
		};
SHAR_EOF
cat << \SHAR_EOF > Print.c

/* Print.c    V1.0.0    27-Mar-88    ) Frangois Gagnon */

/*
   Copyright ) 1988  Frangois Gagnon,  All Rights Reserved.

   The author does not make any warranty expressed or implied,
   or assumes any liability or responsiblity for the use of
   this software.

   Permission is hereby granted to copy, reproduce, redistribute
   or otherwise use this software as long as it is for non-profit.
   This notice and the above copyright notice must remain intact
   and appear on all copies.

   Permission is also granted to correct any problems with this
   software, but modifications and improvements are reserved by
   the author.
*/

#include <stdio.h>
#include <ctype.h>
#include <time.h>

#include <exec/memory.h>
#include <exec/ports.h>
#include <exec/nodes.h>
#include <intuition/preferences.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>

#include "stdefs.h"
#include "Print_Spool.h"

/* Declarations of status messages */
local	char	FIND[] = "\nPrint: Unable to find Spool port.\n\n";
local	char	PROT[] = "\nPrint: Mismatched protocol versions.\n\n";
local	char	PORT[] = "\nPrint: Unable to create Print port.\n\n";
local	char	DATA[] = "\nPrint: Unable to allocate memory.\n\n";
local	char	EXEC[] = "\nPrint: Unknown or ambiguous command = %s.\n\n";
local	char	LOCK[] = "\nPrint: File not found = %s.\n\n";
local	char	TYPE[] = "\nPrint: Wrong file type = %s.\n\n";
local	char	SIZE[] = "\nPrint: File name too long = %s%s.\n\n";

/* Definition for Execution */
local	struct	Inform		*Print_Data;
local	struct	MsgPort 	*Spool_Port, *Print_Port;
local	struct	FileInfoBlock	*Print_Info;

local	File	*Print_List = NULL, *Print_Last = NULL;
local	int	 Print_File = PS_Insert;
local	int	 Print_Opts = (Opts_Headers | Opts_Numbers);
local	int	 Print_Flag = 0;
local	int	 Print_Copy = 1;

#define Options( Mask, Opts) (Print_Opts = (Print_Opts & ~Mask) + Opts)
^L
/*****************************/
/* Define Available Commands */
/*****************************/

/* Add option commands */
#define	PS_CPI10 	30
#define PS_CPI12	31
#define PS_CPI17 	32
#define PS_LPI6		33
#define PS_LPI8		34
#define PS_NARROW	35
#define PS_WIDE 	36
#define PS_SHORT	37
#define PS_LONG 	38

#define PS_HEADER	40
#define PS_NUMBER	41
#define PS_KEEP 	42
#define PS_DELETE	43
#define PS_DRAFT	44
#define PS_LETTER	45

/* Define the data structure */
#define Print_Size	26
#define Print_Used	 7
struct	Print_Type	{ char Name[Print_Used];
			  byte Code;
			};

local	struct	Print_Type	Print_Exec[Print_Size] = {
  { "CANCEL", PS_Cancel }, { "CHANGE", PS_Change }, { "CPI10",  PS_CPI10  },
  { "CPI12",  PS_CPI12  }, { "CPI17",  PS_CPI17  }, { "DELETE", PS_DELETE },
  { "DRAFT",  PS_DRAFT  }, { "FINISH", PS_Finish }, { "FREEZE", PS_Freeze },
  { "HEADER", PS_HEADER }, { "INSERT", PS_Insert }, { "KEEP",   PS_KEEP   },
  { "LETTER", PS_LETTER }, { "LONG",   PS_LONG   }, { "LPI6",   PS_LPI6   },
  { "LPI8",   PS_LPI8   }, { "NARROW", PS_NARROW }, { "NUMBER", PS_NUMBER },
  { "REMOVE", PS_Remove }, { "REPORT", PS_Report }, { "RESET",  PS_Resets },
  { "RESUME", PS_Resume }, { "RETURN", PS_Return }, { "SHORT",  PS_SHORT  },
  { "UPDATE", PS_Update }, { "WIDE",   PS_WIDE   }};
^L
/********************************/
/* Search Allowing Abbreviatoin */
/********************************/

local	int	Find_Command( Name)
/*********************************/
register char	Name[];

{ register int	Length, Result;
  auto	   int  Test,   Next;
  extern   int	strncmp();

  for (Length = 0; Name[Length] != NUL; ++Length)
    Name[Length] = toupper( Name[Length]);

  Next = 1; Result = -1;
  do /* The name table must be sorted */
  { Test = Next;
    if (++Result == Print_Size) { Next = -1; break; }
    Next = strncmp( Name, Print_Exec[Result].Name, Length);
  }
  while (Test > 0);

  if ((Test != 0) || (Next == 0)) return 0;
  return Print_Exec[Result - 1].Code;
}
^L
/******************************/
/* Initialization of Defaults */
/******************************/

#define INTUITIONNAME		"intuition.library"
public	struct IntuitionBase	*IntuitionBase;

local	void	Default_Values()
/******************************/

{ auto	 struct Preferences	PrefBuffer;
  auto	 int			Temp;
  extern struct IntuitionBase  *OpenLibrary();
  extern struct Preferences    *GetPrefs();

  /* Fetch the information from the system */
  IntuitionBase = OpenLibrary( INTUITIONNAME, 0L);
  if (IntuitionBase == NULL) return;
  GetPrefs( &PrefBuffer, (long) sizeof( struct Preferences));
  CloseLibrary( IntuitionBase);

  /* Extract the information from the structure */
  if      (PrefBuffer.PrintPitch == ELITE) Print_Opts |= Line_12;
  else if (PrefBuffer.PrintPitch == FINE)  Print_Opts |= Line_17;

  Temp = (PrefBuffer.PrintSpacing == EIGHT_LPI ? 8 : 6);
  if (PrefBuffer.PrintQuality == LETTER)    Print_Flag |= Flag_Quality;
  if (PrefBuffer.PrintSpacing == EIGHT_LPI) Print_Opts |= Page_8;
  if (PrefBuffer.PaperSize    == W_TRACTOR) Print_Opts |= Line_13;
  if (PrefBuffer.PaperLength / Temp == 11)  Print_Opts |= Page_11;
}


/****************************/
/* Printing Utility Routine */
/****************************/

local	char	Value[3][3] = { "10", "12", "17" };

local	void	Show_File( Info_File)
/***********************************/
register File	*Info_File;

{ printf( "%3s%4s%4s%4s%3c%c%c%c%6d %6ld  %s\n",
	Value[Info_File->File_Opts & Line_Density],
	(Info_File->File_Opts & Line_Formats ? "13" :   "8"),
	(Info_File->File_Opts & Page_Density ?  "8" :   "6"),
	(Info_File->File_Opts & Page_Formats ? "11" : "8.5"),
	(Info_File->File_Opts & Opts_Headers ?  'T' :   'F'),
	(Info_File->File_Opts & Opts_Numbers ?  'T' :   'F'),
	(Info_File->File_Flag & Flag_Quality ?  'L' :   'D'),
	(Info_File->File_Flag & Flag_Deleted ?  'D' :   'K'),
	Info_File->File_Copy, Info_File->File_Size, Info_File->File_Name);
}
^L
/*********************************/
/* General Communication Routine */
/*********************************/

local	int	Request_Action( Action)
/*************************************/
int	Action;

{ extern struct MsgPort *FindPort();
  extern struct	Inform	*GetMsg();

  /* Prepare the message for sending */
  Print_Data->Packet.mn_Node.ln_Type = NT_MESSAGE;
  Print_Data->Packet.mn_ReplyPort    = Print_Port;
  Print_Data->Number		     = PROTOCOL;
  Print_Data->Action		     = Action;

  /* Send the request message across  */
  /* Making sure the port still exist */
  Forbid();
  if ((Spool_Port = FindPort( SPOOLNAME)) == NULL)
  { Permit(); fprintf( stderr, FIND); return TRUE; }
  PutMsg( Spool_Port, Print_Data);
  Permit();

  /* Receive the result message */
  do    WaitPort( Print_Port);
  while ((Print_Data = GetMsg( Print_Port)) == NULL);

  /* Check the protocol version */
  if (Print_Data->Number != PROTOCOL)
  { fprintf( stderr, PROT); return TRUE; }
  return FALSE;
}
^L
/*************************************/
/* Request the status of the Spooler */
/*************************************/

local	char	*State[] =
	{ "\nSpool: Waiting for a file to print.\n",
	  "\nSpool: Waiting for a change of paper.\n",
	  "\nSpool: Waiting to access the printer.\n",
	  "\nSpool: Printing a requested file.\n",
	  "\nSpool: Printing but will pause after the file.\n",
	  "\nSpool: Pausing between two files.\n",
	  "\nSpool: Pausing in the middle of a file.\n"
	};

local	void	Request_Report()
/******************************/

{ register File *File_List, *File_Next;
  register byte  Prog_Info;

  /* Request the wanted operation */
  if (Request_Action( PS_Report)) return;

  /* Display the status information */
  printf( State[Prog_Info = Print_Data->Detail.Status.Prog_State]);
  if (Print_Data->Detail.Status.Flag_State)
    printf( "Spool: Set to terminate its execution.\n");
  if (Print_Data->Detail.Status.Opts_State & Flag_Partial)
    printf( "Spool: Partial list of files, unable to allocate memory.\n");
  if ((Prog_Info == Exec_State) || (Prog_Info == File_State) ||
      (Prog_Info == Stop_State) )
  { printf( "Spool: Printing %ld chars ... %ld lines ... %ld pages.\n",
	    Print_Data->Detail.Status.Char_Print,
	    Print_Data->Detail.Status.Line_Print,
	    Print_Data->Detail.Status.Page_Print);
  }

  /* Display the contents of the print queue */
  if ((File_List = Print_Data->Detail.Status.File_Print) != NULL)
  { printf( "\ncpi ipl lpi ipp  hnqd  copy   size  report\n");
    do
    { File_List = (File_Next = File_List)->File_Next;
      Show_File( File_Next);
      FreeMem( File_Next, (long) sizeof( File));
    }
    while (File_List != NULL);
  }
  putchar( '\n');
}
^L
/*************************************/
/* Request the removal of some files */
/*************************************/

local	void	Request_Remove( Name)
/***********************************/
char	Name[];

{ register File *Temp_File, *Temp_Next;
  strncpy( Print_Data->Detail.Remove, Name, Name_Size);
  if (Request_Action( PS_Remove)) return;

  if ((Temp_File = Print_Data->Detail.Insert) != NULL)
  { printf( "\ncpi ipl lpi ipp  hnqd  copy   size  remove\n");
    do
    { Temp_File = (Temp_Next = Temp_File)->File_Next;
      Show_File( Temp_Next);
      FreeMem( Temp_Next, (long) sizeof( File));
    }
    while (Temp_File != NULL);
  }
  putchar( '\n');
}
^L
/**************************************/
/* Perform the specified file request */
/**************************************/

local	void	Perform()
/***********************/

{ if (Print_List != NULL)
  { /* Execute the operation */
    if (Print_File == PS_Insert) Print_Data->Detail.Insert = Print_List;
    if (Print_File == PS_Update) Print_Data->Detail.Update = Print_List;
    Print_Last->File_Next = NULL;
    if (Request_Action( Print_File))
    { do
      { Print_List = (Print_Last = Print_List)->File_Next;
	FreeMem( Print_Last, (long) sizeof( File));
      }
      while (Print_List != NULL);
      return;
    }

    if (Print_File == PS_Insert) Print_List = NULL;
    if (Print_File == PS_Update)
    { printf( "\ndone   cpi ipl lpi ipp  hnqd  copy   size  update\n");
      do
      { Print_List = (Print_Last = Print_List)->File_Next;
	printf( "%s",
	  (Print_Last->File_Flag & Flag_Updated ?  "TRUE   " : "FALSE  "));
	Show_File( Print_Last);
	FreeMem( Print_Last, (long) sizeof( File));
      }
      while (Print_List != NULL);
      putchar( '\n');
} } }
^L
/*********************************/
/* File Search Utililty Routines */
/*********************************/

local	 int	File_Match( Name, Data)
/*************************************/
register char	*Name, *Data;

{ auto	   char *Save_Name[10],
		*Save_Data[10];
  register int	 Save;

  if ((*Data == NUL) || (*Name == NUL)) return FALSE;
  Save = -1;

  while ((*Data != NUL) || (*Name != NUL))
  { if (*Data == '*')
    { if (*++Data == NUL) return TRUE;
      if ( ++Save ==  10) return FALSE;
      Save_Name[Save] = Name;
      Save_Data[Save] = Data;
      continue;
    }
    if (   ((*Data == '?') && (*Name != NUL))
	|| (toupper(*Data) == toupper(*Name)))
    { ++Data; ++Name; continue; }

    if (*Name == NUL) --Save;
    if ( Save <    0) return FALSE;
    Name = ++Save_Name[Save];
    Data =   Save_Data[Save];
  }
  return TRUE;
}
^L
local	int	File_Parent( File_Name, File_Path, File_Size, File_Info)
/**********************************************************************/
char			*File_Name, *File_Path;
int			 File_Size;
struct	FileInfoBlock	*File_Info;

{ extern   struct FileLock *Lock(),	*ParentDir();
  auto	   struct FileLock *File_Lock,	*File_Next;
  register int		    File_Save,	 File_Loop;

  if ((File_Lock = Lock( File_Name, SHARED_LOCK)) == NULL) return 0;
  File_Path[File_Loop = File_Size - 1] = NUL;
  do
  { if (!Examine( File_Lock, File_Info))
    { UnLock( File_Lock); return 0; }

    File_Save = strlen( File_Info->fib_FileName) + 1;
    if ((File_Loop < File_Save) || (File_Loop < 4))
    { UnLock( File_Lock); return -1; }
    if (File_Save == 1)
	 strcpy( &File_Path[File_Loop - (File_Save = 4)], "RAM");
    else strcpy( &File_Path[File_Loop - File_Save], File_Info->fib_FileName);

    File_Next = ParentDir( File_Lock); UnLock( File_Lock);
    File_Path[File_Loop - 1] = (File_Next == NULL ? ':' : '/');
    File_Lock = File_Next; File_Loop -= File_Save;
  }
  while (File_Lock != NULL);

  if (File_Loop != 0) strcpy( File_Path, &File_Path[File_Loop]);
  return (File_Size - File_Loop);
}
^L
/**********************************/
/* Specific File Handling Routine */
/**********************************/

local	int	File_Insert()
/***************************/

{ extern   void   *AllocMem();
  extern   char   *ctime();
  register File   *Temp_File;
  auto	   char   *Temp_Date;
  auto	   time_t  Temp_Time;

  Temp_File = AllocMem( (long) sizeof(File), MEMF_PUBLIC);
  if (Temp_File == NULL) { fprintf( stderr, DATA); return TRUE; }

  Temp_Time = Print_Info->fib_Date.ds_Days   * 86400
	    + Print_Info->fib_Date.ds_Minute * 60
	    + Print_Info->fib_Date.ds_Tick   / TICKS_PER_SECOND;
  Temp_Date = ctime( &Temp_Time);
  memcpy( Temp_File->File_Date, &Temp_Date[4], Date_Size);

  Temp_File->File_Opts = Print_Opts;
  Temp_File->File_Flag = Print_Flag;
  Temp_File->File_Copy = Print_Copy;
  Temp_File->File_Size = Print_Info->fib_Size;

  if (Print_List == NULL) Print_List		= Temp_File;
  else			  Print_Last->File_Next = Temp_File;
  Print_Last = Temp_File;
  return FALSE;
}
^L
local	void	File_Search( Name)
/********************************/
char	*Name;

{ extern char		 *strchr(), *strrchr();
  extern struct FileLock *Lock();
  auto	 struct FileLock *File_Lock;
  auto	 int		  File_Size;
  auto	 char		 *File_Name, *File_Char;
  auto	 char		  File_Path[Name_Size];

  if ((strchr( Name, '*') == NULL) && (strchr( Name, '?') == NULL))
  { /* Take care of simple file name */
    File_Size = File_Parent( Name, File_Path, Name_Size, Print_Info);
    if (File_Size <= 0)
    { if (File_Size == 0) fprintf( stderr, LOCK, Name);
      else		  fprintf( stderr, SIZE, Name, "");
      return;
    }
    File_Path[File_Size - 2] = NUL;
    if ((File_Lock = Lock( File_Path, SHARED_LOCK)) == NULL)
    { fprintf( stderr, LOCK, Name); return; }

    if (!Examine( File_Lock, Print_Info))
      fprintf( stderr, LOCK, Name);
    else if (Print_Info->fib_DirEntryType >= 0)
      fprintf( stderr, TYPE, Print_Info->fib_FileName);
    else if (!File_Insert())
      strcpy( Print_Last->File_Name, File_Path);
  }
  else
  { /* Extract the directory part */
    if (((File_Name = strrchr( Name, '/')) == NULL) &&
        ((File_Name = strchr(  Name, ':')) == NULL) )
         File_Name = Name;
    else memcpy( File_Path, Name, ++File_Name - Name);
    File_Path[File_Name - Name] = NUL;

    /* Initialize the search */
    File_Size = File_Parent( File_Path, File_Path, Name_Size, Print_Info);
    if (File_Size <= 0)
    { if (File_Size == 0) fprintf( stderr, LOCK, Name);
      else		  fprintf( stderr, SIZE, Name, "");
      return;
    }
    if ((File_Lock = Lock( File_Path, SHARED_LOCK)) == NULL)
    { fprintf( stderr, LOCK, File_Path); return; }
^L
    if (!Examine( File_Lock, Print_Info))
      fprintf( stderr, LOCK, File_Path);
    else if (Print_Info->fib_DirEntryType < 0)
      fprintf( stderr, TYPE, Print_Info->fib_FileName);
    else
    { /* Search for the matching files */
      while (ExNext( File_Lock, Print_Info))
      { if ((Print_Info->fib_DirEntryType < 0)		     &&
	    File_Match( Print_Info->fib_FileName, File_Name) )
	{ if (File_Size + strlen( Print_Info->fib_FileName) > Name_Size)
	    fprintf( stderr, SIZE, File_Path, Print_Info->fib_FileName);
	  else if (!File_Insert())
	  { strcpy( Print_Last->File_Name, File_Path);
	    strcpy( &Print_Last->File_Name[File_Size - 1],
		    Print_Info->fib_FileName);
  } } } } }
  UnLock( File_Lock);
}
^L
/************************/
/* Execute the Commands */
/************************/

local	void	Exec_Action( Exec)
/********************************/
char	Exec[];

{ extern int atoi();
  auto	 int Code;

  if (isdigit( *Exec)) Print_Copy = atoi( Exec);
  else switch( Code = Find_Command( Exec))
  { /* Take care of the options */
    case PS_CPI10   : Options( Line_Density, Line_10);		break;
    case PS_CPI12   : Options( Line_Density, Line_12);		break;
    case PS_CPI17   : Options( Line_Density, Line_17);		break;
    case PS_LPI6    : Options( Page_Density, Page_6);		break;
    case PS_LPI8    : Options( Page_Density, Page_8);		break;
    case PS_NARROW  : Options( Line_Formats, Line_8);		break;
    case PS_WIDE    : Options( Line_Formats, Line_13);		break;
    case PS_SHORT   : Options( Page_Formats, Page_85);		break;
    case PS_LONG    : Options( Page_Formats, Page_11);		break;
    case PS_HEADER  : Print_Opts ^=  Opts_Headers;		break;
    case PS_NUMBER  : Print_Opts ^=  Opts_Numbers;		break;
    case PS_KEEP    : Print_Flag &= ~Flag_Deleted;		break;
    case PS_DELETE  : Print_Flag |=  Flag_Deleted;		break;
    case PS_DRAFT   : Print_Flag &= ~Flag_Quality;		break;
    case PS_LETTER  : Print_Flag |=  Flag_Quality;		break;

    /* Take care of the actions */
    case PS_Insert  : case PS_Update  : case PS_Remove :
      if (Print_File != Code) Perform();
      Print_File = Code;					break;

    case PS_Report  : Perform(); Request_Report();		break;

    case PS_Return  : case PS_Change  : case PS_Freeze :
    case PS_Finish  : case PS_Resets  : case PS_Cancel :
    case PS_Resume  : Perform(); Request_Action( Code);		break;

    /* Take care of unknown commands */
    default	    : fprintf( stderr, EXEC, Exec);		break;
} }
^L
/*****************************/
/* This is the Print program */
/*****************************/

public	void	main( argc, argv)
/*******************************/
int	 argc;
char	*argv[];

{ extern struct MsgPort *CreatePort();
  extern void		*AllocMem();

  printf( "Print  V1.0  27-Mar-88  ) Frangois Gagnon\n");

  /* Allocate Initial Structure */
  Print_Port = CreatePort( NULL, 0L);
  if (Print_Port == NULL) { fprintf( stderr, PORT); exit(1); }
  Print_Data = AllocMem( (long) sizeof(struct Inform), MEMF_PUBLIC);
  if (Print_Data == NULL) { fprintf( stderr, DATA); goto Abort1; }
  Print_Info = AllocMem( (long) sizeof(struct FileInfoBlock), MEMF_PUBLIC);
  if (Print_Info == NULL) { fprintf( stderr, DATA); goto Abort2; }
  Default_Values();

  /* Analyze the given arguments */
  if (argc == 1) Request_Report();
  else
  { while (++argv, --argc)
    { if      ((*argv)[0] ==       '-') Exec_Action( &(*argv)[1]);
      else if (Print_File == PS_Remove) Request_Remove( *argv);
      else				File_Search( *argv);
    }
    if (Print_List != NULL) Perform();
  }

  /* Deallocate Initial Structure */
  Abort3: FreeMem( Print_Info, (long) sizeof(struct FileInfoBlock));
  Abort2: FreeMem( Print_Data, (long) sizeof(struct Inform));
  Abort1: DeletePort( Print_Port);
}
SHAR_EOF
cat << \SHAR_EOF > Spool.c

/* Spool.c    V1.0.0    27-Mar-88    ) Frangois Gagnon */


/*
   Copyright ) 1988  Frangois Gagnon,  All Rights Reserved.

   The author does not make any warranty expressed or implied,
   or assumes any liability or responsiblity for the use of
   this software.

   Permission is hereby granted to copy, reproduce, redistribute
   or otherwise use this software as long as it is for non-profit.
   This notice and the above copyright notice must remain intact
   and appear on all copies.

   Permission is also granted to correct any problems with this
   software, but modifications and improvements are reserved by
   the author.
*/

#include <stdio.h>
#include <ctype.h>

#include <exec/memory.h>
#include <exec/ports.h>
#include <exec/nodes.h>
#include <libraries/dos.h>
#include <intuition/preferences.h>

#include "stdefs.h"
#include "Print_Spool.h"
^L
/*********************************/
/* Local Constants and Variables */
/*********************************/

/* Define constant used in the program */
#define Flag_Test	(Line_Formats | Page_Formats)
#define Flag_Line	(Line_Density | Line_Formats)
#define Flag_Page	(Page_Density | Page_Formats)
#define Flag_Used	(Page_Density | Page_Formats | Opts_Headers)
#define Opts_Page(Opts) ((Opts & Flag_Page) >> 3)
#define Opts_Used(Opts) ((Opts & Flag_Used) >> 3)

local	int	Line_Size[8] = { 80, 96, 132, 80, 136, 164, 232, 136 };
local	int	Page_Size[4] = { 51, 68, 66, 88 };
local	int	Page_Used[8] = { 48, 64, 63, 84, 48, 63, 63, 83 };
local	char	Line_Code[4] = { '0', '2', '4', '0' };

/* Declarations of status messages */
local	char	HEAD[] = "\nSpool  V1.0  27-Mar-88  ) Frangois Gagnon\n\n";
local	char	LOGS[] = "Spool: Unable to access log file.\n";
local	char	EXEC[] = "Spool: Spool is already running.\n";
local	char	INIT[] = "Spool: Unable to create a port.\n";

local	char	OPEN[] = "Unable to open %s for input.\n";
local	char	COPY[] = "Printed %s ... %ld chars ... %ld lines ... %ld pages.\n";
local	char	BACK[] = "Reset %s after %ld chars ... %ld lines ... %ld pages.\n";
local	char	STOP[] = "Canceled %s after %ld chars ... %ld lines ... %ld pages.\n";
local	char	MOVE[] = "Removed %s after %ld chars ... %ld lines ... %ld pages.\n";

/* Definition for Execution */
local	FILE	*Spool_File;
local	char	*Spool_Logs = "RAM:Spool.log";

local	struct	Inform  *Info_Port;
local	struct	MsgPort *Data_Port;

local	Info	Status = { 1, Wait_State, 0, FALSE, 0, 0, 0, NULL };

local	FILE	*From_File, *Dest_File = NULL;
local	short	Line_Limit, Line_Count;
local	int	Char_Saved;

/* Definition for Manipulation */
local	File	**Temp_Move, *Temp_File;
local	File	**Temp_Skip, *Temp_Test;
local	byte	  Temp_Opts;

/* Simple typing optimization */
#define	Next_Char() { Char_Saved = fgetc( From_File); ++Status.Char_Print; }
^L
/******************************/
/* File Name Pattern Matching */
/******************************/

local	 int	File_Match( Name, Data)
/*************************************/
register char	*Name, *Data;

{ auto	   char *Save_Name[10],
		*Save_Data[10];
  register int	 Save;

  if ((*Data == NUL) || (*Name == NUL)) return FALSE;
  Save = -1;

  while ((*Data != NUL) || (*Name != NUL))
  { if (*Data == '*')
    { if (*++Data == NUL) return TRUE;
      if ( ++Save ==  10) return FALSE;
      Save_Name[Save] = Name;
      Save_Data[Save] = Data;
      continue;
    }
    if (   ((*Data == '?') && (*Name != NUL))
	|| (toupper(*Data) == toupper(*Name)))
    { ++Data; ++Name; continue; }

    if (*Name == NUL) --Save;
    if ( Save <    0) return FALSE;
    Name = ++Save_Name[Save];
    Data =   Save_Data[Save];
  }
  return TRUE;
}
^L
/******************************/
/* Initialization of Defaults */
/******************************/

#define INTUITIONNAME		"intuition.library"
public	struct IntuitionBase	*IntuitionBase;

local	void	Default_Values()
/******************************/

{ auto	 struct Preferences	PrefBuffer;
  auto	 int			Temp;
  extern struct IntuitionBase  *OpenLibrary();
  extern struct Preferences    *GetPrefs();

  /* Fetch the information from the system */
  IntuitionBase = OpenLibrary( INTUITIONNAME, 0L);
  if (IntuitionBase == NULL) return;
  GetPrefs( &PrefBuffer, (long) sizeof( struct Preferences));
  CloseLibrary( IntuitionBase);

  /* Extract the information from the structure */
  Temp = (PrefBuffer.PrintSpacing == EIGHT_LPI ? 8 : 6);
  if (PrefBuffer.PaperLength / Temp == 11) Status.Opts_State |= Page_11;
  if (PrefBuffer.PaperSize == W_TRACTOR)   Status.Opts_State |= Line_13;
}


/**********************************/
/* Stop Printing The Current File */
/**********************************/

local	int	Stop_Printing( Spool_Show)
/****************************************/
char	*Spool_Show;

{ extern FILE	*fopen();
  if ((Status.Prog_State != Exec_State) &&
      (Status.Prog_State != File_State) &&
      (Status.Prog_State != Stop_State) ) return FALSE;

  if (Line_Count != Line_Limit) putc( '\f', Dest_File);
  fclose( From_File);
  if (Status.Prog_State == Exec_State)	Status.Prog_State = Wait_State;
  else					Status.Prog_State = Susp_State;

  if ((Spool_File = fopen( Spool_Logs, "a")) == NULL) Spool_File = stderr;
  fprintf( Spool_File,			 Spool_Show,
	   Status.File_Print->File_Name, Status.Char_Print,
	   Status.Line_Print,		 Status.Page_Print);
  if (Spool_File != stderr) fclose( Spool_File);
  return TRUE;
}
^L
/********************************/
/* Execute all pending commands */
/********************************/

local	void	Exec_Print()
/**************************/

{ extern struct	Inform	*GetMsg();
  extern File		*AllocMem();
  extern int		 strncmp();

  while ((Info_Port = GetMsg( Data_Port)) != NULL)
  { if (Info_Port->Number != PROTOCOL)
      Info_Port->Number = PROTOCOL;
    else switch( Info_Port->Action)
    {
      case PS_Insert :
	Temp_Skip = &Status.File_Print;
	while (*Temp_Skip) Temp_Skip = &(*Temp_Skip)->File_Next;
	*Temp_Skip = Info_Port->Detail.Insert;
	Info_Port->Detail.Insert = NULL;
	break;

      case PS_Update :
	for  (Temp_Test  = Info_Port->Detail.Update;
	      Temp_Test != NULL;
	      Temp_Test  = Temp_Test->File_Next)
	{
	  for  (Temp_File  = Status.File_Print;
		Temp_File != NULL;
		Temp_File  = Temp_File->File_Next)
	  {
	    if (strncmp( Temp_Test->File_Name,
			 Temp_File->File_Name, Name_Size) == 0)
	    { if ((Temp_Test->File_Copy == 0)      &&
		  (Temp_File == Status.File_Print) ) Stop_Printing( STOP);
	      Temp_File->File_Opts  = Temp_Test->File_Opts;
	      Temp_File->File_Flag  = Temp_Test->File_Flag;
	      Temp_File->File_Copy  = Temp_Test->File_Copy;
	      Temp_File->File_Size  = Temp_Test->File_Size;
	      Temp_Test->File_Flag |= Flag_Updated;
	      break;
	} } }
	break;
^L
      case PS_Remove :
	Temp_Move = &Temp_File;
	Temp_Skip = &Status.File_Print;
	while ((Temp_Test = *Temp_Skip) != NULL)
	{ if (File_Match( Temp_Test->File_Name,
			  Info_Port->Detail.Remove))
	  { if (Temp_Skip == &Status.File_Print) Stop_Printing( MOVE);
	    *Temp_Move = Temp_Test;
	    *Temp_Skip = Temp_Test->File_Next;
	    Temp_Move  = &Temp_Test->File_Next;
	  }
	  else Temp_Skip = &Temp_Test->File_Next;
	}
	*Temp_Move = NULL;
	Info_Port->Detail.Insert = Temp_File;
	break;

      case PS_Report :
	Info_Port->Detail.Status = Status;
	Temp_Skip = &Info_Port->Detail.Status.File_Print;
	for (Temp_File  = Status.File_Print;
	     Temp_File != NULL;
	     Temp_File  = Temp_File->File_Next)
	{
	  Temp_Test = AllocMem( (long) sizeof( File), MEMF_PUBLIC);
	  if (Temp_Test == NULL)
	  { Info_Port->Detail.Status.Opts_State |= Flag_Partial; }
	  else
	  { *(*Temp_Skip = Temp_Test) = *Temp_File;
	    Temp_Skip = &Temp_Test->File_Next;
	} }
	*Temp_Skip = NULL;
	break;

      case PS_Return :
	Status.Flag_State = !Status.Flag_State;
	break;

      case PS_Change :
	if (Status.File_Print != NULL)
	{ Status.Opts_State &= ~Flag_Test;
	  Status.Opts_State |= Status.File_Print->File_Opts & Flag_Test;
	}
	break;

      case PS_Freeze :
	switch( Status.Prog_State)
	{ case Wait_State : case Page_State : case Open_State :
	    Status.Prog_State = Susp_State; break;
	  case Exec_State : case File_State :
	    Status.Prog_State = Stop_State; break;
	}
	break;
^L
      case PS_Finish :
	  switch( Status.Prog_State)
	  { case Wait_State : case Page_State : case Open_State :
	      Status.Prog_State = Susp_State; break;
	    case Exec_State : case Stop_State :
	      Status.Prog_State = File_State; break;
	  }
	  break;
	  
      case PS_Resets :
	Stop_Printing( BACK);
	Status.Prog_State = Susp_State;
	break;

      case PS_Cancel :
	if (Stop_Printing( STOP)) --Status.File_Print->File_Copy;
	Status.Prog_State = Susp_State;
	break;

      case PS_Resume :
	if (Status.Prog_State == Susp_State) Status.Prog_State = Wait_State;
	if (Status.Prog_State == Stop_State) Status.Prog_State = Exec_State;
	if (Status.Prog_State == File_State) Status.Prog_State = Exec_State;
	break;
    }
    Info_Port->Packet.mn_Node.ln_Type = NT_REPLYMSG;
    ReplyMsg( Info_Port);
} }
^L
/******************************************/
/* Perform according to the current state */
/******************************************/

local	int	Exec_Spool()
/**************************/

{ extern FILE *fopen();

  switch( Status.Prog_State)
  {
    case Wait_State : case Page_State : case Open_State :

      /* Eliminate Old File Information */
      while ((Temp_File = Status.File_Print) != NULL)
      { if (Temp_File->File_Copy != 0) break;
	Status.File_Print = Temp_File->File_Next;
	if (Temp_File->File_Flag & Flag_Deleted)
	  remove( Temp_File->File_Name);
	FreeMem( Temp_File, (long) sizeof( Temp_File));
      }

      /* Wait if there is nothing to do */
      if (Temp_File == NULL)
      { Status.Prog_State = Wait_State;
	if (Dest_File != NULL) { fclose( Dest_File); Dest_File = NULL; }
	if (Status.Flag_State)
	{ Forbid();
	  if ((Info_Port = GetMsg( Data_Port)) == NULL)
	  { DeletePort( Data_Port); Permit(); return FALSE; }
	  PutMsg( Data_Port, Info_Port);
	  Permit();
	}
	else WaitPort( Data_Port);
	break;
      }

      /* Check the format of the paper */
      Temp_Opts = (Status.Opts_State ^ Temp_File->File_Opts);
      if (Temp_Opts & Flag_Test)
      { if (Dest_File != NULL) { fclose( Dest_File); Dest_File = NULL; }
	Status.Prog_State = Page_State;
	WaitPort( Data_Port);
	break;
      }

      /* Request access to the printer */
      if ((Dest_File			     == NULL) &&
	  ((Dest_File = fopen( "PRT:", "w")) == NULL) )
      { Status.Prog_State = Open_State;
	Delay( 5L * TICKS_PER_SECOND);
	break;
      }
^L
      /* Request access to the data file */
      From_File = fopen( Status.File_Print->File_Name, "r");
      if (From_File == NULL)
      { Status.File_Print->File_Copy = 0;
	Status.Prog_State = Wait_State;

	Spool_File = fopen( Spool_Logs, "a");
	if (Spool_File == NULL) Spool_File = stderr;
	fprintf( Spool_File, OPEN, Status.File_Print->File_Name);
	if (Spool_File != stderr) fclose( Spool_File);
	break;
      }

      /* Initialize all control variables */
      Status.Opts_State = Status.File_Print->File_Opts;
      Status.Char_Print = Status.Line_Print = Status.Page_Print = 0;

      Line_Limit = Page_Used[Opts_Used(Status.Opts_State)];
      Line_Count = Line_Limit;

      /* Initialize the printer */
      fprintf( Dest_File,
	"\033c\033#1\033#5\033[%cw\033[1;%ds\033[%cz\033[%dt\033[%c\"z",
	Line_Code[Status.Opts_State & Line_Density],
	Line_Size[Status.Opts_State & Flag_Line],
	(Status.Opts_State & Page_Density ? '0' : '1'),
	Page_Size[Opts_Page(Status.Opts_State)],
	(Status.File_Print->File_Flag & Flag_Quality ? '2' : '1'));
	
      /* Start printing the specified file */
      Status.Prog_State = Exec_State;
      Char_Saved	= fgetc( From_File);
^L
    case Exec_State : case File_State :
      /* Print the file one line at a time */
      if (Char_Saved == EOF)
      { Stop_Printing( COPY);
	--Status.File_Print->File_Copy;
	break;
      }

      /* Deal with form feed characters */
      if (Char_Saved == '\f')
      { if (Line_Count != Line_Limit)
	{ putc( '\f', Dest_File); Line_Count = Line_Limit; }
	Next_Char();
	break;
      }

      /* Deal with page counters and headers */
      if (Line_Count == Line_Limit)
      { ++Status.Page_Print; Line_Count = 0;
	if (Status.Opts_State & Opts_Headers)
	{ fprintf( Dest_File, "%-20.20s    %-42.42s    Page %3ld\n\n\n",
		   Status.File_Print->File_Date,
		   Status.File_Print->File_Name,
		   Status.Page_Print);
	  Line_Count = 3;
      } }

      /* Time to print the line */
      ++Status.Line_Print; ++Line_Count;
      if (Status.Opts_State & Opts_Numbers)
	fprintf( Dest_File, "%6ld: ", Status.Line_Print);
      while ((Char_Saved !=  EOF) &&
	     (Char_Saved != '\n') &&
	     (Char_Saved != '\f') )
      { putc( Char_Saved, Dest_File); Next_Char(); }
      if (Char_Saved == '\n') Next_Char();
      fputs( (Line_Count == Line_Limit ? "\n\f" : "\n"), Dest_File);
      break;

    case Susp_State :
      /* Wait for new comamnds */
      if (Dest_File != NULL) { fclose( Dest_File); Dest_File = NULL; }
    case Stop_State :
      WaitPort( Data_Port);
      break;
  }
  return TRUE;
}
^L
/*****************************/
/* This is the Spool program */
/*****************************/

public	void	main( argc, argv)
/*******************************/
int	 argc;
char	*argv[];

{ extern struct MsgPort *FindPort(), *CreatePort();
  extern FILE		*fopen();

  fprintf( stderr, HEAD);
  if (FindPort( SPOOLNAME) != NULL)
  { fprintf( stderr, EXEC); exit( 1); }

  if (argc > 1) Spool_Logs = argv[1];
  if ((Spool_File = fopen( Spool_Logs, "a")) == NULL)
  { fprintf( stderr, LOGS, Spool_Logs); exit(1); }
  fprintf( Spool_File, HEAD);
  fclose( Spool_File);

  if ((Data_Port = CreatePort( SPOOLNAME, 0L)) == NULL)
  { fprintf( stderr, INIT); exit( 1); }
  Default_Values();

  do Exec_Print();
  while (Exec_Spool());
}
SHAR_EOF
#	End of shell archive
exit 0