/*  sysmac.c                       Copyright (C) 1989-2002 Codemist Ltd */

/*
 * Macintosh system-specific stuff
 */

/*
 * This code may be used and modified, and redistributed in binary
 * or source form, subject to the "CCL Public License", which should
 * accompany it. This license is a variant on the BSD license, and thus
 * permits use of code derived from this in either open and commercial
 * projects: but it does require that updates to this code be made
 * available back to the originators of the package.
 * Before merging other code in with this or linking this code
 * with other packages or libraries please check that the license terms
 * of the other material are compatible with those of this.
 */


/* Signature: 4bed9bc9 08-Apr-2002 */

#include "machine.h"

#include <stdarg.h>
#include <stddef.h>
#include <string.h>
#include <ctype.h>

#include "tags.h"
#include "externs.h"

#include <sysequ.h>
#include <unix.h>
#include <files.h>
#include <unix.h>
#include <retrace.h>
#include <timer.h>

#undef nil          /* #define'd by Think C headers but not wanted (by me) */

static void get_home_directory(char *b, int length);
static void get_users_home_directory(char *b, int length);

#include "filename.c"
/*
 * This is a dummy definition of get_truename, here so that everything
 * will link. Both the calling convention used here and the exact
 * meaning and implementation may be under gentle review!
 */

char *get_truename(char *filename, char *old, size_t n)
{
    char *w;
    process_file_name(filename, old, n);
    if (*filename == 0)
    {   aerror("truename");
        return NULL;
    }
    w = (char *)(*malloc_hool)(1+strlen(filename));
    if (w == NULL) return w;
    strcpy(w, filename);
    return w;
}

char *my_getenv(char *s)
{
    return getenv(s);
}

int my_system(char *s)
{
    return 0;       /* Not available - sorry! */
}

static void get_home_directory(char *b, int length)
{
    char *w = my_getenv("home");
    if (w != NULL) strcpy(b, w);
    else
    strcpy(b, ":");     /* Not satisfactory */
}

static void get_users_home_directory(char *b, int length)
{
    char *w, h[LONGEST_LEGAL_FILENAME];
    sprintf(h, "home$%s", b);
    w = my_getenv(h);
    if (w != NULL) strcpy(b, w);
    else
    strcpy(b, ":");     /* Not satisfactory */
}

int batchp()
{
    return !isatty(fileno(stdin));
}

/*
 * The next procedure is responsible for establishing information about
 * where the main checkpoint image should be recovered from, and where
 * and fasl files should come from.
 */
char *find_image_directory(int argc, char *argv[])
{
    char image[LONGEST_LEGAL_FILENAME];
    char pgmname[LONGEST_LEGAL_FILENAME];
    char *w;
/*
 * For the Macintosh I use the data fork of the application image as
 * a place to store useful stuff.
 */
    WDPBRec pb1;
    CInfoPBRec pb2;
    int i, j, r, p = LONGEST_LEGAL_FILENAME;
    long id;
    pgmname[--p] = 0;
    sprintf(image, "%#s", CurApName);       /* name of current application */
    strcpy(image, program_name);
    i = strlen(image);
    p -= i;
    memcpy(&pgmname[p], image, i);          /* copy to the end of a buffer */
    pb1.ioNamePtr = (unsigned char *)image;
    PBHGetVolSync(&pb1);                    /* find current working dir & volume */
    id = pb1.ioWDDirID;                     /* working directory identifier      */
    while (id != 0)
    {   pb2.dirInfo.ioFDirIndex = -1;       /* use directory ID, not file name/index */
        pb2.dirInfo.ioVRefNum = pb1.ioVRefNum;  /* look in this volume               */
        pb2.dirInfo.ioDrDirID = id;             /* get info about this directory     */
        pb2.dirInfo.ioNamePtr = (unsigned char *)image;
        r = PBGetCatInfoSync(&pb2);         /* Read catalogue - find name & parent   */
        if (r != 0) break;                  /* failed - must be at top of tree       */
        id = pb2.dirInfo.ioDrParID;         /* go on up to parent directory          */
        i = image[0] & 0xff;                /* length of name of this directory      */
        pgmname[--p] = ':';
        p -= i;
        if (p < 10)
        {   fprintf(stderr, "\nPlease re-install this package nearer the top of\n");
            fprintf(stderr, "your directory hierarchy.  It will not work this far down\n");
            abort();
        }
        memcpy(&pgmname[p], &image[1], i);  /* stick names together with ':'s        */
    }
    strcpy(image, &pgmname[p]);             /* Put complete name in convenient place */

/*
 * I copy from local vectors into malloc'd space to hand my
 * answer back.
 */
    w = (char *)(*malloc_hook)(1+strlen(image));
/*
 * The error exit here seem unsatisfactory...
 */
    if (w == NULL)
    {   fprintf(stderr, "\n+++ Panic - run out of space\n");
        exit(EXIT_FAILURE);
    }
    strcpy(w, image);
    return w;
}

int truncate_file(FILE *f, long int where)
{
    if (fflush(f) != 0) return 1;
    return SetEOF(fileno(f), where);  /* Returns zero if successs */
}

#ifdef TICK_STREAM

void accept_tick(void)
{
/*
 * This is where I can put things that need to be done regularly.
 * This will need to involve prodding the window manager etc etc.
 * At present I do NOTHING...
 */
    return;
}


#ifdef USE_VBL

static VBLTask VBL_control_block;

static pascal void deal_with_tick(void)
{
/*
 * This way of recovering register a5 was OK before the multi-finder,
 * but is UNSATISFACTORY now.  It is proper to use the time manager
 * instead of VBL, but only recent versions of the time manager are any good
 * so it is a bit of a mess.
 */
    asm { move.l a5, -(sp)
          movea.l CurrentA5, a5 }
    VBL_control_block.vblCount = 5;
    if (tick_pending == 0)
    {
        if (already_in_gc) tick_on_gc_exit = YES;
        else
        {   Lisp_Object nil = C_nil;
            CSLbool xxx = NO;
            if (exception_pending()) flip_exception(), xxx = YES;
            tick_pending = YES;
            saveheaplimit = heaplimit;
            heaplimit = fringe;
            savevheaplimit = vheaplimit;
            vheaplimit = vfringe;
            savecodelimit = codelimit;
            codelimit = codefringe;
            savestacklimit = stacklimit;
            stacklimit = stackbase;
            if (xxx) flip_exception();
        }
    }
    asm { movea.l (sp)+, a5 }
    return;
}

void remove_ticker(void)
{
    VRemove((QElemPtr)&VBL_control_block);
}

void add_ticker(void)
{
    VBL_control_block.qType = vType;
    VBL_control_block.vblAddr = deal_with_tick;
    VBL_control_block.vblCount = 1;
    VBL_control_block.vblPhase = 0;
    VInstall((QElemPtr)&VBL_control_block);
}

#else /* USE_VBL */

typedef struct xTMTask
{
    struct TMTask a;
    long int a5save;
} xTMTask;

static pascal void do_tick(void)
{
    QElemPtr w;
    long int savea5;
    asm { move.l a5, savea5
          move.l offsetof(xTMTask, a5save)(a1), a5
          move.l a1, w }
    if (tick_pending == 0)
    {
        if (already_in_gc) tick_on_gc_exit = YES;
        else
        {   Lisp_Object nil = C_nil;
            CSLbool xxx = NO;
            if (exception_pending()) flip_exception(), xxx = YES;
            tick_pending = YES;
            saveheaplimit = heaplimit;
            heaplimit = fringe;
            savevheaplimit = vheaplimit;
            vheaplimit = vfringe;
            savecodelimit = codelimit;
            codelimit = codefringe;
            savestacklimit = stacklimit;
            stacklimit = stackbase;
            if (xxx) flip_exception();
        }
    }
    PrimeTime(w, 1000);
    asm { move.l savea5, a5 }
    return;    
}

static xTMTask ticker;

void remove_ticker(void)
{
    RmvTime((QElemPtr)&ticker);
}

void add_ticker()
{
    ticker.a.tmAddr = do_tick;
    ticker.a.tmCount = 0;
    ticker.a.tmWakeUp = 0;
    ticker.a.tmReserved = 0;
    asm { move.l a5,ticker.a5save }
    InsTime((QElemPtr) &ticker);
    PrimeTime((QElemPtr) &ticker, 1000);
}

#endif /* USE_VBL */

#endif /* TICK_STREAM */

/* end of sysmac.c */

