Received: from j.cc.purdue.edu by VM.CC.PURDUE.EDU ; Mon, 13 Jun 88 18:05:42 EST
Received: by j.cc.purdue.edu (5.54/1.14)
    id AA16109; Mon, 13 Jun 88 18:01:45 EST
Date: Mon, 13 Jun 88 18:01:45 EST
From: ain@j.cc.purdue.edu (Patrick White)
Message-Id: <8806132301.AA16109@j.cc.purdue.edu>
To: fys-ma@fintuvm.BITNET
Subject: comp.sources.amiga:   sources/amiga/volume5/printspool.s.sh
 
Newsgroups: comp.sources.amiga
Subject: printspool (sources)
Keywords: printer spooler, printspool
Approved: ain@j.cc.purdue.edu    (Patrick White)
 
Program Name:    printspool  (sources)
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.                */

#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 */

/*************************************/
/* 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)

/*****************************/
/* 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);
}
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"

/*********************************/
/* 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; }

/******************************/
/* 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;
}

/******************************/
/* 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;
}

/********************************/
/* 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;

      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;

      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);
} }

/******************************************/
/* 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;
      }

      /* 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);

    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;
}

/*****************************/
/* 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
