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

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

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

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

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

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

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

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

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

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

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

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; }

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

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

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