/*  sysstmac.c                       Copyright (C) 1989-99 Codemist Ltd */

/*
 * Macintosh system-specific stuff
 */

/* Signature: 78d97b29 18-Apr-1999 */

#include "machine.h"

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

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

#include <unix.h>
#include <files.h>
#include <unix.h>
#include <retrace.h>
#include <timer.h>
#include <ansi_private.h>
#include "macwin.h"
#include <Dialogs.h>

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

int get_home_directory(char *b, int length);
int 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(1+strlen(filename));
    if (w == NULL) return w;
    strcpy(w, filename);
    return w;
}

bool file_exists(char *filename, char *old, size_t n, char *tt)
{
        FileParam pb;
        char buf[FILENAME_MAX];
        
        process_file_name(filename, old, n);
        pb.ioNamePtr = __c2p((const char*)filename, buf);
        pb.ioFRefNum = 0;
        pb.ioFVersNum = 0;
        pb.ioFDirIndex = 0;
        if (PBGetFInfo((ParmBlkPtr)&pb, 0) != noErr) return NO;
        strcpy(tt, ctime(&(pb.ioFlMdDat)));
        return YES;
}
        
char *my_getenv(char *s)
{
    return getenv(s);
}

int
myexec(char *filename, ...)
{
	int				i;
	short			ans;
	EventRecord		event;
#if 1 /* __powerc */
	FSSpec fsp;
	LaunchParamBlockRec lpb;
	
	C2PStr(filename);
	ans = FSMakeFSSpec( 0,0,(unsigned char*)filename,&fsp);
	P2CStr((unsigned char*)filename);
	if (!ans) {
		lpb.launchBlockID = extendedBlock;
		lpb.launchEPBLength = extendedBlockLen;
		lpb.launchFileFlags = 0;
		lpb.launchControlFlags = launchContinue + launchNoFileFlags;
		lpb.launchAppSpec = &fsp;
		lpb.launchAppParameters = 0;
		ans = LaunchApplication(&lpb);
	}
#else
	static struct { 
		unsigned char *name;
	    short 			memuse;
	    char    		lc[2];
	    long 			extBlocLen;
	    short			fFlags;
	    long 			launchFlags;
	} buf;
	unsigned char pname[255];
	extern void _Launch();

	buf.name = __c2p(filename, (char *)pname);
	buf.memuse = 0;
	buf.lc[0] = 'L';  buf.lc[1] = 'C';
	buf.extBlocLen = 6;
	buf.fFlags = 128;	/* RO */
	buf.launchFlags = 0xC0000000;
	asm {
		lea		buf,a0
		dc.w	_Launch
		move.w	d0,ans;
	}
#endif
	if (ans==0)
	  for (i=0; i<20; i++) WaitNextEvent(everyEvent, &event, 10, 0L);
	return ans;
}

int my_system(char *s)
{
    return myexec(s);
}

void my_pclose(FILE *a)
{
    return;
}

int create_directory(char *filename, char *old, size_t n)
{
    process_file_name(filename, old, n);
    if (*filename==0) return 1;
    return 1;	/* Unfinished */    
}

int delete_file(char *filename, char *old, size_t n)
{
    process_file_name(filename, old, n);
    if (*filename==0) return 1;
    return remove(filename);
}

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

static int 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 */
    return strlen(b);
}

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

/*static char CurApName[64];*/

/*
 * 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", LMGetCurApName()); /* name of current application */
    strcpy(program_name, image);
    i = strlen(image);
    strcpy(image+i, " Image");
    i += 6;
    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 dir 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(1+strlen(&pgmname[p]));
/*
 * The error exit here seem unsatisfactory...
 */
    if (w == NULL)
    {   fprintf(stderr, "\n+++ Panic - run out of space\n");
        exit(EXIT_FAILURE);
    }
    strcpy(w, &pgmname[p]);
    return w;
}

int truncate_file(FILE *f, long int where)
{
        short a;
        short ff = f->refnum;
    if (fflush(f) != 0) return 1;
    a = SetEOF(ff, where);  /* Returns zero if successs */
    return (int)a;
}

static bool ticker_active = NO;
int32 software_ticks = INITIAL_SOFTWARE_TICKS;
int32 software_tick_count = 0, prev_software_tick_count = 0;

void add_ticker(void)
{
    if (ticker_active) return;
    if (countdown<0) countdown = SOFTWARE_TICKS; /* Reset if expired */
    ticker_active = YES;
}

void remove_ticker(void)
{
  /*    if (!ticker_active) return; */
    ticker_active = NO;
}

int file_readable(char *filename, char *old, size_t n)
{
    FILE *fp;
    process_file_name(filename, old, n);
    if (*filename == 0) return 0;
    /* The "correct" way to do this is via stat, but this is much simpler! */
    fp = fopen(filename,"r");
    if (fp == NULL) return 0;
    else 
    {   fclose(fp);
        return 1;
    }
}

int file_writeable(char *filename, char *old, size_t n)
{
    FILE *fp;
    process_file_name(filename, old, n);
    if (*filename == 0) return 0;
    fp = fopen(filename,"a");
    if (fp == NULL) return 0;
    else 
    {   fclose(fp);
        return 1;
    }
}

int rename_file(char *from_name, char *from_old, size_t from_size,
                char *to_name, char *to_old, size_t to_size)
{
    process_file_name(from_name, from_old, from_size);
    process_file_name(to_name, to_old, to_size);
    if (*from_name == 0 || *to_name == 0) return 0;
    return rename(from_name,to_name);
}

int change_directory(char *filename, char *old, size_t n)
{
	WDPBRec P;
	Str255 pname;
	process_file_name(filename, old, n);
	if (*filename==0) return 1;
	strcpy((char*)pname, filename);
	C2PStr((char*)pname);
	P.ioNamePtr = pname;
	return PBHSetVol(&P, 0);
}

int directoryp(char *filename, char *old, size_t n)
{
	struct stat statrec;
	process_file_name(filename, old, n);
	stat(filename, &statrec);
	return S_ISDIR(statrec.st_mode);
} 

int get_current_directory(char *s, int n)
{
	return 0;
}

#include "scandir.c"

void list_directory_members(char *filename, char *old,
                            size_t n, directory_callback *fn)
{
    process_file_name(filename, old, n);
    scan_files(filename, fn);
}

/* ************************************************************ */
/* ********** StdWin Interface ******************************** */
/* ************************************************************ */
/* ************************************************************ */

#include "stdwin.h"
#include "tools.h"
#include "editwin.h"
#include "text.h"               /* Rather a cheat as I need to look
                                 * low down in here */

static WINDOW *CSL_window;
static EDITWIN *CSL_ew;
static EDITWIN *help_ew;
int max_nlines = 250;

#ifndef HELP_DATA
#define HELP_DATA "help.data"
#endif
#ifndef HELP_INDEX
#define HELP_INDEX "help.index"
#endif

static MENU *adminmenu;
static MENU *editmenu;
static MENU *loadmenu;
static MENU *libmenu;
static MENU *switchmenu;
static MENU *helpmenu;
/* static MENU *fontmenu; */
/* static MENU *sizemenu; */

#define ADMIN_MENU (1001)
#define MMFile    0
#define MMSaveas  1
#define MMHelp    2 /* main window menu items */
#define MMHelpSel 3
#define MMHelpAsk 4
#define MMInt     5
#define MMAbort   6
#define MMPause   7
#define MMDummy   8
#define MMTime    9
#define MMSpace   10
#define MMOTime   11

#define EDIT_MENU (1002)
#define MMCut     0
#define MMCopy    1
#define MMPaste   2
#define MMClear   3
#define MMSelAll  4

#define HELP_LEN (36)
#define HELP_MENU (1003)
static struct hlp {
  char name[HELP_LEN];
  long offset;
  int  length;
} *hlp;
int help_length;
int helpstamp;

#define LOAD_MENU (1004)

static char *loads[] = { 
  (char*)NULL};


#define LIB_MENU (1005)

static char *libraries[] = {
  (char*)NULL};

#define SWITCH_MENU (1006)
static struct sw {
  char *name;
  int state;
} switches[] = {
  {(char*)NULL, 0}};

/* #define FONT_MENU (1007) */
/* static char *fonts[] = { "Monaco", "Courier", NULL}; */
/* static current_font = 0; */

/* #define SIZE_MENU (1008) */
/* static char *sizes[] = { "8", "9", "10", "11", "12", NULL}; */
/* static current_size = 1; */

 get_full_name(char *name, FSSpec *spec)
{
  char nn[LONGEST_LEGAL_FILENAME];
  char mm[LONGEST_LEGAL_FILENAME];
  char *w;
  int i, r;
  int p = LONGEST_LEGAL_FILENAME;
  long id;
  CInfoPBRec pb2;

  nn[--p] = 0;
  i = spec->name[0];
  p -= i;
  memcpy(&nn[p], &(spec->name[1]), i); /* Copy base name */
  id = spec->parID;
  while (id != 0) {
    pb2.dirInfo.ioFDirIndex = -1; /* use dirID, not file name/index */
    pb2.dirInfo.ioVRefNum = spec->vRefNum;  /* look in this volume  */
    pb2.dirInfo.ioDrDirID = id;             /* get info about this directory */
    pb2.dirInfo.ioNamePtr = (unsigned char *)mm;
    if (PBGetCatInfoSync(&pb2)) break; /* Read catalogue - find name & parent*/
                                /* Fail at top of tree? */
    id = pb2.dirInfo.ioDrParID; /* go on up to parent directory          */
    i = mm[0] & 0xff;           /* length of name of this directory      */
    nn[--p] = ':';
    p -= i;
    if (p < 10) {
      fprintf(stderr, "\nCannot access file so far from root\n");
      strcpy(name, "");
      return FALSE;
    }
    memcpy(&nn[p], &mm[1], i);  /* stick names together with ':'s        */
  }
  strcpy(name, &nn[p]);
  return TRUE;
}

static void menusetup(void)
{
    MENU *mp;
    int i;

    adminmenu= mp= wmenucreate(ADMIN_MENU, "Admin");
        
    wmenuadditem(mp, "Input file...", 'F');
    wmenuadditem(mp, "Saveas...", -1);
    wmenuadditem(mp, "Help Window", 'H');
    wmenuadditem(mp, "Help Selection", 'S');
    wmenuadditem(mp, "Help Topic...", -1);
    wmenuadditem(mp, "Interrupt", 'I');
    wmenuadditem(mp, "Quit", 'Q');
    wmenuadditem(mp, "Pause", 'P');
    wmenuadditem(mp, "---------", -1);
    wmenuadditem(mp, "0.00", -1);
    wmenuadditem(mp, "[GC 0]", -1);
    wmenuadditem(mp, "", -1);
    wmenuadditem(mp, "", -1);

    wmenuenable(mp, MMDummy, NO);
    wmenuenable(mp, MMTime, NO);
    wmenuenable(mp, MMSpace, NO);
    wmenuenable(mp, MMOTime, NO);

    editmenu= mp= wmenucreate(EDIT_MENU, "Edit");
    wmenuadditem(mp, "Cut", 'X');
    wmenuadditem(mp, "Copy", 'C');
    wmenuadditem(mp, "Paste", 'V');
    wmenuadditem(mp, "Clear", 'B');
    wmenuadditem(mp, "Select All", 'A');

/*         fontmenu= mp= wmenucreate(FONT_MENU, "Font"); */
/*         for (i=0; fonts[i]!= NULL; i++) wmenuadditem(mp, fonts[i], -1); */
/*         wmenucheck(mp, current_font, YES); */
        
/*         sizemenu= mp= wmenucreate(SIZE_MENU, "FontSize"); */
/*         for (i=0; sizes[i]!= NULL; i++) wmenuadditem(mp, sizes[i], -1); */
/*         wmenucheck(mp, current_size, YES); */

/*    helpmenu= mp= wmenucreate(HELP_MENU, "Help"); */
    helpmenu = mp = (MENU*)GetMenu(HELP_MENU);
    if (mp != 0)        /* This checks against problems */
    InsertMenu((MenuHandle)mp, 0); DrawMenuBar();
    {
      FILE* hlpindx = fopen(HELP_INDEX, "r");
      if (hlpindx != NULL) {
        fscanf(hlpindx, "%d\n", &helpstamp);
        fscanf(hlpindx, "%d", &help_length);
        hlp = (struct hlp*) calloc(1+help_length, sizeof(struct hlp));
        if (hlp == NULL) {
          help_length = 0;
          goto loader;          /* Insufficient memory */
        }
        for (i=0; i<help_length; i++) {
          int ch, p=0;
          fscanf(hlpindx, "%d %d ", &(hlp[i].offset), &(hlp[i].length));
          while (p<HELP_LEN-1) {
            ch = getc(hlpindx);
            if (ch == '\n') break;
            hlp[i].name[p++] = ch; /* Was toupper here */
          }
          while (ch != '\n') ch = getc(hlpindx);
          hlp[i].name[p] = '\0';
          /*wmenuadditem(mp, hlp[i].name, -1);*/
        }
        fclose(hlpindx);
      }
      else {
        help_length = 0;
        strcpy(hlp[0].name, "No help");
        hlp[0].offset = -1;
        hlp[0].length = 0;
      }
    }

loader:
    loadmenu= mp= wmenucreate(LOAD_MENU, "Load");
    for (i=0; loads[i]!= NULL; i++)
      wmenuadditem(mp, loads[i], -1);

    libmenu= mp= wmenucreate(LIB_MENU, "Library");
    for (i=0; libraries[i]!= NULL; i++)
      wmenuadditem(mp, libraries[i], -1);

    switchmenu= mp= wmenucreate(SWITCH_MENU, "Switch");
    for (i=0; switches[i].name!= NULL; i++) {
      wmenuadditem(mp, switches[i].name, -1);
      wmenucheck(mp, i, switches[i].state);
    }
}

static void fixmenus(void)
{
    bool focus;

    focus= (CSL_ew->tp->foclen != 0);
    wmenuenable(editmenu, MMCut, focus);
    wmenuenable(editmenu, MMCopy, focus);
    wmenuenable(editmenu, MMClear, focus);
    wmenuenable(adminmenu, MMHelpSel, focus);
/*  wmenuenable(editmenu, MMPaste, YES); */

    if (help_ew != NULL) {
      focus= (help_ew->tp->foclen != 0);
      if (focus) {
        wmenuenable(editmenu, MMCut, focus);
        wmenuenable(editmenu, MMCopy, focus);
        wmenuenable(editmenu, MMClear, focus);
        wmenuenable(adminmenu, MMHelpSel, focus);
      }
    }
}

#define TTYBUF_SIZE 256
static char tty_buff[TTYBUF_SIZE];
static int tty_index = 0;
static bool tty_ready = NO;
static int tty_nnl = 0;
static int tty_offset;

#define STDOUT_BUFSIZE 1024
#define LONGEST_PRINTF 120

static char stdout_buffer[STDOUT_BUFSIZE];
static char *stdout_p;
static int stdout_n;
extern int _w_font;

void start_up_window_manager(int use_wimp)
{
    int CSL_width, CSL_height;
    _w_font = 4;  /* Font family */
    menusetup();
    wsetdefwinsize(82*wcharwidth('0'), 25*wlineheight());
    CSL_ew = ewcreate(NULL);
    CSL_window = CSL_ew->win;
    wsettitle(CSL_window, "Reduce 3.7");
    fixmenus();
/*  wmenudetach(CSL_window, helpmenu); */
    wmenuenable(editmenu, MMTime, NO); /* These are not selectable */
    wmenuenable(editmenu, MMOTime, NO); /* These are not selectable */
    wmenuenable(editmenu, MMSpace, NO);
    wmenudetach(CSL_window, loadmenu); /* These are only valid when reading */
    wmenudetach(CSL_window, libmenu);
    wmenudetach(CSL_window, switchmenu);
/*  wmessage("Welcome to X-Reduce\n\n"); */
    stdout_n = 0;
    stdout_p = stdout_buffer;
    help_ew = NULL;
}

static char *help_buff;
bool
helpwindow(int index)
{
    FILE* help = fopen(HELP_DATA, "r");
    int ch;
    int width, height;
    int size = hlp[index].length;

    if (help == NULL) return FALSE;
    fscanf(help, "%d\n", &ch);
    if (ch != helpstamp) return FALSE;        /* not  same generation */
/*  fprintf(stderr, "Help on index %d offset=%d size=%d\n",
          index, hlp[index].offset, hlp[index].length); */
    fseek(help, hlp[index].offset, SEEK_SET);
    if (help_ew) {
      free(help_buff);
      tesetfocus(help_ew->tp, 0, tegetlen(help_ew->tp));
      ewreplace(help_ew, "");
    }
    else {
      help_ew = ewcreate(NULL);
      wsettitle(help_ew->win, "Help for Reduce 3.7");
    }
    help_buff = malloc(size+1);
    if (help_buff == NULL) {
      dprintf("Insufficient memory");
      wfleep();
      return FALSE;
    }
/*  fprintf(stderr, "reading %d bytes to buffer\n", size); */
    fread(help_buff, size, 1, help);
/*  fprintf(stderr, "help starts as %.100s\n", help_buff); */
    help_buff[size] = '\0';
    wmenudetach(help_ew->win, loadmenu);
    wmenudetach(help_ew->win, libmenu);
    wmenudetach(help_ew->win, switchmenu);
/*  wmenudetach(help_ew->win, adminmenu); */
    ewreplace(help_ew, help_buff);
    tesetfocus(help_ew->tp, 0, 0);
    fclose(help);
    return TRUE;
}

void help_buffer(char *buf, int len)
{
    int i;
    for (i=0; i<help_length; i++) {
      /* fprintf(stderr, "Check against %s\n", hlp[i].name); */
      if (strncmp(hlp[i].name, buf, len) == 0) {
        helpwindow(i);
        break;
      }
    }
    if (i == help_length) wfleep();
}

void help_selection(EDITWIN *ew)
{
    int i;
    char *text;
    TEXTEDIT *tp = ew->tp;
    if (tp->foclen == 0) {
      wfleep();
      return;
    }
    text= tegettext(tp);
    help_buffer(text+tp->foc, tp->foclen);
}

extern int pause_set = 0;
void event_loop(void)
{
    for (;;) {                    /* Loop while there are events */
      EVENT e;
      EDITWIN *ew;
      bool closed;
                
      wgetevent(&e);
    /*    fprintf(stderr, "Event: type=%d win=%p data=%d\n", e.type, e.window, e.u.command);  */
      ew= ewfind(e.window);
      if (ew == CSL_ew) {
        switch (e.type) {
        case WE_CHAR:
          switch(e.u.character) {
          case 'A'-'@':         /* ^A == beginning of line */
            {
              TEXTEDIT* tp = CSL_ew->tp;
              tesetfocus(tp,
                         zsubgap(tp->start[tewhichline(tp,
                                                       zaddgap(tp->foc),
                                                       tp->focprev)]),
                         -1);
              break;
            }
          case 'B'-'@':         /* ^B back a character */
            tearrow(CSL_ew->tp, WC_LEFT);
            break;
          case 127:             /* Delete character */
          case 'D'-'@':         /* ^D delete at cursor */
            {
              TEXTEDIT* tp = CSL_ew->tp;
              tearrow(tp, WC_RIGHT);
              {
                int f1 = tp->foc;
                int i;
                int ss = tp->start[tp->nlines-tty_nnl];
                int ee = ss + tty_offset;
                if (ss <= f1 && f1 < ee) {
                  if (tty_offset>0) tty_offset--;
                }
                e.type = WE_COMMAND;
                e.u.command = WC_BACKSPACE;
                ewevent(ew,&e, NULL);
                break;
              }
            }
          case 'E'-'@':         /* ^E end of line */
            {
              TEXTEDIT* tp = CSL_ew->tp;
              int line = tewhichline(tp, zaddgap(tp->foc), tp->focprev);
              int xxx = zsubgap(tetextround(tp, line, tp->right));
              tesetfocus(tp, xxx, -1);
              break;
            }
          case 'F'-'@':         /* ^F forward a character */
            tearrow(CSL_ew->tp, WC_RIGHT);
            break;
          case 'K'-'@':         /* ^K kill to end of line */
            {                   /* This does not take notice of offset */
              TEXTEDIT* tp = CSL_ew->tp;
              int f1;
              int i;
              int ss;
              int ee;
              tesetfocus(tp,
                         tp->foc,
                         zsubgap(tetextround(tp,
                                             tewhichline(tp, zaddgap(tp->foc),
                                                             tp->focprev),
                                             tp->right)));
              f1 = tp->foc;
              ss = tp->start[tp->nlines-tty_nnl];
              ee = ss + tty_offset;
              for (i=0; i<tp->foclen; i++)
                if (ss <= f1+i && f1+i < ee) {
                  if (tty_offset>0) tty_offset--;
                }
              ewreplace(ew, "");
              break;
            }
          case 'N'-'@':         /* ^N next line */
            tearrow(CSL_ew->tp, WC_DOWN);
            break;
          case 'P'-'@':         /* ^P previous line */
            tearrow(CSL_ew->tp, WC_UP);
            break;
          case 'Z'-'@':         /* ^Z move to end of buffer */
            {
              TEXTEDIT* tp = CSL_ew->tp;
              tesetfocus(tp, tp->buflen - tp->gaplen, -1);
              break;
            }
          default:
            ewevent(ew, &e, &closed);
            if (tty_ready == YES && !pause_set) return;
          }
          break;
        case WE_COMMAND:
          switch (e.u.command) {
          case WC_CANCEL:
            interrupt_pending = YES;
            break;
          case WC_CLOSE:
            pause_for_user();
            exit(1);
          case WC_RETURN:
            {
              TEXTEDIT* tp = CSL_ew->tp;
	      int f1 = zaddgap(tp->foc);
              int ee = tp->start[tp->nlines-tty_nnl] + tty_offset;
/* printf("f1=%d ee=%d offset=%d\n", f1, ee, tty_offset); */
	      if (f1 >= ee) {
                tty_nnl++;
		if (f1 == tp->buflen) {
                  ewevent(ew, &e, &closed);
                  tty_ready = YES;
                  if (!pause_set) return;
                }
              }
            }
            break;
          case WC_DEL:
          case WC_BACKSPACE:
                                /* Need to check to see if we are deleting
                                 * in the offset area */
            {
              TEXTEDIT* tp = CSL_ew->tp;
              int f1 = tp->foc;
              int i;
              int ss = tp->start[tp->nlines-tty_nnl];
              int ee = ss + tty_offset;
	      if (tp->foclen == 0) {
		if (ss < f1 && f1 <= ee && tty_offset>0) tty_offset--;
	      }
	      else {
                for (i=0; i<tp->foclen; i++) {
		  if (ss <= f1+i && f1+i < ee && tty_offset>0) tty_offset--;
		}
	      }
            }
            break;
          case WC_LEFT:
          case WC_RIGHT:
          default:
            break;
          }
          ewevent(ew, &e, &closed);
          break;
        case WE_MENU:
          switch (e.u.m.id) {
          case ADMIN_MENU:
            switch (e.u.m.item) {
            case MMFile:
              {
                short wd;
                TEXTEDIT *tp = CSL_ew->tp;
                struct StandardFileReply sfr;
                StandardGetFile(NULL, -1, NULL, &sfr);
                if (sfr.sfGood && !sfr.sfIsFolder && !sfr.sfIsVolume) {
                  char name[256];
                  if (!get_full_name(name, &(sfr.sfFile))) break;
                  tesetfocus(tp, tp->buflen - tp->gaplen, -1);
                  ewreplace(CSL_ew, "in \"");
                  ewreplace(CSL_ew, name);
                  ewreplace(CSL_ew, "\"");
                  /*tetoend(tp); */
                }
                break;
              }
            case MMSaveas:
              ewsaveas(CSL_ew);
              break;
            case MMHelp:
              helpwindow(0);
              break;
            case MMHelpSel:
              help_selection(ew);
              break;
          case MMHelpAsk:
            {
              char buf[40];
              int len;
              if (waskstr("Help on topic", buf, 40)) {
                len = strlen(buf);
                help_buffer(buf, len);
              }
            }
            break;
            case MMAbort:
              pause_for_user();
              exit(1);
            case MMInt:
              interrupt_pending = YES;
              return;
            case MMPause:
              pause_set = !pause_set;
              wmenucheck(adminmenu, MMPause, pause_set);
              if (pause_set) wsettitle(CSL_window, "<Paused> Reduce 3.7");
              else           wsettitle(CSL_window, "Reduce 3.7");
              break;
            }
            break;
          case EDIT_MENU:
            switch (e.u.m.item) {
            case MMCopy:
              ewcopy(ew);
              break;
            case MMPaste:
              {
                char* text = wgetclip();
                char* tt = text;
                int valid = YES;

                if (text == NULL) wfleep();
                else {
                  TEXTEDIT *tp = CSL_ew->tp;
                  tesetfocus(tp, tp->buflen - tp->gaplen, -1);
                  while (*tt != '\0')
                    if (*tt++=='\n') valid = NO;
                  if (valid) ewreplace(ew, text);
                  else /*wmessage("Cannot Paste text including new line"); */
                  	Alert(129, NULL);
                }
              }
              break;
            case MMCut:
              ewcopy(ew);
            case MMClear:
                                /* Need to check to see if we are deleting
                                 * in the offset area */
              {
                TEXTEDIT* tp = CSL_ew->tp;
                int f1 = tp->foc;
                int i;
                int ss = tp->start[tp->nlines-tty_nnl];
                int ee = ss + tty_offset;
                for (i=0; i<tp->foclen; i++)
                  if (ss <= f1+i && f1+i < ee) {
                    if (tty_offset>0) tty_offset--;
                  }
              }
              ewreplace(ew, "");
              break;
            case MMSelAll:
              tesetfocus(ew->tp, 0, tegetlen(ew->tp));
              break;
            }
            break;
          case LOAD_MENU:
            {
              char *load = "load_package ";
              char *tt = loads[e.u.m.item];
              TEXTEDIT *tp = CSL_ew->tp;
              tesetfocus(tp, tp->buflen - tp->gaplen, -1);
              tp->aim= UNDEF;
              tp->focprev= FALSE;
              ewreplace(CSL_ew, load);
              ewreplace(CSL_ew, tt);
              tty_ready = YES;
              ewreplace(CSL_ew, ";\n");
              tty_nnl++;
              wmenuenable(loadmenu, e.u.m.item, NO);
              return;
            }
          case LIB_MENU:
            {
              char *load = "load_package ";
              char *tt = libraries[e.u.m.item];
              TEXTEDIT *tp = CSL_ew->tp;
              tesetfocus(tp, tp->buflen - tp->gaplen, -1);
              tp->aim= UNDEF;
              tp->focprev= FALSE;
              ewreplace(CSL_ew, load);
              ewreplace(CSL_ew, tt);
              tty_ready = YES;
              ewreplace(CSL_ew, ";\n");
              tty_nnl++;
              wmenuenable(loadmenu, e.u.m.item, NO);
              return;
            }
          case SWITCH_MENU:
            {
              char *load = (switches[e.u.m.item].state ? "off " : "on ");
              char *tt = switches[e.u.m.item].name;
              TEXTEDIT *tp = CSL_ew->tp;
              tesetfocus(tp, tp->buflen - tp->gaplen, -1);
              ewreplace(CSL_ew, load);
              ewreplace(CSL_ew, tt);
              tty_ready = YES;
              ewreplace(CSL_ew, ";\n");
              tty_nnl++;
              switches[e.u.m.item].state = ~ switches[e.u.m.item].state;
              wmenucheck(switchmenu, e.u.m.item, switches[e.u.m.item].state);
              return;
            }
          case HELP_MENU:
            helpwindow(e.u.m.item);
            break;
/*       case FONT_MENU: */
/*         if (ew == NULL) { */
/*           wfleep(); */
/*           break; */
/*         } */
/*         { */
/*           int f = wsetfont(fonts[e.u.m.item]); */
/*           if (f) { */
/*             TEXTATTR attr; */
/*             extern void getinfo_(); */
/*             wgetwintextattr(CSL_window, &attr); */
/*             attr.font = f; */
/*             wsetwintextattr(CSL_window, &attr); */
/*             getfinfo_(); */
/*             wmenucheck(fontmenu, current_font, NO); */
/*             wmenucheck(fontmenu, current_font = e.u.m.item, YES); */
/*           } */
/*         } */
/*         break; */
/*       case SIZE_MENU: */
/*         if (ew == NULL) { */
/*           wfleep(); */
/*           break; */
/*         } */
/*         wsetsize(atoi(sizes[e.u.m.item])); */
/*         { */
/*           TEXTATTR attr; */
/*           extern void getinfo_(); */
/*           wgetwintextattr(CSL_window, &attr); */
/*           attr.size = atoi(sizes[e.u.m.item]); */
/*           wsetwintextattr(CSL_window, &attr); */
/*           getfinfo_(); */
/*         } */
/*         wmenucheck(sizemenu, current_size, NO); */
/*         wmenucheck(sizemenu, current_size = e.u.m.item, YES); */
/*         break; */
          } 
          break;
        case WE_CLOSE:
          pause_for_user();
          exit(1);
        default:
          ewevent(ew, &e, &closed);
          break;
        }
      }
      else if (ew == help_ew) {
        /* printf("%d %d\n", e.type, e.u.character); */
        switch (e.type) {
        case WE_MOUSE_UP:         /* Extend to a word on single click */
          if (!ew->tp->mdown) break;
          ew->tp->mdown= FALSE;
          if (e.u.where.clicks > 1) {
            teclicknew(ew->tp, e.u.where.h, e.u.where.v, FALSE, TRUE);
            help_selection(ew);
          }
          break;
        case WE_CHAR:
          switch(e.u.character) {
          case 'A'-'@':         /* ^A == beginning of line */
            {
              TEXTEDIT* tp = help_ew->tp;
              tesetfocus(tp,
                         zsubgap(tp->start[tewhichline(tp,
                                                       zaddgap(tp->foc),
                                                       tp->focprev)]),
                         -1);
              break;
            }
          case 'B'-'@':         /* ^B back a character */
            tearrow(help_ew->tp, WC_LEFT);
            break;
          case 'E'-'@':         /* ^E end of line */
            {
              TEXTEDIT* tp = help_ew->tp;
              int line = tewhichline(tp, zaddgap(tp->foc), tp->focprev);
              int xxx = zsubgap(tetextround(tp, line, tp->right));
              tesetfocus(tp, xxx, -1);
              break;
            }
          case 'F'-'@':         /* ^F forward a character */
            tearrow(help_ew->tp, WC_RIGHT);
            break;
          case 'K'-'@':         /* ^K is Page up */
            {
              int i;
              int j;
              wgetdefwinsize(&i, &j);
              j = j/wlineheight() -1;
              for (i=0; i<j; i++) tearrow(help_ew->tp, WC_UP);
              break;
          }
          case 'N'-'@':         /* ^N next line */
            tearrow(help_ew->tp, WC_DOWN);
            break;
          case 'P'-'@':         /* ^P previous line */
            tearrow(help_ew->tp, WC_UP);
            break;
          case 'Z'-'@':         /* ^Z move to end of buffer */
            {
              TEXTEDIT* tp = help_ew->tp;
              tesetfocus(tp, tp->buflen - tp->gaplen, -1);
              break;
            }
          case 'V'-'@':         /* ^V page down */
          case ' ':
          case 'L'-'@':         /* ^L is Page down */
            {
              int i;
              int j;
              wgetdefwinsize(&i, &j);
              j = j/wlineheight() -1;
              for (i=0; i<j; i++) tearrow(help_ew->tp, WC_DOWN);
              break;
          }
          case 'i':
          case 'I':
            {
              char buf[40];
              int len;
              if (waskstr("Help on topic", buf, 40)) {
                len = strlen(buf);
                help_buffer(buf, len);
              }
          break;
            }
          case 'n':
          case 'N':
            {
              extern char *strstr(const char*, const char*);
              char *p = strstr((const char*)help_buff, (const char*)"Next: ");
              int i = 0;
              if (p == NULL) {
                wfleep(); break;
              }
              p += 6;
              while (p[i]!= ',') i++;
              help_buffer(p,i);
              break;
            }
          case 'p':
          case 'P':
            {
              extern char *strstr(const char*, const char*);
              char *p = strstr((const char*)help_buff, "Prev: ");
              int i = 0;
              if (p == NULL) {
                wfleep(); break;
              }
              p += 6;
              while (p[i]!= ',') i++;
              help_buffer(p,i);
              break;
            }
          case 'q':
          case 'Q':
            help_ew->saved = TRUE; /* Fake it */
            free(help_buff);
            ewclose(help_ew);
            help_ew = 0;
            break;
          case 'u':
          case 'U':
            {
              extern char *strstr(const char*, const char*);
              char *p = strstr((const char*)help_buff, "Up: ");
              int i = 0;
              if (p == NULL) {
                wfleep(); break;
              }
              p += 4;
              while (p[i]!= '\n') i++;
              help_buffer(p,i);
              break;
            }
          }
          break;
        case WE_MENU:
          switch (e.u.m.id) {
          case ADMIN_MENU:
            switch (e.u.m.item) {
            case MMSaveas:
              ewsaveas(help_ew);
              break;
            case MMHelp:
              if (help_length) helpwindow(0);
              break;
            case MMHelpSel:
              help_selection(ew);
              break;
            case MMHelpAsk:
              {
                char buf[40];
                int len;
                if (waskstr("Help on topic", buf, 40)) {
                  len = strlen(buf);
                  help_buffer(buf, len);
                }
                break;
              }
            case MMAbort:
              help_ew->saved = TRUE; /* Fake it */
              free(help_buff);
              ewclose(help_ew);
              help_ew = 0;
              break;
            }
            break;
          case EDIT_MENU:
            switch (e.u.m.item) {
            case MMCopy:
              ewcopy(ew);
              break;
            case MMSelAll:
              tesetfocus(ew->tp, 0, tegetlen(ew->tp));
              break;
            }
            break;
          case HELP_MENU:
            helpwindow(e.u.m.item);
            break;
          }
          break;
        case WE_COMMAND:
          switch (e.u.command) {
          case WC_CANCEL:
            interrupt_pending=YES;
            break;
          case WC_RETURN:
            help_selection(ew);
            break;
          case WC_DEL:
          case WC_BACKSPACE:
            helpwindow(0);
            break;
          default:
            ewevent(ew, &e, &closed);
            break;
          }
          if (e.u.command != WC_CLOSE) {
            break;
          }
        case WE_CLOSE:
          help_ew->saved = TRUE; /* Fake it */
          free(help_buff);
          ewclose(help_ew);
          help_ew = 0;
          break;
        default:
          ewevent(ew, &e, &closed);
          if (closed) {
            help_ew->saved = TRUE;        /* Fake it */
            free(help_buff);
            ewclose(help_ew);
            help_ew = 0;
          }
          break;
        }
      }
      else if (ew != NULL) ewevent(ew, &e, &closed);
      else wfleep();
      fixmenus();
    }
}

void poll_for_attn(void)
{
  for (;;) {                    /* Loop while there are events */
    EVENT e;
    EDITWIN *ew;
    bool closed;

    if (!pause_set) {
        if (!wpollevent(&e)) return;
    }
    else wgetevent(&e);
    /*    fprintf(stderr, "Event: type=%d win=%p data=%d\n", e.type, e.window, e.u.command);  */
    ew= ewfind(e.window);
    if (ew == CSL_ew) {
      switch (e.type) {
      case WE_CHAR:
        switch(e.u.character) {
          case 'A'-'@':         /* ^A == beginning of line */
          {
            TEXTEDIT* tp = CSL_ew->tp;
            tesetfocus(tp,
              zsubgap(tp->start[tewhichline(tp,
                                            zaddgap(tp->foc),
                                            tp->focprev)]),
                       -1);
            break;
          }
          case 'B'-'@':         /* ^B back a character */
          tearrow(CSL_ew->tp, WC_LEFT);
          break;
          case 'D'-'@':         /* ^D delete at cursor */
          {
            TEXTEDIT* tp = CSL_ew->tp;
            tearrow(tp, WC_RIGHT);
            e.type = WE_COMMAND;
            e.u.command = WC_BACKSPACE;
            ewevent(ew,&e, NULL);
            break;
          }
          case 'E'-'@':         /* ^E end of line */
          {
            TEXTEDIT* tp = CSL_ew->tp;
            int line = tewhichline(tp, zaddgap(tp->foc), tp->focprev);
            int xxx = zsubgap(tetextround(tp, line, tp->right));
            tesetfocus(tp, xxx, -1);
            break;
          }
          case 'F'-'@':         /* ^F forward a character */
          tearrow(CSL_ew->tp, WC_RIGHT);
          break;
          case 'K'-'@':         /* ^K kill all type ahead */
          tty_index = 0;
          break;
          case 'N'-'@':         /* ^N next line */
          tearrow(CSL_ew->tp, WC_DOWN);
          break;
          case 'P'-'@':         /* ^P previous line */
          tearrow(CSL_ew->tp, WC_UP);
          break;
          case 'Z'-'@':         /* ^Z move to end of buffer */
          {
            TEXTEDIT* tp = CSL_ew->tp;
            tesetfocus(tp, tp->buflen - tp->gaplen, -1);
            break;
          }
	  case 'V'-'@':		/* ^V page down */
	  case ' ':
	    {
	      int i;
	      int j;
	      wgetdefwinsize(&i, &j);
	      j = j/wlineheight() -1;
	      for (i=0; i<j; i++) tearrow(help_ew->tp, WC_DOWN);
	      break;
	    }
        default:
          if (tty_index > TTYBUF_SIZE-2) wfleep();
          else tty_buff[tty_index++] = e.u.character;
          break;
        }
        break;
      case WE_COMMAND:
        switch (e.u.command) {
        case WC_CANCEL:
          interrupt_pending = YES;
          break;
        case WC_CLOSE:
          pause_for_user();
          exit(1);
        case WC_RETURN:
          if (tty_index > TTYBUF_SIZE-2) wfleep();
          else tty_buff[tty_index++] = '\n';
          break;
        case WC_TAB:
          if (tty_index > TTYBUF_SIZE-2) wfleep();
          else tty_buff[tty_index++] = '\t';
          break;
        case WC_DEL:
        case WC_BACKSPACE:
          if (tty_index>0) tty_index--;
          break;
        }
        break;
      case WE_MENU:
        switch (e.u.m.id) {
        case ADMIN_MENU:
          switch (e.u.m.item) {
          case MMHelp:
            helpwindow(0);
            break;
          case MMHelpSel:
            help_selection(ew);
            break;
          case MMAbort:
            pause_for_user();
            exit(1);
          case MMInt:
            interrupt_pending = YES;
            return;
          case MMPause:
            pause_set = !pause_set;
            wmenucheck(adminmenu, MMPause, pause_set);
            if (pause_set) wsettitle(CSL_window, "<Paused> Reduce 3.7");
            else           wsettitle(CSL_window, "Reduce 3.7");
            break;
          }
          break;
        case EDIT_MENU:
          switch (e.u.m.item) {
          case MMCopy:
            ewcopy(ew);
            break;
          case MMPaste:
            break;
          case MMCut:
            ewcopy(ew);
          case MMClear:
            ewreplace(ew, "");
            break;
          case MMSelAll:
	      tesetfocus(ew->tp, 0, tegetlen(ew->tp));
            break;
          }
          break;
        case HELP_MENU:
          helpwindow(e.u.m.item);
          break;
        }
        break;
      case WE_CLOSE:
        pause_for_user();
        exit(1);
      default:
        ewevent(ew, &e, &closed);
        break;
      }
    }
    else if (ew == help_ew) {
      switch (e.type) {
      case WE_MOUSE_UP:         /* Extend to a word on single click */
        if (!ew->tp->mdown)
          break;
        ew->tp->mdown= FALSE;
          if (e.u.where.clicks > 1) {
            teclicknew(ew->tp, e.u.where.h, e.u.where.v, FALSE, TRUE);
            help_selection(ew);
          }
        break;
      case WE_CHAR:
/* 	  fprintf(stderr, "WE_CHAR %d(%c)\n", e.u.character,e.u.character); */
        switch(e.u.character) {
          case 'A'-'@':         /* ^A == beginning of line */
          {
            TEXTEDIT* tp = help_ew->tp;
            tesetfocus(tp,
              zsubgap(tp->start[tewhichline(tp,
                                            zaddgap(tp->foc),
                                            tp->focprev)]),
                       -1);
            break;
          }
          case 'B'-'@':         /* ^B back a character */
          tearrow(help_ew->tp, WC_LEFT);
          break;
          case 'E'-'@':         /* ^E end of line */
          {
            TEXTEDIT* tp = help_ew->tp;
            int line = tewhichline(tp, zaddgap(tp->foc), tp->focprev);
            int xxx = zsubgap(tetextround(tp, line, tp->right));
            tesetfocus(tp, xxx, -1);
            break;
          }
          case 'F'-'@':         /* ^F forward a character */
          tearrow(help_ew->tp, WC_RIGHT);
          break;
          case 'N'-'@':         /* ^N next line */
          tearrow(help_ew->tp, WC_DOWN);
          break;
          case 'P'-'@':         /* ^P previous line */
          tearrow(help_ew->tp, WC_UP);
          break;
          case 'Z'-'@':         /* ^Z move to end of buffer */
          {
            TEXTEDIT* tp = help_ew->tp;
            tesetfocus(tp, tp->buflen - tp->gaplen, -1);
            break;
          }
          case 'L'-'@':         /* ^L is PgDn */
          case 'V'-'@':         /* ^V page down */
          case ' ':
            {
              int i;
              int j;
              wgetdefwinsize(&i, &j);
              j = j/wlineheight() -1;
              for (i=0; i<j; i++) tearrow(help_ew->tp, WC_DOWN);
              break;
            }
          case 'K'-'@':         /* ^K is PgUp */
            {
              int i;
              int j;
              wgetdefwinsize(&i, &j);
              j = j/wlineheight() -1;
              for (i=0; i<j; i++) tearrow(help_ew->tp, WC_UP);
              break;
            }
        }
        break;
      case WE_MENU:
        switch (e.u.m.id) {
        case ADMIN_MENU:
          switch (e.u.m.item) {
          case MMHelp:
            if (help_length) helpwindow(0);
            break;
          case MMHelpSel:
            help_selection(ew);
            break;
          }
          break;
        case EDIT_MENU:
          switch (e.u.m.item) {
          case MMCopy:
            ewcopy(ew);
            break;
          case MMSelAll:
            tesetfocus(ew->tp, 0,
                       tegetlen(ew->tp));
            break;
          }
          break;
        case HELP_MENU:
          helpwindow(e.u.m.item);
          break;
        }
        break;
      case WE_COMMAND:
        switch (e.u.command) {
        case WC_CANCEL:
          interrupt_pending = YES;
          break;
        case WC_RETURN:
          help_selection(ew);
          break;
        case WC_DEL:
        case WC_BACKSPACE:
          helpwindow(0);
          break;
        default:
          ewevent(ew, &e, &closed);
          break;
        }
        if (e.u.command != WC_CLOSE) {
          break;
        }
      case WE_CLOSE:
        help_ew->saved = TRUE;  /* Fake it */
          free(help_buff);
        ewclose(help_ew);
        help_ew = 0;
        break;
      default:
        ewevent(ew, &e, &closed);
        if (closed) {
          help_ew->saved = TRUE;        /* Fake it */
            free(help_buff);
          ewclose(help_ew);
          help_ew = 0;
        }
        break;
      }
    }
    else if (ew != NULL) ewevent(ew, &e, &closed);
    else wfleep();
    fixmenus();
  }
}

void flush_screen(void) 
{
    TEXTEDIT* tp = CSL_ew->tp;
    if (stdout_n != 0) {
    /*     fprintf(stderr, stdout_buffer);  */
      ewreplace(CSL_ew, stdout_buffer);
      stdout_n = 0; stdout_p = stdout_buffer;
    }
    if (tp->nlines > max_nlines+5) { /* Allow a five line slop */
      techangefocus(tp, 0, zsubgap(tp->start[tp->nlines-max_nlines]));
      tp->aim= UNDEF;
      tp->focprev= FALSE;
/*    ewreplace(CSL_ew, ""); */
      tereplace(tp, "");
    /*    fprintf(stderr, "-> %d\n", tp->nlines); */
    /* focus to end */
      tesetfocus(tp, tp->buflen - tp->gaplen, -1);
    }
    wupdate(CSL_window);
}

static clock_t prev_clock = 0;

void accept_tick(void)
{
    clock_t t0 = clock();
#ifdef SOFTWARE_TICKS_PER_SECOND
    software_tick_count++;
#endif
#ifdef SOFTWARE_TICKS_PER_SECOND
        if (prev_clock != 0)
        {   double t1 = (double)(t0-prev_clock)/(double)CLOCKS_PER_SEC;
            double ratio = 
               (double)(software_tick_count - prev_software_tick_count)/t1;
            int32 w;
/*
 * t1 is how long since I was last here, ratio is the number of
 * ticks per second over that time-span.
 */
            ratio = ratio / (double)SOFTWARE_TICKS_PER_SECOND;
            prev_software_tick_count = software_tick_count;
/*
 * Now ratio is the extent by which I was taking ticks too fast.
 * To dampen out my correction I will scale software_ticks by the
 * square root of this.
 */
            ratio = sqrt(ratio);
            w = (int)(1000.0 * ratio);
/*
 * I clamp the correction fator so I never adjust my clock rate by
 * a factor of more than (about) 3.
 */
            if (w > 3000) w = 3000;
            else if (w < 300) w = 300;
/*
 * Furthermore I attempt to keep software_ticks within integer range.
 */
            if (software_ticks < (0x7fffffff/3000) &&
                software_ticks > 50)
                software_ticks = (w*software_ticks)/1000;
        }
#endif
/*
 * 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...
 */
    t0 = clock();
    remove_ticker();

    if (t0 > prev_clock + CLOCKS_PER_SEC) {
/*
 * Time spent doing all of this is counted as "overhead" or "system time"
 * and not included in the times that I will report to my users...
 */
      if (clock_stack == &consolidated_time[0]) {
        consolidated_time[0] +=
          ((double)(clock_t)(t0 - base_time))/((double)CLOCKS_PER_SEC);
        base_time = clock();
      }
      report_time((int32)(100.0*consolidated_time[0]), (int32)(100.0*gc_time));
    }
    push_clock();
    wupdate(CSL_window);
    poll_for_attn();
    polltick_pending = NO;
    ensure_screen();
    add_ticker();
    pop_clock();
    return;
}

static char time_string[32], space_string[32];

void report_time(int32 t, int32 gct)
{
    sprintf(time_string, "%d.%.2d secs", t/100, t%100);
    wmenusetitem(adminmenu, MMTime, time_string);
    sprintf(time_string, "+ %d.%.2d secs", gct/100, gct%100);
    wmenusetitem(adminmenu, MMOTime, time_string);
}

void report_space(int gc_count, double percent)
{
    sprintf(space_string, "[GC %d]:%.2f%%", gc_count, percent);
    wmenusetitem(adminmenu, MMSpace, space_string);
}

#ifndef HAVE_UNIVERSAL_HEADERS
#define ModalFilterUPP ProcPtr
#define NewModalFilterProc(x) ((ProcPtr)x)
#endif
extern pascal Boolean
    filter_addnl(DialogPtr, EventRecord, short *);
void pause_for_user(void)
/*
 * This is called at the end of a run so that the user gets a chance to read
 * the final screen-full of output.  It pops up a dialog box that will
 * wait for a button push.  I take the view that if output is going to a
 * file then the delay is not needed, since the user can always check for
 * messages there. This has the effect that non-interactive build sequences
 * will often run without the pause - a good thing!  Note however that this
 * mean that you MUST use the close box to exit from a wimp session. Just
 * "quit;" or "(stop 0)" will not do.
 */
{
 	ModalFilterUPP filterupp = NewModalFilterProc(filter_addnl);
    remove_ticker();
    ensure_screen();
    CSL_ew->saved = TRUE;       /* Fake it */
    if (spool_file != NULL) return;
/*
 * Here I just loop handling events until the user hits the CLOSE box
 * on my window.
 */
/*    wmessage("Leaving Reduce 3.7"); */
	set_arrow();
	Alert(128, filterupp);
    ewclose(CSL_ew);
    wdone();
}

int wimpget(char *tty_buffer)
/*
 * This is the main call from the body of CSL into the window manager
 * to obtain input from the user.  It is expected to copy some input
 * characters into tty_buffer and return the number of characters
 * provided.  In this implementation I read the text back from the
 * stdwin textedit buffer, looking rather too low.
 *  Any type ahead is handled in here.
 */
{
    TEXTEDIT* tp = CSL_ew->tp;
    int i;
    int n;
    int lineno;
    tty_nnl = 1;                /* Offset from nlines */

    push_clock();
    remove_ticker();
    if (stdout_n != 0) flush_screen();
    wmenuattach(CSL_window, loadmenu);
    wmenuattach(CSL_window, libmenu);
    wmenuattach(CSL_window, switchmenu);
    lineno = tp->nlines-1;      /* Need to do this after the toend */
    tty_offset = tp->foc - tp->start[lineno];
    if (tty_offset<0) tty_offset += tp->gaplen;
    wmenuenable(adminmenu, MMPause, NO);
    if(tty_index>0) {
        int i = 0;
        for (; i<tty_index; i++) 
          if (tty_buff[i] == '\n') tty_nnl++;
        tty_buff[tty_index] = '\0';
        ewreplace(CSL_ew, tty_buff);
        tty_index = 0;
    }   
    event_loop();
    /*    fprintf(stderr, "Event loop ended\n");  */
    lineno = tp->nlines-tty_nnl;
    /*      fprintf(stderr, "Offset=%d Length=%d\n",
              tty_offset,
              tp->foc- tp->start[lineno]-tty_offset);
      for (i=0; i<tp->foc- tp->start[lineno]-tty_offset; i++) {
        fprintf(stderr, "%c",  *(tegettext(tp)+tp->start[lineno]+tty_offset+i));
      }
      fprintf(stderr, "\n"); */
    memcpy(tty_buffer, tegettext(tp)+tp->start[lineno]+tty_offset,
           tp->foc- tp->start[lineno]-tty_offset);
    wmenudetach(CSL_window, loadmenu);
    wmenudetach(CSL_window, libmenu);
    wmenudetach(CSL_window, switchmenu);
    wmenuenable(adminmenu, MMPause, YES);
    add_ticker();
    n = tp->foc- tp->start[lineno]-tty_offset;
    if (n<0) {
        n += tp->gaplen;
        fprintf(stderr, "N correction\n");
    }
    tty_ready = NO;
    pop_clock();
    return n;
}

int char_cnt = 0;
void putc_stdout(int c)
{
    *stdout_p++ = c;
    *stdout_p = 0;
    stdout_n++;
    char_cnt++;
    remove_ticker();
    if (c == '\n' || stdout_n > STDOUT_BUFSIZE - LONGEST_PRINTF) {
      push_clock(); flush_screen();
      if (char_cnt>200) {	/* Where 200 is a random number */
	poll_for_attn(); char_cnt = 0;
      }
      pop_clock();
    }
    if (polltick_pending) accept_tick();
    add_ticker();
}

void vfprintf_stdout(char *fmt, va_list a)
{
    int n;
/*
 * I have given myself LONGEST_PRINTF locations in the array for
 * this vsprintf to fill in.  If it overflows I am in big trouble!
 */
    n = vsprintf(stdout_p, fmt, a);
    stdout_p += n;
    stdout_n += n;
    char_cnt += n;
    remove_ticker();
    if (stdout_n > STDOUT_BUFSIZE - LONGEST_PRINTF) {
      push_clock(); flush_screen();
      if (char_cnt>200) {
	poll_for_attn(); char_cnt = 0;
      }
      pop_clock();
    }
    if (polltick_pending) accept_tick();
    add_ticker();
}

#define NARGS           25

static char *argv[NARGS+1];
static char argbuf[256];

/*
 *  init_command_line - prepare initial command line
 *
 *  The command line is preset to show the name of the program.
 *  The program name is quoted as necessary.
 *
 */

static char*
init_command_line(char *buf1, char *buf2)
{
    register char *s, *t = buf2;
    int c, space = 0, dquote = 0, squote = 0, quote = 0;
        
    sprintf(s = buf1, "%#s", LMGetCurApName());
    while (c = *s++) {
      if (c == ' ')
        space = 1;
      else if (c == '"')
        dquote = 1;
      else if (c == '\'')
        squote = 1;
    }
    if (space || dquote || squote)
      *t++ = quote = dquote && !squote ? '\'' : '"';
    for (s = buf1; c = *s++; *t++ = c) {
      if (c == quote || c == '\\')
        *t++ = '\\';
    }
    if (quote)
      *t++ = quote;
    *t++ = ' ';
    *t++ = 0;
    return t;
}


/*
 *  parse - divide command line into "words"
 *
 *  Words are delimited by one or more spaces.  Any characters within
 *  matching single (') or double (") quotes are taken literally.  Any
 *  character preceded by a backslash (\) is taken literally.
 *
 */

static int
parse(char *s, char *t)
{
    int c, quote = 0, argc = 0;
                
    while (c = *s++) {
      if (c == ' ')
        continue;
      if (argc < NARGS)
        argv[argc++] = t;
      do {
        if (c == '\\' && *s)
          c = *s++;
        else if (c == '"' || c == '\'') {
          if (!quote) {
            quote = c;
            continue;
          }
          if (c == quote) {
            quote = 0;
            continue;
          }
        }
        *t++ = c;
      } while (*s && ((c = *s++) != ' ' || quote));
      *t++ = 0;
    }
    return(argc);
}

#ifdef OLD
int wgetargs(char ***av)
{
    short i, argc;
    char buf[256];
    char *t = init_command_line(buf, argbuf);

    if( !waskstr("       Arguments for REDUCE 3.7", t, 256)) exit(1);
    t[-1] = ' ';
    sprintf(buf, "%s", argbuf);
    argc = parse(buf, argbuf);
    *av = argv;
    /*     for(i=0;i<argc; i++) wmessage(argv[i]); */
    return(argc);
}
#else

enum {		/* Dialog names for main box */
    exDEF = 3,
    exLOG,
    exVER,
    exXTA
};

enum {		/* Dialog names for additional box */
    exBAT = 3,
    exBAC,
    exTRA,
    exCLD,
    exLAP,
    exQUI,
    exREC,
    exRED,
    exRAN,
    exSIZ,
    exOTH,
    exLGG,
    exDFF,
    exVBE
};

/*
 *  get_args - process "command line"
 *
 */
 
 /* Dialog numbers */
 #define START_1 (129)
 #define START_2 (128)

/*Rect bounds = {60, 20, 320, 220};
Rect boundx = {80, 30, 400, 500};
*/

int DoExtra(char ***av)
{
    DialogPtr ee;
    short i;
    int verbos = 0;
    int deflt = 0;
    int logging = 0;
    char buff[256];
    char name[256];
    char nams[256];
    Rect box;
    short type;
    Handle item;
    char *t = init_command_line(buff, argbuf);
    char *s = t-1;
    int argc;
 	ModalFilterUPP filterupp = NewModalFilterProc(filter_addnl);
	int batch =0, back = 0, cold = 0, lap = 0, quiet = 0;
	int trap = 0, recover = 0, redirect = 0;
/*    ee = NewDialog(NULL, &boundx, "\p", 1, 1, (WindowPtr)-1, 0, 256L,
			 GetResource('DITL',132)); */
    ee = GetNewDialog(START_2, NULL, (WindowPtr)-1);
	GetDItem(ee, 1, &type, &item, &box);
	SetPort(ee);
	InsetRect(&box, -4, -4);
	PenSize(3, 3);
	FrameRoundRect(&box, 16, 16);
	set_arrow();
	do {                /*  engage in dialog  */
	  ModalDialog(filterupp, &i);
	  switch (i) {
	  case cancel:
	    DisposDialog(ee);
	    exit(0);
	  case exBAT:
	    batch = !batch;
	      break;
	    case exBAC:
	      back = !back;
	      break;
	    case exTRA:
	      trap = !trap;
	      break;
	    case exCLD:
	      cold = !cold;
	      break;
	    case exLAP:
	      lap = !lap;
	      break;
	    case exQUI:
	      quiet = !quiet;
	      break;
	    case exREC:
	      recover = !recover;
	      break;
	    case exRED:
	      redirect = waskfile("Redirection File: ", nams, 256, 1);
	      break;
	    case exLGG:
      	  logging = waskfile("Log File: ", name, 256, 1);
	      break;
        case exDFF:
	      deflt = !deflt;
      	  break;
        case exVBE:
          verbos = !verbos;
      	  break;
	    }
	  } while (i != ok);
	  if (batch) { strcpy(t, " -b"); t += 3; }
	  if (back) { strcpy(t, " -g"); t += 3; }
	  if (cold) { strcpy(t, " -z"); t += 3; }
	  if (lap) { strcpy(t, " -s"); t += 3; }
	  if (quiet) { strcpy(t, " -q"); t += 3; }
	  if (recover) { strcpy(t, " -y"); t += 3; }
	  if (trap) { strcpy(t, " -x"); t += 3; }
      if (verbos) { strcpy(t, " -v"); t += 3; }
      if (logging) { strcpy(t, " -l"); t += 3;
	    strcpy(t, name); t += strlen(name);
      }
      if (redirect) { strcpy(t, " --"); t += 3;
	    strcpy(t, nams); t += strlen(nams);
      }
	  {
	    char bb[256];
	    GetDItem(ee, exRAN, &type, &item, &box);
	    GetIText(item, (unsigned char *)bb);
#ifndef CLEVERGLUE
	    P2CStr((unsigned char *)bb);
#endif 
        if (bb[0]!='\0') {
	      strcpy(t, " -r"); t += 3;
	      strcpy(t, bb); t += strlen(bb);
	    }
	    GetDItem(ee, exSIZ, &type, &item, &box);
	    GetIText(item, (unsigned char *)bb);
#ifndef CLEVERGLUE
	    P2CStr((unsigned char *)bb);
#endif
	    if (bb[0]!='\0') {
	      strcpy(t, " -k"); t += 3;
	      strcpy(t, bb); t += strlen(bb);
	    }
	    GetDItem(ee, exOTH, &type, &item, &box);
	    GetIText(item, (unsigned char *)bb);
#ifndef CLEVERGLUE
	    P2CStr((unsigned char *)bb);
#endif
	    if (bb[0]!='\0') {
	      strcpy(t, bb); t += strlen(bb);
	    }
	  }
	out:
	  DisposDialog(ee);
    if (!deflt) *s = ' ';
    sprintf(buff, "%s", argbuf);
    argc = parse(buff, argbuf);
    *av = argv;
/*    for(i=0;i<argc; i++) wmessage(argv[i]); */
    return(argc);
}

int wgetargs(char ***av)
{
    DialogPtr dd;
    short i;
    int verbos = 0;
    int deflt = 0;
    int logging = 0;
    char buff[256];
    char name[256];
    char nams[256];
    int p = 0;
    Rect box;
    short type;
    Handle item;
    char *t = init_command_line(buff, argbuf);
    char *s = t-1;
    int argc;
 	ModalFilterUPP filterupp = NewModalFilterProc(filter_addnl);

/*    dd = NewDialog(NULL, &bounds, "\p", 1, 1, (WindowPtr)-1, 0, 256L,
                   GetResource('DITL',131)); */
    dd = GetNewDialog(START_1, NULL, (WindowPtr)-1);
	GetDItem(dd, 1, &type, &item, &box);
	SetPort(dd);
	InsetRect(&box, -4, -4);
	PenSize(3, 3);
	FrameRoundRect(&box, 16, 16);
	set_arrow();
    do {                /*  engage in dialog  */
      ModalDialog(filterupp, &i);
      switch (i) {
      case cancel:
	    DisposDialog(dd);
	    exit(0);
      case exDEF:
	    deflt = !deflt;
      	break;
      case exVER:
      	verbos = !verbos;
      	break;
      case exLOG:
      	logging = waskfile("Log File: ", name, 256, 1);
        break;
      case exXTA:
        set_watch();
      	DisposDialog(dd);
      	return DoExtra(av);
      }
    } while (i != ok);
	set_watch();
    {
      char aa[256];
      if (verbos) { 
      	strcpy(t, " -v"); 
      	t += 3; }
      if (logging) {
        strcpy(t, " -l"); t += 3;
	    strcpy(t, name); p += strlen(aa);
      }
    }
    if (!deflt) *s = ' ';
    DisposDialog(dd);
    sprintf(buff, "%s", argbuf);
    argc = parse(buff, argbuf);
    *av = argv;
/*    for(i=0;i<argc; i++) wmessage(argv[i]); */
    return(argc);
}
#endif

/* end of sysmac.c */

