
/* 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());
}
