/* Copyright ) Darin Johnson, 1989 */
/*                                 */
/* Permission is granted to use    */
/* this program and to freely copy */
/* it and/or source code as long   */
/* as these notices remain.        */
/* No charges for these copies may */
/* be made, except for handling    */
/* and distribution fees.          */

/* This program puts customized menus into the WorkBench menubar.      */
/* The program is split up into two parts, MyMenu and MyMenu-Handler.  */
/* This section is MyMenu and it initializes and starts up a process   */
/* to run MyMenu-Handler, and then terminates.  MyMenu also terminates */
/* and cleans up after the suprocess.  MyMenu will parse the users     */
/* menus, initialize data to be passed to MyMenu-Handler, and start    */
/* up MyMenu-Handler.  MyMenu-Handler does very little memory          */
/* allocation in order to save space, so MyMenu does allocation and    */
/* freeing for it.                                                     */

#include <exec/types.h>
#include <exec/memory.h>
#include <graphics/gfxbase.h>
#include <intuition/intuitionbase.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <functions.h>
#include <stdio.h>
#include <ctype.h>
#include "mymenu.h"

static char *copyright = "Copyright ) Darin Johnson, 1989";

/* actually, I don't know if it will work under Lattice */
#ifdef AZTEC_C
#define strcmp _BUILTIN_strcmp
#define strcpy _BUILTIN_strcpy
#define strlen _BUILTIN_strlen
#endif

struct IntuitionBase *IntuitionBase = NULL;
struct GfxBase *GfxBase = NULL;

extern int menu_num;

/* this is the data structure that we use to pass data to MyMenu-Handler */
struct MMData *MM;

char do_quit;
int i;

/* make (and allocate) a copy of the passed string */
char *copystr(str)
char *str;
{
  char *newstr;
  newstr = AllocMem(strlen(str)+1, MEMF_PUBLIC);
  strcpy(newstr, str);
  return newstr;
}

/* strcmp, ignoring case */
#define UPPER(c) (islower(c)?toupper(c):(c))
int stricmp(s, t)
  char *s, *t;
{
  for ( ; UPPER(*s) == UPPER(*t); s++, t++)
    if (*s== NULL)
      return 0;
  return UPPER(*s) - UPPER(*t);
}

/* Find the workbench window - I'm sure there's a better way... */
struct Window *find_workbench() {
  struct Screen *scr;
  struct Window *win;
  ULONG ilock;
  ilock = LockIBase(0L);	/* just so's things don't change on us */
  for (scr = IntuitionBase->FirstScreen;	/* find WB Screen */
       scr && strcmp(scr->DefaultTitle, "Workbench Screen");
       scr=scr->NextScreen);
  if (!scr) {
    UnlockIBase(ilock);
    printf("That's odd...  I can't find the workbench screen...\n");
    _abort(2000);
  }
  for (win=scr->FirstWindow; win; win = win->NextWindow)
    if (win->Flags & WBENCHWINDOW) break;      
  UnlockIBase(ilock);
  return win;
}

/* open a library */
APTR *open_lib(name, rev)
char *name;
long rev;
{
  APTR *lib;
  if ((lib = (APTR*)OpenLibrary(name, rev)) == NULL) {
    printf("Can't open %s (rev=%d\n", name, rev);
    _abort(1000);
  }
  return lib;
}

/* make a copy of our CLI path.  We also convert BCPL stuff. */
copy_path() {
  register struct Path *oldpath, *newpath, *tmppath;
  struct CommandLineInterface *cli;
  struct Process *pr;
  
  pr = (struct Process *)FindTask(NULL);
  cli = (struct CommandLineInterface *)BADDR(pr->pr_CLI);
  newpath = MM->CLI_path = NULL;
  for (oldpath = (struct Path *)BADDR(cli->cli_CommandDir); oldpath;
              oldpath = (struct Path *) BADDR(oldpath->path_Next)) {
    tmppath = (struct Path *)AllocMem(sizeof(struct Path), MEMF_PUBLIC);
    if (!MM->CLI_path) {
      MM->CLI_path = tmppath;
    } else {
      newpath->path_Next = tmppath;	/* we don't convert to BCPL */
    }
    newpath = tmppath;
    tmppath->path_Next = NULL;
    tmppath->path_Lock = DupLock(oldpath->path_Lock);
  }
}

/* free up the space used by the copy of the CLI path */
free_path() {
  register struct Path *tmp, *path;
      /* clean out copy of path */
  path = MM->CLI_path;
  MM->CLI_path = NULL;
  while (path) {
    tmp = path;
    path = path->path_Next;
    UnLock(tmp->path_Lock);
    FreeMem(tmp, sizeof(struct Path));
  }
}

/* Initialize and load up the handler process, or update the menus */
/* for an existing handler.                                        */
add_handler() {
  ULONG ilock;

  if (MM) {
      /* remove old menus and update */
    printf("Updating menus -- ");
    fflush(stdout);
      /* coordinate with handler */
    MM->parent_task = FindTask(NULL);
    MM->parent_sig = AllocSignal(-1L);
    Signal(MM->handler_task, SIGBREAKF_CTRL_D);
    Wait(1 << MM->parent_sig);
      /* clean old stuff up */
    free_menus();
    ilock = LockIBase(0L);
    menu_num = 0;
    for (MM->prev_menu = MM->WBWindow->MenuStrip;
	MM->prev_menu->NextMenu;
	MM->prev_menu=MM->prev_menu->NextMenu)
      menu_num++;
    UnlockIBase(ilock);

      /* get new menus */
    if (!parse_menus()) {
      do_quit = TRUE;
      return;
    }

      /* let handler know it can continue */
    Signal(MM->handler_task, SIGBREAKF_CTRL_D);
    printf("ok\n");
    return;
  }

    /* create a new handler */  
  printf("Installing MyMenu -- ");
  fflush(stdout);

    /* get area to pass data in */
  MM = (struct MMData *)AllocMem(sizeof(struct MMData), MEMF_PUBLIC|MEMF_CLEAR);

  MM->WBWindow = find_workbench();
  if (MM->WBWindow==NULL) {
    printf("Can't find Workbench...\n");
    do_quit = TRUE;
    return;
  }

    /* we need to find out what menu number ours start at */
  ilock = LockIBase(0L);
  menu_num = 0;
  for (MM->prev_menu = MM->WBWindow->MenuStrip;
	MM->prev_menu->NextMenu;
	MM->prev_menu=MM->prev_menu->NextMenu)
    menu_num++;
  UnlockIBase(ilock);  

    /* read in user's menus */
  if (!parse_menus()) {
    do_quit = TRUE;
    return;
  }

  MM->port.mp_Flags = PA_IGNORE;	/* we'll get msg's from do_wbrun */
  MM->port.mp_Node.ln_Pri = 0;
  MM->port.mp_Node.ln_Type = NT_MSGPORT;
  MM->port.mp_Node.ln_Name = copystr(MYMENU_NAME);
  NewList(&MM->port.mp_MsgList);

    /* load in handler */
  MM->segment = LoadSeg("l:MyMenu-Handler");
  if (!MM->segment)
    MM->segment = LoadSeg("MyMenu-Handler");
  if (!MM->segment) {
    printf("Can't find L:MyMenu-Handler\n");
    do_quit = TRUE;
    return;
  }

    /* add to port list so that handler can find it */
  AddPort(&MM->port);

  MM->parent_task = FindTask(NULL);
  MM->parent_sig = AllocSignal(-1L);
  copy_path();
  CreateProc(MYMENU_NAME, 0, MM->segment, STACK);
    /* handshake so that we know it got started ok */
  Wait(1 << MM->parent_sig);
  FreeSignal(MM->parent_sig);
  if (MM->error_code) {
    printf("Handler error (%d)\n", MM->error_code);
    do_quit = TRUE;
  } else {
    printf("ok - %s\n", VERSION);
  }
}

/* shutdown and clean up after handler */
remove_handler() {
  if (!MM)
    return;
  printf("removing -- ");
  fflush(stdout);
  if (MM->segment != NULL) {
    MM->parent_task = FindTask(NULL);
    MM->parent_sig = AllocSignal(-1L);
    Signal(MM->handler_task, SIGBREAKF_CTRL_C);

      /* wait until handler cleans up */
      /* (don't advertise that we catch ^C - for emergency use only) */
    Wait(1 << MM->parent_sig);
    FreeSignal(MM->parent_sig);
    if (MM->error_code != ERR_OK) {
      /* we can get an error if there are outstanding WB processes */
      printf("Leaving handler in place...\n");
      return;
    }
    RemPort(&MM->port);
    UnLoadSeg(MM->segment);
  }
    /* this is done here, not in handler... */
  free_path();
  free_menus();
  if (MM->port.mp_Node.ln_Name)
    FreeMem(MM->port.mp_Node.ln_Name, strlen(MM->port.mp_Node.ln_Name)+1);
  FreeMem(MM, sizeof(struct MMData));
  printf("done\n");
}

/* Close up libraries and exit.  Having a routine named _abort() is */
/* handy for use with SDB, otherwise, I would have a better name.   */
_abort(st)
  int st;
{
  if (IntuitionBase)
    CloseLibrary(IntuitionBase);
  if (GfxBase)
    CloseLibrary(GfxBase);
  exit(st);
}

main(argc, argv)
  int argc;
  char *argv[];
{
  IntuitionBase = (struct IntuitionBase *)open_lib("intuition.library", 0L);
  GfxBase = (struct GfxBase *)open_lib("graphics.library", 0L);
  
  do_quit=FALSE;
  for (i=1; i<argc; i++) {
    if (stricmp(argv[i], "QUIT")==0) {
      do_quit=TRUE;
      continue;
    }
      /* no other options yet */
    printf("Usage: MyMenu [quit]\n");
    _abort(10);
  }

    /* find shared data (if it exists) */
  MM = (struct MMData *)FindPort(MYMENU_NAME);

  if (do_quit == FALSE) {
    add_handler();
  };
  if (do_quit==TRUE) {		/* note that this isn't an 'else' */
      remove_handler();
  }
  _abort(0);
}
