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