
/*
 * Copyright (c) Kopriha Software,  1990
 *       All Rights Reserved
 *
 * C.Port.CC
 *
 * Description:
 *     This module is the heart of the file support of the
 *     printer port driver.
 *
 *
 *   Table of Contents:
 *
 *     c_port_open . . . . . . . . . . Open the data/command files
 *     c_port_write  . . . . . . . . . Write the buffer of data
 *     c_port_close  . . . . . . . . . Close the data/command files
 *                                     (if opened)
 *
 *
 *  History:Mar 25, 1991  Dave  Created this file
 *
 */

/*
 *  define DEBUG_CODE
 *                     - add # to define to create the local
 *                       debug code (IE:module)
 */


#pragma noroot

#include <ctype.h>
#include <types.h>
#include <quickdraw.h>
#include <control.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

#define PATH_LPQ ":var:spool:lpq"

#include "ks.fileio.e"

#include "ks.memory.h"

#ifndef _ERRORS_
#include "Errors.h"
#endif

#ifndef __MEMORY__
#include <memory.h>
#endif

#ifndef __MISCTOOL__
#include <MiscTool.h>
#endif

#ifndef __WINDOW__
#include <Window.h>
#endif
#include <fcntl.h>
#include <signal.h>
#include <gno/gno.h>
#include <sys/ports.h>

/*
 * Local variable declarations...
 *
 *  Start with structures...
 */

ResultBuf255  next_filename;
Word          optionList[20];
DirEntryRecGS dir_entry;
GSString32Ptr new_data_path;


/*
 * Now define all the pointers to structures...
 */

KS_FILE_PTR   data_file_ptr = (KS_FILE_PTR) NULL;
                                /* Buffered file structure ptr           */
KS_FILE_PTR   dir_file_ptr;     /* Used to get all of the filenames      */
                                /*  from spool directory                 */
Handle        spoolpath_handle; /* Handle allocated for spool path       */
GSString32Ptr spool_directory_ptr;
                                /* Pointer to the spoolfile directory    */
                                /*  pathname (as we've read it from our  */
                                /*  datafile).                           */

/*
 * On to the simple variables...
 */

KS_STRUCT_ID  ks_expected_id;   /* The structure ID we expected to find  */
                                /*  during a structure id check          */
Word          ProgramID;        /* Our Memory Manager UserId             */


KS_E_ERROR  error;              /* Used to catch error codes             */
KS_E_ERROR  error2;             /* Used to catch secondary errors        */

size_t      pathname_length;    /* Length (in bytes) of the spool path   */
Word        max_spool_number;   /* Which is the mac spool number found?  */
Word        num_entries;        /* How many directory entries are there? */
Word        spoolfile_number;   /* The spoolfile # we decided to use     */
char        cnumber;            /* Used during conversion of spoolfile # */
                                /*  into part of the spool pathname      */
char        delimiter;          /* Delimiter for pathname                */
#if 0
Word        index1, index2;     /* Index variables used in for loops     */
#endif

Word        error_message;      /* The error message to display          */
Word        button;             /* Which button of our error dialog did  */
                                /*  our user hit?                        */
Word        open_count = 0;     /* Count of how many open's we've done...*/

char 	    tmp[128];
Word	    port_spoolnum = 1;	/* count of how many open's we've done   */

/*
 * Now define some constants...
 */
typedef struct
    {
    Word length;
    char text[35];
    } GSString35;

/* GSString35 fileport_datapath = {22, ":Natasha:FilePort.Data" }; */
GSString35 fileport_datapath = {30, "*:System:Drivers:FilePort.Data" };

#define       SPOOL_FILENAME_EXTENSION     3 /* 2 for ##, 1 for NULL */
#define       BASE_SPOOL_FILENAME_LENGTH   9

char          base_spool_filename[BASE_SPOOL_FILENAME_LENGTH+1] =
                                                         {"SpoolFile"};

#define SPOOLFILE_FILETYPE ((Word) 4)
#define SPOOLFILE_AUXTYPE  ((LongWord) 0)

/*
 * Port flag states
 */

enum port_flags {PORT_NOT_ACTIVE = 1,
                 PORT_READY,
                 PORT_SPOOLING,
                 PORT_ERROR};

Word        port_status = PORT_NOT_ACTIVE;
                           /* Current status of port driver...      */

#if 0	/* we take these out of libc instead */
void *malloc(size_t size)
{
handle h;
	h = NewHandle(size,ProgramID,0,0l);
	return *h;
}

void free(void *data)
{
	DisposeHandle(FindHandle(data));
}
#endif

/* ****************************************************************** *
 *   c_port_open - Create and open the data/command spool files for   *
 *                 the port driver.                                   *
 *                                                                    *
 *   History: Mar 25, 1991  Dave  Created this routine                *
 *                                                                    *
 *            Dec 23, 1991  Dave  Completed pathname expansion (we    *
 *                                now handle prefixes (#:),           *
 *                                appleshare users (@:), and system   *
 *                                disk (*:) references and any        *
 *                                set of delimiters.                  *
 *                                                                    *
 * ****************************************************************** */

#define ROUTINE_NAME "c_port_open"

void c_port_open(void)
{
extern GSString32Ptr __C2GSMALLOC(char *);

    /* ************************************************************** *
     *  No local variables...                                         *
     * ************************************************************** */


    /* ************************************************************** *
     *  Check if we already have a file open... Apple said this should*
     *  never happen... but I really don't believe them...            *
     * ************************************************************** */

    if ((data_file_ptr != (KS_FILE_PTR) NULL) ||
        (port_status != PORT_NOT_ACTIVE))
        {
        open_count++;
        return;
        };


    /* ************************************************************** *
     *  Get the applications memory manager user ID - We'll need it   *
     *  to be able to allocate memory...                              *
     * ************************************************************** */

    ProgramID = MMStartUp();


    /* ************************************************************** *
     *  Set the size of the data buffer to 64k...                     *
     * ************************************************************** */

    KS_FILE_SET_BUFFER_SIZE((LongWord) (64*1024),
                            error);
    if (error != KS_E_SUCCESS)
        {
        error_message = BAD_BUFFER_SIZE;
        goto ERROR_BAD_BUFFER_SIZE;
        };


    /*  > Open our data file in System:filePort.data: and read it
     *
     *	The line print spool directory is hard-coded
     */

    /*	Create a spool file name, and set it up in GS/OS format for use
     *	by the KS_FILE_OPEN routine
     */

    sprintf(tmp,PATH_LPQ ":tmp.%08lX",GetTick());
    spool_directory_ptr = __C2GSMALLOC(tmp);

    /* create the spool file now */
    KS_FILE_CREATE((GSString255Ptr) spool_directory_ptr,
                   SPOOLFILE_FILETYPE,
                   SPOOLFILE_AUXTYPE,
                   error);
    if (error != KS_E_SUCCESS)
        {
        error_message = CANT_CREATE_SPOOL_FILE;
        goto ERROR_CANT_CREATE_SPOOL_FILE;
        };

    /* ************************************************************** *
     *  Open the data spool file                                      *
     * ************************************************************** */

    KS_FILE_OPEN((GSString255Ptr) spool_directory_ptr,
                 KS_FILE_WRITE_ACCESS,
                 KS_FILE_DATA_FORK,
                 KS_FILE_BUFFER_IO,
                 &data_file_ptr,
                 error);
    if (error != KS_E_SUCCESS)
        {
        error_message = CANT_OPEN_SPOOL_FILE;
        goto ERROR_CANT_OPEN_SPOOL_FILE;
        };


    /* ************************************************************** *
     *  Releasing pathname back to free memory...                     *
     *  No error checking because what would we do??                  *
     * ************************************************************** */


    KS_MEMORY_DEALLOCATE(spoolpath_handle,
                         error);


    /* ************************************************************** *
     *  Set a flag and a counter...                                   *
     * ************************************************************** */

    port_status = PORT_SPOOLING;
    open_count = 1;


    /* ************************************************************** *
     *  Everything is all set - return to our caller...               *
     * ************************************************************** */

    return;



    /* ************************************************************** *
     *  Error processing for all of the error cases of the above code *
     *                                                                *
     *  Once we clean up whatever needs to be cleaned up, then we     *
     *  will get around to putting up a dialog box for our user.      *
     * ************************************************************** */

ERROR_CANT_OPEN_SPOOL_FILE:

    KS_FILE_DELETE((GSString255Ptr) spool_directory_ptr,
                   error2);

ERROR_TOO_MANY_SPOOLFILES:

ERROR_CANT_CREATE_SPOOL_FILE:

ERROR_CANT_GET_NEXT_FILE:

ERROR_CANT_GET_ENTRY_COUNT:

    KS_FILE_CLOSE(dir_file_ptr, error2);

ERROR_CANT_OPEN_DIR:

    KS_MEMORY_DEALLOCATE(spoolpath_handle,
                         error2);

    goto ERROR_MESSAGE;

ERROR_CANT_READ_PATHNAME:

    KS_MEMORY_DEALLOCATE(spoolpath_handle,
                         error2);

ERROR_CANT_ALLOCATE_PATHNAME:

    KS_FILE_CLOSE(dir_file_ptr, error2);

ERROR_CANT_OPEN_CONFIG_FILE:

ERROR_BAD_BUFFER_SIZE:

ERROR_MESSAGE:

    port_status = PORT_ERROR;

    ERROR_DIALOG(error_message, error, button);

    return;


}   /* End of c_port_open()                                           */



/* ****************************************************************** *
 *   c_port_write - Write to the data/command spool files for         *
 *                  the port driver.                                  *
 *                                                                    *
 *   History: Mar 27, 1991  Dave  Created this routine                *
 * ****************************************************************** */

#undef ROUTINE_NAME
#define ROUTINE_NAME "c_port_write"

void c_port_write(char *buffer, Word size)
{
    /* ************************************************************** *
     *  No local variables...                                         *
     * ************************************************************** */


    /* ************************************************************** *
     *  Now lets see if it looks like we are allowed to be printing...*
     *  Check for the following:                                      *
     *    1) We are at level 1 for spool files...                     *
     *    2) We have an open spool file                               *
     *    3) No errors have occured (yet)                             *
     * ************************************************************** */

    if ((open_count != 1) ||
        (data_file_ptr == (KS_FILE_PTR) NULL) ||
        (port_status != PORT_SPOOLING))
        {
        return;
        };


    /* ************************************************************** *
     *  Looks good... write this data to the buffered output file.    *
     *  Remember to check for an error...                             *
     * ************************************************************** */

    KS_FILE_WRITE(data_file_ptr,
                  KS_NEXT_FILE_POSITION,
                  (LongWord) size,
                  TO_POINTER(buffer),
                  error);
    if (error != KS_E_SUCCESS)
        {
        error_message = CANT_WRITE_DATA;
        goto ERROR_CANT_WRITE_DATA;
        };


    /* ************************************************************** *
     *  Everything is all done here - return to our caller...         *
     * ************************************************************** */

    return;


    /* ************************************************************** *
     *  Error processing for all of the error cases of the above code *
     *                                                                *
     *  Something bad happened... that means we have to give up       *
     *  on printing and give our user an error dialog that tells      *
     *  them what happened.                                           *
     * ************************************************************** */

ERROR_CANT_WRITE_DATA:

    /* ************************************************************** *
     *  First we close the spool file (can't use it anymore now...)   *
     *  then give our user some feed back (like an error message).    *
     * ************************************************************** */

    KS_FILE_CLOSE(data_file_ptr, error2);

    data_file_ptr = (KS_FILE_PTR) NULL;
    open_count--;
    port_status = PORT_ERROR;

    ERROR_DIALOG(error_message, error, button);

    return;
}

/* pragma debug -1 */


/* ****************************************************************** *
 *   c_port_close - Close the data/command spool files for            *
 *                  the port driver.                                  *
 *                                                                    *
 *   History: Mar 27, 1991  Dave  Created this routine                *
 * ****************************************************************** */

#undef ROUTINE_NAME
#define ROUTINE_NAME "c_port_close"

void c_port_close(void)
{
static char cf[128];
int slf;
int lpdport;
int cfid;
long msg;


    /* ************************************************************** *
     *  Now lets see if it looks like we can close the spoolfile      *
     *  Check for the following:                                      *
     *    1) We are at level 1 for spool files...                     *
     *       (otherwise simply decrement the count and return)        *
     *    2) We have an open spool file                               *
     *    3) No errors have occured                                   *
     * ************************************************************** */

    if (open_count > 1)
        {
        open_count--;
        return;
        };

    if ((data_file_ptr == (KS_FILE_PTR) NULL) ||
        (port_status != PORT_SPOOLING))
        {
        return;
        };


    /* ************************************************************** *
     *  Close the spool file and reset the file pointer to be NULL    *
     *  so we know that the file has been closed.                     *
     *                                                                *
     *  Note: I haven't added error checking here because there really*
     *        isn't anything we can do if an error occurs...          *
     * ************************************************************** */

    KS_FILE_CLOSE(data_file_ptr,
                  error);

    data_file_ptr = NULL;
    open_count = 0;
    port_status = PORT_NOT_ACTIVE;

    lpdport = pgetport("LPD");
    if (lpdport == -1) {
        error_message = DAEMON_NOT_ACTIVE;
        goto ERROR_DAEMON_NOT_ACTIVE;
    }
    msg = (1l << 16) | getpid();
    psend(lpdport,msg);
    while ((cfid = (int) procreceive()) != -1);

    sprintf(cf,PATH_LPQ ":cf.%05d",cfid);
    slf = open(cf,O_WRONLY);
    if (slf < 0) { SysBeep(); SysBeep(); }
    write(slf,"l",1);
    write(slf,tmp,strlen(tmp));
    write(slf,"\r",1);
    write(slf,"U",1);
    write(slf,tmp,strlen(tmp));
    write(slf,"\r",1);
    close(slf);
    /* register the job */
    msg = (2l << 16) | cfid;
    if (psend(lpdport,msg) == -1) SysBeep();

    /* ************************************************************** *
     *  Everything is all set - return to our caller...               *
     * ************************************************************** */

    return;

ERROR_DAEMON_NOT_ACTIVE:

    /* ************************************************************** *
     *  First we close the spool file (can't use it anymore now...)   *
     *  then give our user some feed back (like an error message).    *
     * ************************************************************** */

    KS_FILE_CLOSE(data_file_ptr, error2);
    /* delete the spool file */
    remove(tmp);

    data_file_ptr = (KS_FILE_PTR) NULL;
    open_count--;
    port_status = PORT_ERROR;

    ERROR_DIALOG(error_message, error, button);

    return;
}
