[comp.sources.amiga] v89i123: mymenu - add custom menus to workbench strip

page%swap@Sun.COM (Bob Page) (05/10/89)

Submitted-by: leadsv!laic!darin@pyramid.pyramid.com (Darin Johnson)
Posting-number: Volume 89, Issue 123
Archive-name: workbench/mymenu.1

Allows you to add custom menus to the workbench menu strip to run
commonly used commands.

[binary was previously posted in comp.binaries.amiga v89i051.  ..bob]

# This is a shell archive.
# Remove anything above and including the cut line.
# Then run the rest of the file through 'sh'.
# Unpacked files will be owned by you and have default permissions.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: SHell ARchive
# Run the following text through 'sh' to create:
#	DoRun.c
#	Makefile
#	Menu.c
#	Mon.c
#	MyMenu-Handler.c
#	MyMenu.c
#	MyMenu.conf
#	MyMenu.doc
#	MyMenu.h
#	Parse.c
#	README
#	newbugs
# This is archive 1 of a 1-part kit.
# This archive created: Tue May  9 21:25:25 1989
echo "extracting DoRun.c"
sed 's/^X//' << \SHAR_EOF > DoRun.c
X/* Copyright ) Darin Johnson, 1989 */
X
X/* DoRun.c -- I met her on a Monday, and my heart stood still... */
X
X#include <exec/types.h>
X#include <exec/memory.h>
X#include <libraries/dos.h>
X#include <libraries/dosextens.h>
X#include <intuition/intuition.h>
X#include <workbench/icon.h>
X#include <workbench/startup.h>
X#include <workbench/workbench.h>
X#include <functions.h>
X#include "mymenu.h"
X
X#ifdef AZTEC_C
X#define strlen _BUILTIN_strlen
X#define strcpy _BUILTIN_strcpy
X#endif
X
X#ifdef DO_PATH
X#define CMDSIZ 256
X#define CBUFSZ 80
X#endif
X
Xextern struct Process *MyProcess;
X
X/* text to put into autorequesters */
Xstruct IntuiText err_nocmd = { 0,1,JAM2, 20,10, NULL, 
X	(UBYTE*)"MyMenu: can't execute command", NULL };
Xstruct IntuiText err_nomem = { 0,1,JAM2, 20,10, NULL, 
X	(UBYTE*)"MyMenu: out of memory!", NULL };
Xstruct IntuiText err_noinfo = { 0,1,JAM2, 20,10, NULL, 
X	(UBYTE*)"MyMenu: can't open info file", NULL };
Xstruct IntuiText err_notool = { 0,1,JAM2, 20,10, NULL, 
X	(UBYTE*)"MyMenu: not a tool or project", NULL };
Xstruct IntuiText req_neg = { 0,1,JAM2, 7,3, NULL,
X	(UBYTE*)"Aw, shucks", NULL };
X
X/* process a menu item */
Xint run_item(item)
X  struct ext_MenuItem *item;
X{
X  if (item->cmd) {
X    if (item->type == 'C') {
X      do_clirun(item->cmd, item->args);
X    }
X#ifdef DO_WB
X    else if (item->type == 'W') {
X      do_wbrun(item->cmd);
X    }
X#endif
X  }
X}
X
X#ifdef DO_PATH
Xchar *FindIt();
X#endif
X
X/* execute a CLI command */
Xdo_clirun(cmd, args)
X  char *cmd, *args;
X{
X  struct FileHandle *NilFh, *Open();
X  register char *buf;
X  register short siz;
X
X#ifdef DO_PATH
X  cmd = FindIt(cmd);
X#endif
X  if (cmd==NULL) {
X    AutoRequest(NULL, &err_nocmd, NULL, &req_neg, 0,0,300,60);
X    return;
X  }
X
X  siz = 32 + strlen(cmd);
X  if (args)
X    siz += strlen(args);
X  buf = (char *)AllocMem(siz, MEMF_PUBLIC);
X  if (buf==NULL) {
X    AutoRequest(NULL, &err_nomem, NULL, &req_neg, 0,0,300,60);
X    return;
X  }
X  strcpy(buf, "RUN <NIL: >NIL: \"");
X  strcat(buf, cmd);
X  strcat(buf, "\" <NIL: >NIL: ");
X  if (args)
X    strcat(buf, args);
X    
X  NilFh = Open("NIL:", MODE_NEWFILE);
X  if (NilFh) {
X    Execute(buf, NULL, NilFh);
X    Close(NilFh);
X  }
X  FreeMem(buf, siz);
X}
X
X/* procedures to support running WorkBench programs */
X
X#ifdef DO_WB
Xextern int wb_cnt;
Xextern struct MsgPort *wb_reply_port;
X
X/* create (and allocate) a copy of a string */
Xchar *copystr(str) {
X  char *tmpstr;
X  if (!str)
X    return NULL;
X  tmpstr = (char *)AllocMem(strlen(str)+1, MEMF_PUBLIC);
X  strcpy(tmpstr, str);
X  return tmpstr;
X}
X
X/* Free up space used by a workbench startup message.  Called whenever
X   a workbench program replies to the startup message, and whenever
X   do_wbrun() gets an error */
Xwbfree(WBStartup)
X  struct WBStartup *WBStartup;
X{
X  register BPTR lock;
X  register int i;
X  register char *cp;
X  if (WBStartup != NULL) {
X    if (WBStartup->sm_ArgList != NULL) {
X      for (i=0; i<WBStartup->sm_NumArgs; i++) {
X        if ((lock=WBStartup->sm_ArgList[i].wa_Lock) != NULL) {
X          UnLock(lock);
X	}
X	if ((cp=WBStartup->sm_ArgList[i].wa_Name) != NULL)
X	  FreeMem(cp, strlen(cp)+1);
X      }
X      FreeMem(WBStartup->sm_ArgList,
X  	sizeof(struct WBArg)*WBStartup->sm_NumArgs);
X    }
X    if (WBStartup->sm_Segment != NULL) {
X      UnLoadSeg(WBStartup->sm_Segment);
X    }
X    if ((cp=WBStartup->sm_ToolWindow) != NULL)
X      FreeMem(cp, strlen(cp)+1);
X    FreeMem(WBStartup, sizeof(struct WBStartup));
X  }
X}
X
X/* take the path passed, and split it into a lock and name,
X   suitable for putting into a WBArg structure */
Xsplit_path(path, dir_lock, name)
X  char *path, **name;
X  BPTR *dir_lock;
X{
X  register char *p;
X  register BPTR lock;
X  
X  for (p=path, *name=path; *p; p++)
X    if (*p == '/' || *p == ':')
X      *name = p+1;
X  lock = (BPTR)Lock(path, ACCESS_READ);
X  if (lock) {
X    *dir_lock = (BPTR)ParentDir(lock);
X    UnLock(lock);
X  } else {
X    *dir_lock = (BPTR)DupLock(MyProcess->pr_CurrentDir);
X  }
X}
X
X/* load and run a workbench program */
Xint do_wbrun(cmd)
X  char *cmd;
X{
X  BPTR lock, oldlock;
X  struct WBStartup *WBStartup;
X  struct DiskObject *disk_object;
X  char argc, *name, buf[128];
X  char error;
X    
X  disk_object = WBStartup = NULL;
X  lock = oldlock = NULL;
X  name = NULL;
X  error = TRUE;	/* assume the worst */
X
X    /* allocate the startup message */
X  if ((WBStartup = (struct WBStartup *)AllocMem(sizeof(struct WBStartup),
X  			MEMF_PUBLIC|MEMF_CLEAR)) == NULL) {
X    AutoRequest(NULL, &err_nomem, NULL, &req_neg, 0,0,300,60);
X    return TRUE;	/* don't go to finish:, nothing allocated yet */
X  }
X#ifdef DO_PATH
X  if ((cmd = FindIt(cmd)) == NULL)
X    goto finish;
X#endif
X
X    /* find the directory and name of the program to run */
X  split_path(cmd, &lock, &name);
X  if (lock == NULL) {
X    AutoRequest(NULL, &err_nocmd, NULL, &req_neg, 0,0,300,60);
X    goto finish;
X  }
X
X    /* try to load in the .info file */
X  oldlock = (BPTR)CurrentDir(lock);
X  if ((disk_object = GetDiskObject(name)) == NULL) {
X    AutoRequest(NULL, &err_noinfo, NULL, &req_neg, 0,0,300,60);
X    goto finish;
X  }
X
X    /* allocate the WBArgs - if we are a tool, we allocate one */
X    /* of these, else two (one for tool, one for project)      */
X  if (disk_object->do_Type == WBTOOL) {
X    if ((WBStartup->sm_ArgList = (struct WBArg *)
X	AllocMem(sizeof(struct WBArg), MEMF_PUBLIC|MEMF_CLEAR)) == NULL) {
X      AutoRequest(NULL, &err_nomem, NULL, &req_neg, 0,0,300,60);
X      goto finish;
X    }
X    WBStartup->sm_NumArgs = 1;
X  } else if (disk_object->do_Type == WBPROJECT) {
X    if ((WBStartup->sm_ArgList = (struct WBArg *)
X	AllocMem(sizeof(struct WBArg)*2, MEMF_PUBLIC|MEMF_CLEAR)) == NULL) {
X      AutoRequest(NULL, &err_nomem, NULL, &req_neg, 0,0,300,60);
X      goto finish;
X    }
X    WBStartup->sm_NumArgs = 2;
X      /* fill in arg #2 with the info we already have */
X    WBStartup->sm_ArgList[1].wa_Lock = (BPTR)lock;
X    WBStartup->sm_ArgList[1].wa_Name = copystr(name);
X      /* now find the tool */
X    strcpy(buf, disk_object->do_DefaultTool);
X    split_path(buf, &lock, &name);
X    FreeDiskObject(disk_object);
X    CurrentDir(lock);
X    if ((disk_object = GetDiskObject(name)) == NULL) {
X      AutoRequest(NULL, &err_noinfo, NULL, &req_neg, 0,0,300,60);
X      goto finish;
X    }
X      /* we have to have a tool at this point - or else */
X    if (disk_object->do_Type != WBTOOL) {
X      AutoRequest(NULL, &err_notool, NULL, &req_neg, 0,0,300,60);
X      goto finish;
X    }
X  }
X
X    /* fill in arguments */
X  WBStartup->sm_ArgList[0].wa_Lock = (BPTR)lock;
X  WBStartup->sm_ArgList[0].wa_Name = copystr(name);
X  
X    /* initialize rest of startup message */
X  WBStartup->sm_Message.mn_ReplyPort = wb_reply_port;
X  WBStartup->sm_Message.mn_Length = sizeof(struct WBStartup);
X  WBStartup->sm_Message.mn_Node.ln_Type = NT_MESSAGE;
X  WBStartup->sm_ToolWindow = copystr(disk_object->do_ToolWindow);
X
X    /* get a decent stack size, there are a few progs that set this to zero */
X  if (disk_object->do_StackSize < 4000)
X    disk_object->do_StackSize = 4000;
X
X    /* load in the program */
X  if ((WBStartup->sm_Segment = (BPTR)LoadSeg(name)) == NULL) {
X    AutoRequest(NULL, &err_nocmd, NULL, &req_neg, 0,0,300,60);
X    goto finish;
X  }
X
X   /* create process */
X  if ((WBStartup->sm_Process = (struct MsgPort *)CreateProc(name,
X	 0, WBStartup->sm_Segment, disk_object->do_StackSize)) == NULL) {
X    AutoRequest(NULL, &err_nocmd, NULL, &req_neg, 0,0,300,60);
X    goto finish;
X  }
X    /* everything's ok -- start 'er up */
X  PutMsg(WBStartup->sm_Process, WBStartup);
X  error = FALSE;
X  wb_cnt++;	/* keep track of unreplied startup messages */
Xfinish:
X  if (disk_object) FreeDiskObject(disk_object);
X  if (oldlock) CurrentDir(oldlock);
X  if (error)
X    wbfree(WBStartup);
X  return error;
X}
X#endif
X
X#ifdef DO_PATH
X/* This section is largely taken from RunBack
X  (which was largely taken from which.c by Carolyn Scheppner).
X  This handles path searching.
X*/
X
X/***** currently these routines can cause guru's
X       Run a workbench program, and then run a program that
X       uses the path searching - often causes a Guru, but can't
X       tell why.  If the wbfree() does NOT free its locks, then
X       it doesn't guru.
X*****/
X
Xstatic char sbuf[CMDSIZ];
X
X/* search for a command in our path */
Xchar *FindIt(command)
X  char *command;
X{
X  BOOL getPath();
X  struct Path *path;
X  struct FileInfoBlock *fib;
X  BPTR lock, startcd;
X  BOOL Found, InitialCD;
X
X     /* Were we given full path in command name (':' in name) ? */
X  {
X    register char *p;
X    for (p=command; *p; p++) {
X      if (*p == ':') {
X        lock = (BPTR)Lock(command,ACCESS_READ);
X	if (lock==NULL)
X	  return NULL;		/* command not found */
X	UnLock(lock);
X        return command;
X      }
X    }
X  }
X  
X    /* allocate this for Examine() calls */ 
X  fib = (struct FileInfoBlock *)
X	AllocMem(sizeof(struct FileInfoBlock), MEMF_PUBLIC|MEMF_CLEAR);
X  if (fib == NULL)
X    return NULL;
X    
X    /* Check current directory */
X  Found = getPath(command, fib, sbuf);
X
X    /* Check along paths */
X  if (!Found) {
X    InitialCD = TRUE;
X
X    for (path = MM->CLI_path; (path) && (!Found); path = path->path_Next) {
X        /* CD to each path */
X      lock = (BPTR)CurrentDir(path->path_Lock);
X      if (InitialCD) {
X        startcd = lock; InitialCD = FALSE;
X      }
X        /* See if command is there */
X      Found = getPath(command, fib, sbuf);
X    }
X      /* If we CD'd anywhere, restore initial CD */
X    if (!InitialCD) 
X      CurrentDir(startcd);
X  }
X
X    /* Check C: */
X  if (!Found) {
X    char cbuf[CBUFSZ];
X    strcpy(cbuf,"C:");
X    strcpy(&cbuf[2], command);
X    Found = getPath(cbuf, fib, sbuf);
X  }
X
X    /* Free fib */
X  if (fib != NULL)
X    FreeMem(fib, sizeof(struct FileInfoBlock));
X
X  if (Found)
X    return(sbuf);
X  else
X    return(NULL);
X}
X
X/* see if command is in current directory, if so, return path name
X   in 'buf' */
XBOOL
XgetPath(command,fib,buf)
X  char *command;
X  struct FileInfoBlock *fib;
X  char *buf;
X{
X  BPTR lock;
X  BOOL success = FALSE;
X  if (lock = (BPTR)Lock(command, ACCESS_READ)) {
X    if (Examine(lock, fib)) {
X      buildPath(lock, fib, buf);
X      success = TRUE;
X    }
X    UnLock(lock);
X  }
X  return(success);
X}
X
X/* builds up a path name from 'inlock' and returns it in 'buf' */
XbuildPath(inlock,fib,buf)
X  BPTR inlock;
X  struct FileInfoBlock *fib;
X  char *buf;
X{
X  int i;
X  BPTR lock, oldlock;
X  buf[0] = NULL;
X  lock = inlock;
X
X    /* follow path up, building up name as we go */
X  while (lock) {
X    if (Examine(lock, fib)) {
X      if (fib->fib_FileName[0] > ' ') {
X        if (buf[0]) insert(buf,"/");
X        insert(buf,fib->fib_FileName);
X      }
X    }
X    oldlock = lock;
X    lock = (BPTR)ParentDir(lock);
X      /* make sure we don't unlock the lock passed to us */
X    if (oldlock != inlock)
X       UnLock(oldlock);
X  }
X
X    /* fix up path name */
X  if (fib->fib_FileName[0] > ' ') {
X    register char *p;
X    for (p=buf; *p; p++) {
X      if (*p == '/') {
X        *p = ':';
X	break;
X      }
X    }
X  } else
X    insert(buf,"RAM:");	/* why? */
X}
X
X/* insert 's' at the beginning of 'buf' */
Xinsert(buf,s)
X  char *buf,*s;
X{
X  char tmp[CMDSIZ];
X  strcpy(tmp, buf);
X  strcpy(buf, s);
X  strcpy(&buf[strlen(s)], tmp);
X}
X#endif
SHAR_EOF
echo "extracting Makefile"
sed 's/^X//' << \SHAR_EOF > Makefile
XALL: MyMenu MyMenu-Handler
X
XMyMenu: MyMenu.o Menu.o Parse.o
X	ln -o MyMenu +Q MyMenu.o Menu.o Parse.o -lc32
X
XMyMenu.o: MyMenu.c MyMenu.h
X	cc +L +m MyMenu.c
X
XMyMenu-Handler: MyMenu-Handler.o Mon.o DoRun.o
X	ln -o MyMenu-Handler +Q MyMenu-Handler.o Mon.o DoRun.o -lc32
X
XMyMenu-Handler.o: MyMenu-Handler.c MyMenu.h
X	cc +L MyMenu-Handler.c
X
XDoRun.o: DoRun.c MyMenu.h
X	cc +L DoRun.c
X
XMon.o:	Mon.c MyMenu.h
X	cc +L Mon.c
X
XMenu.o:	Menu.c MyMenu.h
X	cc +L +m Menu.c
X
XParse.o: Parse.c MyMenu.h
X	cc +L +m Parse.c
SHAR_EOF
echo "extracting Menu.c"
sed 's/^X//' << \SHAR_EOF > Menu.c
X/* Copyright ) Darin Johnson, 1989 */
X
X#include <exec/memory.h>
X#include <graphics/text.h>
X#include <graphics/gfxbase.h>
X#include <intuition/intuition.h>
X#include <clib/macros.h>
X#include <functions.h>
X#include "MyMenu.h"
X
XBYTE *copystr();
X
Xunsigned long mnum, inum, snum, menu_num;
Xstruct ext_MenuItem *tail_list;
XUWORD font_width, font_height;
XUBYTE menu_pen;		/* pen number for newly created items */
X
Xextern struct GfxBase *GfxBase;
X
Xstruct Menu *mptr;
Xstruct MenuItem *iptr, *sptr;
X
X/* clean things up and initialize */
Xstart_menu() {
X  MM->my_menu = NULL;
X  menu_pen = 0;	/* default */
X  MM->item_list = tail_list = NULL;
X  font_width = GfxBase->DefaultFont->tf_XSize;
X  font_height = GfxBase->DefaultFont->tf_YSize;
X}
X
X/* clean up widths and other info now that menu is all built */
Xend_menu() {
X  UWORD maxw, smaxw, txtw, top, stop, left;
X  
X  ULONG ilock;
X  if (MM->prev_menu) {
X    ilock = LockIBase(0L);
X    left = MM->prev_menu->LeftEdge + MM->prev_menu->Width + SEPARATE;
X    UnlockIBase(ilock);
X  }
X  for (mptr = MM->my_menu; mptr; mptr=mptr->NextMenu) {
X    mptr->LeftEdge = left;
X    maxw = mptr->Width = (strlen(mptr->MenuName)+1) * font_width;
X    maxw += SEPARATE;
X    left += maxw;
X    top = 0;
X      /* determine max width */
X    for (iptr = mptr->FirstItem; iptr; iptr=iptr->NextItem) {
X      iptr->TopEdge = top;
X      top += iptr->Height;
X      txtw = IntuiTextLength(iptr->ItemFill);
X      if (iptr->Flags & COMMSEQ)
X        txtw += COMMWIDTH+font_width+4;
X      if (txtw > maxw)
X        maxw = txtw;
X    }
X    for (iptr = mptr->FirstItem; iptr; iptr=iptr->NextItem) {
X      iptr->Width = maxw;
X      stop = smaxw = 0;
X      for (sptr=iptr->SubItem; sptr; sptr=sptr->NextItem) {
X        sptr->LeftEdge = maxw;
X        sptr->TopEdge = stop;
X        stop += sptr->Height;
X        txtw = IntuiTextLength(sptr->ItemFill);
X        if (sptr->Flags & COMMSEQ)
X          txtw += COMMWIDTH+font_width+4;
X        if (txtw > smaxw)
X          smaxw = txtw;
X      }
X      for (sptr=iptr->SubItem; sptr; sptr=sptr->NextItem)
X        sptr->Width = smaxw;
X    }
X  }
X}
X
X/* allocate and initialize a new menu */
Xstruct Menu *new_menu(menustr)
X  char *menustr;
X{
X  struct Menu *menu;
X  menu = (struct Menu *)
X	AllocMem(sizeof(struct Menu), MEMF_PUBLIC|MEMF_CLEAR);
X  menu->MenuName = copystr(menustr);
X  menu->Flags = MENUENABLED;
X  menu->NextMenu = NULL;
X  menu->FirstItem = NULL;
X  menu->TopEdge = menu->Height = 0;
X  return menu;
X}
X
X/* Find a menu.  If not found, create a new one */
Xstruct Menu *get_menu(menustr)
X  char *menustr;
X{
X  struct Menu *menu, *prev;
X  mnum = menu_num + 1;
X  if (MM->my_menu == NULL)		/* our menu strip */
X    menu = MM->my_menu = new_menu(menustr);
X  else {
X    for (menu=MM->my_menu; menu; menu=menu->NextMenu) {
X      if (!strcmp(menustr, (char *)menu->MenuName))
X        break;	/* already here */
X      prev = menu;
X      mnum++;
X    }
X    if (menu == NULL)
X      menu = prev->NextMenu = new_menu(menustr);
X  }
X  return menu;
X}
X
X/* create and initialize a new menu item */
Xstruct MenuItem *new_item(itemstr)
X  char *itemstr;
X{
X  struct MenuItem *item;
X  struct IntuiText *it;
X    /* notice that we allocate extra space */
X  item = (struct MenuItem *)
X	AllocMem(sizeof(struct ext_MenuItem), MEMF_PUBLIC|MEMF_CLEAR);
X  it = (struct IntuiText *)
X	AllocMem(sizeof(struct IntuiText), MEMF_PUBLIC|MEMF_CLEAR);
X  item->NextItem = item->SubItem = NULL;
X  item->SelectFill = NULL;
X  item->NextSelect = 0;
X  item->Height = font_height + 1;
X  item->LeftEdge = item->TopEdge = item->MutualExclude = 0;
X  item->Flags = ITEMTEXT+HIGHCOMP+ITEMENABLED;
X  it->FrontPen = menu_pen;
X  it->BackPen = AUTOBACKPEN;
X  it->LeftEdge = it->TopEdge = 1;
X  it->ITextFont = NULL;
X  it->NextText = NULL;
X  it->DrawMode = JAM2;
X  it->IText = (UBYTE*)copystr(itemstr);
X  item->ItemFill = (APTR)it;
X  return item;
X}
X
X/* find a menu item, if not found, create a new one */
Xstruct MenuItem *get_item(menustr, itemstr)
X  char *menustr, *itemstr;
X{
X  struct Menu *menu;
X  struct MenuItem *item, *prev;
X  menu = get_menu(menustr);
X  inum = 0;
X  if (menu->FirstItem == NULL)
X    item = menu->FirstItem = new_item(itemstr);
X  else {
X    for (item=menu->FirstItem; item; item=item->NextItem) {
X      if (!strcmp(itemstr, ((struct IntuiText *)item->ItemFill)->IText))
X        break;	/* already here */
X      prev = item;
X      inum++;
X    }
X    if (item == NULL)
X      item = prev->NextItem = new_item(itemstr);
X  }
X  return item;
X}
X
X#define EITEM ((struct ext_MenuItem *)item)
X
X/* create a new menu item (or sub item) */
Xstruct ext_MenuItem *add_menu(menustr, itemstr, substr, comm)
X  char *menustr, *itemstr, *substr, comm;
X{
X  struct MenuItem *item, *sub, *prev;
X  if (!itemstr)
X    return NULL;	/* oops */
X  item = get_item(menustr, itemstr);	/* get (or create) the item */
X  if (substr && *substr) {
X    snum = 0;
X    if (item->SubItem == NULL)
X      sub = item->SubItem = new_item(substr);	/* get the sub-item */
X    else {
X      for (sub=item->SubItem; sub; sub=sub->NextItem) {
X        if (!strcmp(substr, (char *)sub->ItemFill))
X          break;	/* duplicate */
X        prev = sub;
X	snum++;
X      }
X      if (sub == NULL)
X        sub = prev->NextItem = new_item(substr);
X    }
X    item = sub;
X  } else
X    snum = NOSUB;
X  if (comm) {
X    item->Command = comm;
X    item->Flags |= COMMSEQ;
X  }
X    /* fill in our private fields */
X  EITEM->id = SHIFTMENU(mnum)|SHIFTITEM(inum)|SHIFTSUB(snum);
X  EITEM->cmd = EITEM->args = NULL;
X  EITEM->type = NULL;
X    /* stick onto item_list */
X  EITEM->next_item = NULL;
X  if (MM->item_list==NULL)
X    MM->item_list = EITEM;
X  else
X    tail_list->next_item = EITEM;
X  tail_list = EITEM;
X  return EITEM;
X}
X
X/* free up memory used by a menu item */
Xfree_item(item)
X  struct ext_MenuItem *item;
X{
X  struct IntuiText *it;
X  it = (struct IntuiText *)item->MenuItem.ItemFill;
X  FreeMem(it->IText, strlen(it->IText)+1);
X  FreeMem(it, sizeof(struct IntuiText));
X  if (item->cmd)
X    FreeMem(item->cmd, strlen(item->cmd)+1);
X  if (item->args)
X    FreeMem(item->args, strlen(item->args)+1);
X  FreeMem(item, sizeof(struct ext_MenuItem));
X}
X
X/* free up all space taken up by our menus */
Xfree_menus() {
X  struct Menu *mtmp;
X  struct MenuItem *itmp;
X
X  mptr = MM->my_menu;
X  MM->my_menu = NULL;
X  while (mptr) {
X    iptr = mptr->FirstItem;
X    while (iptr) {
X      sptr = iptr->SubItem;
X      while (sptr) {
X        itmp = sptr;
X	sptr = sptr->NextItem;
X	free_item(itmp);
X      }
X      itmp = iptr;
X      iptr = iptr->NextItem;
X      free_item(itmp);
X    }
X    mtmp = mptr;
X    mptr = mptr->NextMenu;
X    FreeMem(mtmp->MenuName, strlen(mtmp->MenuName)+1);
X    FreeMem(mtmp, sizeof(struct Menu));
X  }
X}
SHAR_EOF
echo "extracting Mon.c"
sed 's/^X//' << \SHAR_EOF > Mon.c
X/* Copyright ) Darin Johnson, 1989 */
X
X/* This file has routines to allow us to 'monitor' an Intuition port */
X
X/* What happens, is that we create a port, myPort, and set things up */
X/* so that GetMsg works normally for myPort, and the original port,  */
X/* winPort.  However, PutMsg to myPort will put the new message      */
X/* on winPort, and vice-versa.  Basically, the heads of both message */
X/* lists are normal, but the two tails have been swapped.  Since     */
X/* GetMsg uses the head of the list, it acts normally.  PutMsg uses  */
X/* the tail of the list, so it acts differently.  (the sigBit and    */
X/* sigTasks have also been swapped)                                  */
X
X/* What this means, is that anything PutMsg'ed to the winPort, will  */
X/* signal us, and we can GetMsg it.  When we are finished looking at */
X/* that message, we can give it to winPort by PutMsg'ing to myPort.  */
X
X#include <exec/ports.h>
X#include <libraries/dos.h>
X#include <libraries/dosextens.h>
X#include <intuition/intuitionbase.h>
X#include <devices/inputevent.h>
X#include <functions.h>
X#include "mymenu.h"
X
X#ifdef DO_WB
Xextern struct MsgPort *wb_reply_port;
Xextern int wb_cnt;
X#endif
X
Xstatic struct MsgPort *winPort;
Xstatic struct List *winMsgList;
Xstatic LONG winSignal, our_signal;
Xstatic LONG winMask;
Xstatic BYTE orig_pri;
X
X/* our port */
Xstatic struct MsgPort myPort =
X{
X   {NULL, NULL, NT_MSGPORT, 0, NULL},
X   PA_SIGNAL,
X   0,
X   NULL,
X   {NULL, NULL, NULL, NT_MESSAGE, 0}
X};
X
Xstatic struct List *myMsgList = &(myPort.mp_MsgList);
X
X/* fiddle with the two ports, setting things up */
Xmake_MsgPort()
X{
X  Forbid();
X    /* get the info we need */
X  winPort    = MM->WBWindow->UserPort;
X  winMsgList = &(winPort->mp_MsgList);
X  winSignal  = winPort->mp_SigBit;
X  winMask    = 1L << winSignal;
X
X    /* setup our port */  
X  myPort.mp_SigBit    = winSignal;
X  myPort.mp_SigTask   = winPort->mp_SigTask;
X
X    /* flip things around */
X  winPort->mp_SigTask = MM->handler_task;
X  myMsgList->lh_Head = (struct Node *)&(winMsgList->lh_Tail);
X  myMsgList->lh_TailPred = winMsgList->lh_TailPred;
X  winMsgList->lh_TailPred = (struct Node *)&(myMsgList->lh_Head);
X  myMsgList->lh_TailPred->ln_Succ = (struct Node *)&(myMsgList->lh_Tail);
X
X  Permit();
X
X    /* prevent deadlocks */
X  orig_pri = SetTaskPri(MM->handler_task,
X		myPort.mp_SigTask->tc_Node.ln_Pri+1);
X}
X
X/* Restore things the way they were before make_MsgPort()           */
X/* Note that we don't refer to winPort.  This is because we         */
X/* may have had someone else 'monitor' this port (such as MonIDCMP) */
X/* besides us.                                                      */
Xdel_MsgPort() {
X  struct Message *tmpMsg;
X  struct MsgPort *tmpPort;
X  
X  Forbid();
X    /* clean out our list */
X  while ((tmpMsg = GetMsg(&myPort)) != NULL) PutMsg(&myPort, tmpMsg);
X
X     /* find MsgPort that myMsgList->lh_Head belongs to */
X     /*      (the port we've been PutMsg'ing to)        */
X  tmpPort = (struct MsgPort *) (
X    (UBYTE*) &myPort - (UBYTE*) &(myPort.mp_MsgList.lh_Tail)
X    + (UBYTE*)myMsgList->lh_Head);
X
X    /* restore things */
X  myMsgList->lh_Head->ln_Pred = myMsgList->lh_TailPred;
X  myMsgList->lh_TailPred->ln_Succ = myMsgList->lh_Head;
X  tmpPort->mp_SigTask = myPort.mp_SigTask;
X  Permit();
X  
X  SetTaskPri(MM->handler_task, orig_pri);
X}
X
X/* do all appropriate initialization of monitor */
XBOOL setup_mon()
X{
X  make_MsgPort();
X    /* we need to allocate the same signal, so that it doesn't get
X       allocated to something else, messing us up */
X  our_signal = AllocSignal(winSignal);
X  if (our_signal != winSignal) {
X    finish_mon();
X    return FALSE;
X  }
X  return TRUE;
X}
X
X/* clean up everything */
Xfinish_mon() {
X  del_MsgPort();
X  FreeSignal(our_signal);
X}
X
X/* See if we handle this event.  If so, return ext_MenuItem structure */
Xstruct ext_MenuItem *
Xmatch_item(Class, Code)
X  ULONG Class;
X  USHORT Code;
X{
X  register struct ext_MenuItem *tmp;
X  if (Class != MENUPICK || Code == MENUNULL)
X    return NULL;
X  for (tmp = MM->item_list; tmp; tmp=tmp->next_item)
X    if (tmp->id == Code)
X      return tmp;
X  return NULL;
X}
X
X/* The actual monitor.  Just keep waiting for messages, and acting on them */
X/* Note that if we don't use a certain IDCMP message, we PutMsg it to get  */
X/* it back to the window it was meant for.                                 */
Xmonitor()
X{
X  register struct IntuiMessage *msg;
X  register void *item;
X  register ULONG mask, wb_mask;
X
X#ifdef DO_WB
X  winMask |= (wb_mask = (1L << wb_reply_port->mp_SigBit));
X#endif
X
X  winMask |= SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D;
X
X  while (TRUE) {
X    mask = Wait(winMask);
X    if (mask & SIGBREAKF_CTRL_C) {
X	/* caught ^C, time to close up */
X      break;
X    }
X    if (mask & SIGBREAKF_CTRL_D) {
X        /* reparse menus */
X      del_menu_strip();
X      Signal(MM->parent_task, (1 << MM->parent_sig));
X      Wait(SIGBREAKF_CTRL_D);
X      add_menu_strip();
X      continue;
X    }
X#ifdef DO_WB
X    if (mask & wb_mask) {
X        /* caught a reply from a WorkBench program exitting */
X      register struct Message *wb_msg;
X      while ((wb_msg = GetMsg(wb_reply_port)) != NULL) {
X        wbfree(wb_msg);
X	wb_cnt = (wb_cnt>0 ? (wb_cnt-1) : 0);
X      }
X      continue;
X    }
X#endif
X      /* if we got here, we have an IDCMP message */
X    while ((msg = (struct IntuiMessage *)GetMsg(&myPort)) != NULL) {
X          /* check if we deal with this or not */
X      if ((item=(void *)match_item(msg->Class, msg->Code))!=NULL) {
X        ReplyMsg(msg);		/* always reply */
X        run_item(item);		/* run the indicated program */
X      } else {
X        PutMsg(&myPort, msg);	/* pass on to original destination */
X      }
X    }
X  }
X}
SHAR_EOF
echo "extracting MyMenu-Handler.c"
sed 's/^X//' << \SHAR_EOF > MyMenu-Handler.c
X/* Copyright ) Darin Johnson, 1989 */
X/*                                 */
X/* Permission is granted to use    */
X/* this program and to freely copy */
X/* it and/or source code as long   */
X/* as these notices remain.        */
X/* No charges for these copies may */
X/* be made, except for handling    */
X/* and distribution fees.          */
X
X/* This is the main process for MyMenu.  This gets loaded and run in the */
X/* background by MyMenu.  When started, it finishes initializing itself, */
X/* and starts monitoring the WorkBench IDCMP port.  If any messages show */
X/* up that we are interested in, we run them as a CLI or WorkBench       */
X/* process.  We don't do any memory allocation or cleanup and leave that */
X/* all to MyMenu (in order to save some space).  Communication with      */
X/* MyMenu is done via a public port - never used as a port, but it holds */
X/* "global" variables.                                                   */
X
X#include <exec/types.h>
X#include <intuition/intuition.h>
X#include <libraries/dos.h>
X#include <libraries/dosextens.h>
X#include <workbench/icon.h>
X#include <functions.h>
X#include "mymenu.h"
X
Xstatic char *copyright = "Copyright ) Darin Johnson, 1989";
X
Xstruct IntuitionBase *IntuitionBase;
X
X#ifdef DO_WB
Xstruct IconBase *IconBase;	/* already defined in Manx C */
X#endif
X
Xstruct MMData *MM;		/* data shared with other tasks */
Xstruct Process *MyProcess;
X#ifdef DO_WB
Xint wb_cnt;			/* number of wb processes run */
Xstruct MsgPort *wb_reply_port;	/* replies from terminating WB processes */
X#endif
X
X#ifdef DO_WB
X  /* error messages */
Xstruct IntuiText wb_open_msg_5 = { 3,1,JAM2,24,36,NULL,
X	(UBYTE*)"Do you REALLY want to quit now?", NULL };
Xstruct IntuiText wb_open_msg_4 = { 0,1,JAM2,7,28,NULL,
X	(UBYTE*)"the program finishes after MyMenu.", &wb_open_msg_5 };
Xstruct IntuiText wb_open_msg_3 = { 0,1,JAM2,7,20,NULL,
X	(UBYTE*)"still be open.  This can cause a crash if", &wb_open_msg_4 };
Xstruct IntuiText wb_open_msg_2 = { 0,1,JAM2,7,12,NULL,
X	(UBYTE*)"A WorkBench program started by MyMenu may", &wb_open_msg_3 };
Xstruct IntuiText wb_open_msg = { 3,1,JAM2,142,4,NULL,
X	(UBYTE*)"WARNING!!", &wb_open_msg_2 };
Xstruct IntuiText true_msg = { 0,1,JAM2,7,3,NULL,
X	(UBYTE*)"Yes, quit MyMenu", NULL };
Xstruct IntuiText false_msg = { 0,1,JAM2,7,3,NULL,
X	(UBYTE*)"No! I didn't mean it", NULL };
X#endif
X
X/* returns an error to MyMenu if we get one during initialization */
Xerror(code)
Xchar code;
X{
X  MM->error_code = code;
X  if (IntuitionBase)
X    CloseLibrary(IntuitionBase);
X#ifdef DO_WB
X  if (IconBase)
X    CloseLibrary(IconBase);
X#endif
X    /* coordinate with add_handler() in MyMenu */
X  Signal(MM->parent_task, 1 << MM->parent_sig);
X    /* now coordinate with remove_handler() in MyMenu */
X  Wait(SIGBREAKF_CTRL_C);	/* wait for parent */
X  Signal(MM->parent_task, 1 << MM->parent_sig);  
X  _exit(0);
X}
X
X/* add personalized menus to menustrip */
Xadd_menu_strip() {
X  struct Menu *menu_strip;
X  ULONG ilock;
X  menu_strip = MM->WBWindow->MenuStrip;
X  ilock = LockIBase(0L);
X  ClearMenuStrip(MM->WBWindow);
X  MM->prev_menu->NextMenu = MM->my_menu;
X  SetMenuStrip(MM->WBWindow, menu_strip);
X  UnlockIBase(ilock);
X}
X
X/* remove our personalized menus from menustrip */
Xdel_menu_strip() {
X  struct Menu *menu_strip;
X  ULONG ilock;
X  menu_strip = MM->WBWindow->MenuStrip;
X  ilock = LockIBase(0L);
X  ClearMenuStrip(MM->WBWindow);
X  MM->prev_menu->NextMenu = NULL;
X  SetMenuStrip(MM->WBWindow, menu_strip);
X  UnlockIBase(ilock);
X}
X
X/* main program - initialize and start up monitor */
X_main() {
X    /* find data set up by parent */
X  MM = (struct MMData *)FindPort(MYMENU_NAME);
X  if (!MM)
X    _exit(0);	/* some one ran us directly, bail out */
X
X    /* finish initialization */
X  MM->handler_task = FindTask(NULL);
X  MyProcess = (struct Process *)MM->handler_task;
X  MyProcess->pr_ConsoleTask = NULL;
X  MyProcess->pr_CLI = NULL;
X  MyProcess->pr_WindowPtr = (APTR)-1;	/* disable requesters for FindIt() */
X  MyProcess->pr_CurrentDir = NULL;
X
X    /* open necessary libraries */  
X  IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 0);
X  if (IntuitionBase == NULL)
X    error(ERR_LIB);
X#ifdef DO_WB
X  IconBase = (struct IconBase *)OpenLibrary(ICONNAME, 0);
X  if (IconBase == NULL)
X    error(ERR_LIB);
X#endif
X
X  if (!MM->WBWindow)
X    error(ERR_WIN);
X  if (!setup_mon())
X    error(ERR_MON);
X
X  MM->error_code = ERR_OK;
X
X    /* signal parent that we're fully weaned */
X  Signal(MM->parent_task, 1 << MM->parent_sig);
X
X  add_menu_strip();
X
X#ifdef DO_WB
X  wb_cnt = 0;
X    /* create port AFTER setup_mon() so we don't get same signal */
X  wb_reply_port = CreatePort(WBPORT_NAME, 0);
X  NewList(&wb_reply_port->mp_MsgList);	/* is this necessary? */
X#endif
X
Xrestart:
X    monitor();
X#ifdef DO_WB
X      /* see if we can leave gracefully */
X      /* we don't want to terminate and then have a WB reply message */
X    if ((wb_cnt > 0) && (AutoRequest(NULL,&wb_open_msg,
X    			&true_msg,&false_msg,0,0,370,80)==FALSE))
X    {
X      MM->error_code = ERR_WB_OPEN;
X      Signal(MM->parent_task, 1 << MM->parent_sig);  
X      goto restart;
X    }
X#endif
X
X    /* clean everything up */
X  MM->error_code = ERR_OK;	/* let parent know that we finished up */
X  finish_mon();
X
X  del_menu_strip();
X  CloseLibrary(IntuitionBase);
X#ifdef DO_WB
X  CloseLibrary(IconBase);
X#endif
X
X    /* Forbid until we terminate, so that we don't get UnLoadSeg'ed to soon */
X  Forbid();
X
X#ifdef DO_WB
X  {
X    register struct Message *msg;
X    while ((msg=GetMsg(wb_reply_port))!=0)
X      wbfree(msg);
X    DeletePort(wb_reply_port);
X  }
X#endif
X
X    /* signal parent that we are done */
X  Signal(MM->parent_task, 1 << MM->parent_sig);  
X}
SHAR_EOF
echo "extracting MyMenu.c"
sed 's/^X//' << \SHAR_EOF > MyMenu.c
X/* Copyright ) Darin Johnson, 1989 */
X/*                                 */
X/* Permission is granted to use    */
X/* this program and to freely copy */
X/* it and/or source code as long   */
X/* as these notices remain.        */
X/* No charges for these copies may */
X/* be made, except for handling    */
X/* and distribution fees.          */
X
X/* This program puts customized menus into the WorkBench menubar.      */
X/* The program is split up into two parts, MyMenu and MyMenu-Handler.  */
X/* This section is MyMenu and it initializes and starts up a process   */
X/* to run MyMenu-Handler, and then terminates.  MyMenu also terminates */
X/* and cleans up after the suprocess.  MyMenu will parse the users     */
X/* menus, initialize data to be passed to MyMenu-Handler, and start    */
X/* up MyMenu-Handler.  MyMenu-Handler does very little memory          */
X/* allocation in order to save space, so MyMenu does allocation and    */
X/* freeing for it.                                                     */
X
X#include <exec/types.h>
X#include <exec/memory.h>
X#include <graphics/gfxbase.h>
X#include <intuition/intuitionbase.h>
X#include <libraries/dos.h>
X#include <libraries/dosextens.h>
X#include <functions.h>
X#include <stdio.h>
X#include <ctype.h>
X#include "mymenu.h"
X
Xstatic char *copyright = "Copyright ) Darin Johnson, 1989";
X
X/* actually, I don't know if it will work under Lattice */
X#ifdef AZTEC_C
X#define strcmp _BUILTIN_strcmp
X#define strcpy _BUILTIN_strcpy
X#define strlen _BUILTIN_strlen
X#endif
X
Xstruct IntuitionBase *IntuitionBase = NULL;
Xstruct GfxBase *GfxBase = NULL;
X
Xextern int menu_num;
X
X/* this is the data structure that we use to pass data to MyMenu-Handler */
Xstruct MMData *MM;
X
Xchar do_quit;
Xint i;
X
X/* make (and allocate) a copy of the passed string */
Xchar *copystr(str)
Xchar *str;
X{
X  char *newstr;
X  newstr = AllocMem(strlen(str)+1, MEMF_PUBLIC);
X  strcpy(newstr, str);
X  return newstr;
X}
X
X/* strcmp, ignoring case */
X#define UPPER(c) (islower(c)?toupper(c):(c))
Xint stricmp(s, t)
X  char *s, *t;
X{
X  for ( ; UPPER(*s) == UPPER(*t); s++, t++)
X    if (*s== NULL)
X      return 0;
X  return UPPER(*s) - UPPER(*t);
X}
X
X/* Find the workbench window - I'm sure there's a better way... */
Xstruct Window *find_workbench() {
X  struct Screen *scr;
X  struct Window *win;
X  ULONG ilock;
X  ilock = LockIBase(0L);	/* just so's things don't change on us */
X  for (scr = IntuitionBase->FirstScreen;	/* find WB Screen */
X       scr && strcmp(scr->DefaultTitle, "Workbench Screen");
X       scr=scr->NextScreen);
X  if (!scr) {
X    UnlockIBase(ilock);
X    printf("That's odd...  I can't find the workbench screen...\n");
X    _abort(2000);
X  }
X  for (win=scr->FirstWindow; win; win = win->NextWindow)
X    if (win->Flags & WBENCHWINDOW) break;      
X  UnlockIBase(ilock);
X  return win;
X}
X
X/* open a library */
XAPTR *open_lib(name, rev)
Xchar *name;
Xlong rev;
X{
X  APTR *lib;
X  if ((lib = (APTR*)OpenLibrary(name, rev)) == NULL) {
X    printf("Can't open %s (rev=%d\n", name, rev);
X    _abort(1000);
X  }
X  return lib;
X}
X
X/* make a copy of our CLI path.  We also convert BCPL stuff. */
Xcopy_path() {
X  register struct Path *oldpath, *newpath, *tmppath;
X  struct CommandLineInterface *cli;
X  struct Process *pr;
X  
X  pr = (struct Process *)FindTask(NULL);
X  cli = (struct CommandLineInterface *)BADDR(pr->pr_CLI);
X  newpath = MM->CLI_path = NULL;
X  for (oldpath = (struct Path *)BADDR(cli->cli_CommandDir); oldpath;
X              oldpath = (struct Path *) BADDR(oldpath->path_Next)) {
X    tmppath = (struct Path *)AllocMem(sizeof(struct Path), MEMF_PUBLIC);
X    if (!MM->CLI_path) {
X      MM->CLI_path = tmppath;
X    } else {
X      newpath->path_Next = tmppath;	/* we don't convert to BCPL */
X    }
X    newpath = tmppath;
X    tmppath->path_Next = NULL;
X    tmppath->path_Lock = DupLock(oldpath->path_Lock);
X  }
X}
X
X/* free up the space used by the copy of the CLI path */
Xfree_path() {
X  register struct Path *tmp, *path;
X      /* clean out copy of path */
X  path = MM->CLI_path;
X  MM->CLI_path = NULL;
X  while (path) {
X    tmp = path;
X    path = path->path_Next;
X    UnLock(tmp->path_Lock);
X    FreeMem(tmp, sizeof(struct Path));
X  }
X}
X
X/* Initialize and load up the handler process, or update the menus */
X/* for an existing handler.                                        */
Xadd_handler() {
X  ULONG ilock;
X
X  if (MM) {
X      /* remove old menus and update */
X    printf("Updating menus -- ");
X    fflush(stdout);
X      /* coordinate with handler */
X    MM->parent_task = FindTask(NULL);
X    MM->parent_sig = AllocSignal(-1L);
X    Signal(MM->handler_task, SIGBREAKF_CTRL_D);
X    Wait(1 << MM->parent_sig);
X      /* clean old stuff up */
X    free_menus();
X    ilock = LockIBase(0L);
X    menu_num = 0;
X    for (MM->prev_menu = MM->WBWindow->MenuStrip;
X	MM->prev_menu->NextMenu;
X	MM->prev_menu=MM->prev_menu->NextMenu)
X      menu_num++;
X    UnlockIBase(ilock);
X
X      /* get new menus */
X    if (!parse_menus()) {
X      do_quit = TRUE;
X      return;
X    }
X
X      /* let handler know it can continue */
X    Signal(MM->handler_task, SIGBREAKF_CTRL_D);
X    printf("ok\n");
X    return;
X  }
X
X    /* create a new handler */  
X  printf("Installing MyMenu -- ");
X  fflush(stdout);
X
X    /* get area to pass data in */
X  MM = (struct MMData *)AllocMem(sizeof(struct MMData), MEMF_PUBLIC|MEMF_CLEAR);
X
X  MM->WBWindow = find_workbench();
X  if (MM->WBWindow==NULL) {
X    printf("Can't find Workbench...\n");
X    do_quit = TRUE;
X    return;
X  }
X
X    /* we need to find out what menu number ours start at */
X  ilock = LockIBase(0L);
X  menu_num = 0;
X  for (MM->prev_menu = MM->WBWindow->MenuStrip;
X	MM->prev_menu->NextMenu;
X	MM->prev_menu=MM->prev_menu->NextMenu)
X    menu_num++;
X  UnlockIBase(ilock);  
X
X    /* read in user's menus */
X  if (!parse_menus()) {
X    do_quit = TRUE;
X    return;
X  }
X
X  MM->port.mp_Flags = PA_IGNORE;	/* we'll get msg's from do_wbrun */
X  MM->port.mp_Node.ln_Pri = 0;
X  MM->port.mp_Node.ln_Type = NT_MSGPORT;
X  MM->port.mp_Node.ln_Name = copystr(MYMENU_NAME);
X  NewList(&MM->port.mp_MsgList);
X
X    /* load in handler */
X  MM->segment = LoadSeg("l:MyMenu-Handler");
X  if (!MM->segment)
X    MM->segment = LoadSeg("MyMenu-Handler");
X  if (!MM->segment) {
X    printf("Can't find L:MyMenu-Handler\n");
X    do_quit = TRUE;
X    return;
X  }
X
X    /* add to port list so that handler can find it */
X  AddPort(&MM->port);
X
X  MM->parent_task = FindTask(NULL);
X  MM->parent_sig = AllocSignal(-1L);
X  copy_path();
X  CreateProc(MYMENU_NAME, 0, MM->segment, STACK);
X    /* handshake so that we know it got started ok */
X  Wait(1 << MM->parent_sig);
X  FreeSignal(MM->parent_sig);
X  if (MM->error_code) {
X    printf("Handler error (%d)\n", MM->error_code);
X    do_quit = TRUE;
X  } else {
X    printf("ok - %s\n", VERSION);
X  }
X}
X
X/* shutdown and clean up after handler */
Xremove_handler() {
X  if (!MM)
X    return;
X  printf("removing -- ");
X  fflush(stdout);
X  if (MM->segment != NULL) {
X    MM->parent_task = FindTask(NULL);
X    MM->parent_sig = AllocSignal(-1L);
X    Signal(MM->handler_task, SIGBREAKF_CTRL_C);
X
X      /* wait until handler cleans up */
X      /* (don't advertise that we catch ^C - for emergency use only) */
X    Wait(1 << MM->parent_sig);
X    FreeSignal(MM->parent_sig);
X    if (MM->error_code != ERR_OK) {
X      /* we can get an error if there are outstanding WB processes */
X      printf("Leaving handler in place...\n");
X      return;
X    }
X    RemPort(&MM->port);
X    UnLoadSeg(MM->segment);
X  }
X    /* this is done here, not in handler... */
X  free_path();
X  free_menus();
X  if (MM->port.mp_Node.ln_Name)
X    FreeMem(MM->port.mp_Node.ln_Name, strlen(MM->port.mp_Node.ln_Name)+1);
X  FreeMem(MM, sizeof(struct MMData));
X  printf("done\n");
X}
X
X/* Close up libraries and exit.  Having a routine named _abort() is */
X/* handy for use with SDB, otherwise, I would have a better name.   */
X_abort(st)
X  int st;
X{
X  if (IntuitionBase)
X    CloseLibrary(IntuitionBase);
X  if (GfxBase)
X    CloseLibrary(GfxBase);
X  exit(st);
X}
X
Xmain(argc, argv)
X  int argc;
X  char *argv[];
X{
X  IntuitionBase = (struct IntuitionBase *)open_lib("intuition.library", 0L);
X  GfxBase = (struct GfxBase *)open_lib("graphics.library", 0L);
X  
X  do_quit=FALSE;
X  for (i=1; i<argc; i++) {
X    if (stricmp(argv[i], "QUIT")==0) {
X      do_quit=TRUE;
X      continue;
X    }
X      /* no other options yet */
X    printf("Usage: MyMenu [quit]\n");
X    _abort(10);
X  }
X
X    /* find shared data (if it exists) */
X  MM = (struct MMData *)FindPort(MYMENU_NAME);
X
X  if (do_quit == FALSE) {
X    add_handler();
X  };
X  if (do_quit==TRUE) {		/* note that this isn't an 'else' */
X      remove_handler();
X  }
X  _abort(0);
X}
SHAR_EOF
echo "extracting MyMenu.conf"
sed 's/^X//' << \SHAR_EOF > MyMenu.conf
X# this is a sample configuration file.
Xcolor 2
Xmenu DMouse "DMouse On" | CLI fd0:utilities/dmouse -R0008 -l0008 -A2 -Q0008 "<nil: >nil: newcon:320/120/320/80/NewCLI"
Xmenu DMouse "Dmouse Off" | CLI fd0:utilities/dmouse quit
Xmenu <T> Stuff "Games   ;" Tetrix | WB fd0:games/tetrix
Xmenu <B> Stuff "Games   ;" "Black Box" | WB fd0:games/Black Box
Xmenu <S> Stuff "Games   ;" "Sorry!" | WB fd0:games/Sorry!
Xmenu <C> Stuff "Games   ;" Kamikaze | CLI fd0:games/KamikazeChess
Xmenu <N> Stuff NewCLI | CLI c:newcli newcon:320/100/0/0/NewCLI
Xmenu <V> Stuff VT100 | CLI fd0:comm/vt100
Xcolor 3
Xmenu Stuff "MyMenu Quit" | CLI fd0:progs/mymenu/mymenu quit
SHAR_EOF
echo "extracting MyMenu.doc"
sed 's/^X//' << \SHAR_EOF > MyMenu.doc
XMyMenu is a program to allow you to create your own menus in the WorkBench
Xto run your own commands.  This can save the hassle of opening up lots
Xof drawers to get to the command you want.  MyMenu will allow you to execute
Xboth CLI and WorkBench programs, and is configured with a normal text
Xfile.  Hopefully, this program will be obsolete when WorkBench 1.4
Xis released, since I hear this capability may be added.
X
XBuilding:
X
X  There is a supplied Makefile, which works with Manx 3.6, and a version
X  of make from a Fish disk (I don't remember which one).  The Makefile
X  should work with most make programs.  I haven't tried to get this
X  to work with 16-bit integers, or to work with Lattice (although, I
X  would like this).
X
X  There are some defines in MyMenu.h that can be customized.  DO_WB
X  determines if support for WorkBench programs is included.  Undefining
X  this can save a lot of space if you don't need this option.  DO_PATH
X  if defined will compile in code for path searching for commands.
X  Currently, it is undefined.
X
XInstallation:
X
X  Copy MyMenu-Handler to the L: directory.  MyMenu will also look
X  for this file in the current directory if it does not exist in
X  the L: directory.
X
X  Create the file S:MyMenu.conf.  The format of this file is described
X  later.
X
XRunning:
X
X  Step one is to start up the Workbench!
X  
X  The command "MyMenu" will load and start up the handler process.
X  Menus should now appear in the WorkBench menu strip.
X  
X  Running MyMenu while the handler is already loaded will cause
X  it to re-parse the configuration file and rebuild the menus.
X
X  The command "MyMenu quit" will terminate and unload the handler
X  process.
X
XConfiguration file:
X
X  The configuration file is read in when you run MyMenu.  It is
X  looked for in the S: directory, and in the current directory in
X  that order.
X
X  The configuration file defines the menus you want, and what commands
X  they will run.  You may put carriage returns between keywords, but
X  you cannot put a carriage return in the middle of a command.
X  Comments begin with a #, and continue until the end of the line.
X  Upper and lowercase does not make a difference.
X
X  COLOR n
X
X    This will set the foreground pen color for new menus.  You can
X    change this as often as you want.  The arguments is number that is
X    the pen number to use.  The default is 2 (black).
X
X  MENU [<command-char>] menu-name item-name [sub-item-name] | command-def
X
X    Defines a new menu.  Each menu definition must have a menu-name and
X    an item-name.  A sub-item-name is optional.  If any of these names
X    contain whitespace, enclose the name in double quotes, or precede
X    the whitespace character with a backslash (\).  A command character
X    may be defined for the menu item by putting the character after the
X    MENU keyword and surround it with <>'s.
X
X    Separate the menu definition and the command definition with a vertical
X    bar (|).  After the bar, you can have the keywords CLI or WB, to
X    specify that the command is a CLI or WorkBench command.  Everything
X    after WB until the end of the line is taken to be the command.
X    Everything after CLI until a space or end of the line is taken to be
X    the command.  Everything else will be passed as arguments to the
X    CLI command.  Do not include redirection in a CLI command.
X
X  Examples:
X
X    menu Games Tetrix | WB dh0:games/tetrix
X
X      This will define a menu "Games", with a menu item "Tetrix".
X      If this is selected, the command "dh0:games/tetrix" will be run
X      as a WorkBench program.
X
X    menu Games "Black Box" | WB df0:Black Box
X
X      This will define a menu item "Black Box" under the menu "Games".
X      The menu Games will already exist from the previous example, and
X      will now contain "Tetrix" and "Black Box" as items.  The command
X      to be run is "df0:Black Box" (Box is not an argument).
X
X    color 3
X    menu  Utilities DMouse "Start DMouse" | c:DMouse
X    menu  Utilities DMouse "Quit DMouse" | c:DMouse quit
X
X      This will define two sub menu items (drawn in yellow).  The second
X      command has an argument.
X
XFuture plans and hopeless dreams:
X  AREXX support.
X  Fix up path searching.
X  A better parser.
X  Support for graphical menu items.
X
XLimitations, bugs, and other wierd things:
X  Path searching does not work.  Do not compile this in.
X  I would appreciate it if someone can find the problem, since I
X  am stuck.  Details are in DoRun.c.
X
X  New preferences causes menus to disappear (but handler is not unloaded).
X  The menus can be rebuilt by running MyMenu again.
X
X  Redirection can not be done for CLI programs, since the code already
X  does redirection to and from NIL:.
X
X  Line limit of 256 characters in configuration file.
X
XAcknowledgements:
X
X  I have borrowed ideas and code snipits from other public domain
X  programs.  I have also received help directly from some people.
X  I would like to thank those people (whose names I remember):
X  
X    Matt Dillon		- Ideas for menu building and how to load in the
X                          handler were taken from DME and DMouse.
X    Peter da Silva	- Ideas taken from wblaunch program.
X    Rob Peck		- Some ideas and help.
X    Davide Cervone	- LOTS of ideas taken from MonIDCMP.
X
XSignature:
X
X  Darin Johnson
X  darin@laic.UUCP
X  {leadsv!laic!darin@pyramid.pyramid.com}
X  (415) 368-0972
SHAR_EOF
echo "extracting MyMenu.h"
sed 's/^X//' << \SHAR_EOF > MyMenu.h
X/* Copyright ) Darin Johnson, 1989 */
X
X#define VERSION "MyMenu v1.0"
X
X#define MYMENU_NAME "MyMenu"
X#define WBPORT_NAME "MyMenu-WBPort"
X
X#define DO_WB
X/* #define DO_PATH	- not working yet */
X/* #define DO_AREXX 	- future plans... */
X
X#define SEPARATE 32	/* distance between menus */
X#define STACK 4096	/* 2048 is too small */
X
Xstruct ext_MenuItem {
X  struct MenuItem MenuItem;
X  struct ext_MenuItem *next_item;
X  ULONG id;
X  char *cmd;
X  char *args;
X  char type;
X};
X
Xstruct Path {
X  struct Path *path_Next;
X  struct FileLock *path_Lock;
X};
X
X/* would declare whole thing 'volatile' if allowed */
Xstruct MMData {
X  struct MsgPort port;
X  struct ext_MenuItem *item_list;
X  struct Menu *my_menu, *prev_menu, *WBMenu;
X  struct Window *WBWindow;
X  struct Task *handler_task, *parent_task;
X  USHORT parent_sig, handler_sig;
X  char error_code;
X  struct Segment *segment;
X  struct Path *CLI_path;		/* copy of CLI path */
X};
X
Xextern struct MMData *MM;
Xextern struct ext_MenuItem *add_menu();
X
X/* error codes */
X#define ERR_OK		0
X#define ERR_LIB		1
X#define ERR_WIN		2
X#define ERR_MON		3
X#define ERR_WB_OPEN	4
SHAR_EOF
echo "extracting Parse.c"
sed 's/^X//' << \SHAR_EOF > Parse.c
X/* Copyright ) Darin Johnson, 1989 */
X
X#include <exec/types.h>
X#include <intuition/intuition.h>
X#include <stdio.h>
X#include <ctype.h>
X#include "mymenu.h"
X
X#define STR 1
X
X#define SYNTAX(msg) { fprintf(stderr, "Error parsing MyMenu.conf: %s\n"); \
X			return FALSE; }
X
X#ifdef AZTEC_C
X#define strcmp _BUILTIN_strcmp
X#define strcpy _BUILTIN_strcpy
X#define strlen _BUILTIN_strlen
X#endif
X
XFILE *conf;
Xchar tok[256], menustr[256], itemstr[256], substr[256];
X
Xextern UBYTE menu_pen;
X
Xmake_action(mi, typ, action)
X  struct ext_MenuItem *mi;
X  char typ, *action;
X{
X  register char *p, *index(), *rindex(), *copystr();
X
X  mi->args = mi->cmd = NULL;
X  if (typ=='c') {	/* CLI */
X    if (*action == '"') {
X      action++;
X      p = index(action, '"');
X    } else {
X      p = index(action, ' ');
X    }
X    if (p) {	/* if arguments */
X      *p++ = NULL;
X      mi->args = copystr(p);
X    }
X    mi->cmd = copystr(action);
X    mi->type = 'C';
X  } else if (typ=='w') {
X    if (*action=='"') {
X      action++;
X      p = rindex(action, '"');
X      *p = NULL;
X    }
X    mi->cmd = copystr(action);
X    mi->type = 'W';
X  }
X}
X
Xchar get_token()
X{
X  char quote;
X  register char c, *p;
Xretry:
X  c = fgetc(conf);
X  while(isspace(c)) c=fgetc(conf);	/* skip extra spaces */
X  if (c=='#') {				/* comment */
X    while((c=fgetc(conf))!='\n' && c!=EOF);
X    goto retry;
X  }
X  if (!isalnum(c) && c!='"')
X    return c;
X	/* scan string */
X  if (c=='"') {
X    c = fgetc(conf);
X    quote = TRUE;
X  } else
X    quote = FALSE;
X  p = tok;
X  do {
X    if ((quote && c=='"') || (!quote && isspace(c)))
X      break;
X    if (c=='\\')
X      c = fgetc(conf);
X    if (isspace(c))
X      c = ' ';
X    *p++ = c;
X  } while ((c=fgetc(conf))!=EOF);
X  *p = NULL;
X  return STR;
X}
X
Xint parse_conf() {
X  register char t;
X  char *p, c, flag, cmd;
X  struct ext_MenuItem *mi;
X    
X  while ((t=get_token()) != EOF) {
X    if (t==STR) {
X      if (stricmp(tok, "MENU")==0) {
X        cmd = NULL;
X        if ((t=get_token())=='<') {	/* command char */
X	  cmd = fgetc(conf);
X	  if (get_token() != '>')
X	    SYNTAX("Missing closing'>'");
X	  t=get_token();
X	}
X        if (t==STR)
X	  strcpy(menustr, tok);
X	else
X	  SYNTAX("Missing menu name");
X        if (get_token()==STR)
X	  strcpy(itemstr, tok);
X	else
X          SYNTAX("Missing menu item name");
X	if ((t=get_token())==STR) {
X	  strcpy(substr, tok);
X          t=get_token();
X	} else {
X	  substr[0] = NULL;
X	}
X	if (t != '|')
X	  SYNTAX("Missing '|' separator");
X	mi = add_menu(menustr, itemstr, substr, cmd);
X	if (get_token() != STR)
X	  SYNTAX("Syntax error after '|'");
X		/* find out type */
X	if (stricmp(tok, "CLI")==0)
X	  flag = 'c';
X	else if (stricmp(tok, "WB")==0)
X	  flag = 'w';
X	else
X	  flag = NULL;
X		/* read in command (rest of line */
X	p = tok;
X	while ((c=fgetc(conf)) != '\n' && c!=EOF)
X	  *p++ = c;
X	*p = NULL;
X	make_action(mi, flag, tok);
X      } else if (stricmp(tok, "COLOR")==0) {
X        if ((t=get_token())!=STR)
X          SYNTAX("Expected number after COLOR keyword");
X        menu_pen = (UBYTE)atoi(tok);
X      } else
X        SYNTAX("Didn't find keyword");
X    } else
X      SYNTAX("Didn't find keyword");
X  }
X  return TRUE;
X}
X
Xint parse_menus() {
X  int stat;
X  conf = fopen("S:MyMenu.conf", "r");
X  if (conf==NULL) {
X    conf = fopen("MyMenu.conf", "r");
X    if (conf==NULL) {
X      fprintf(stderr, "Can't open MyMenu.conf!\n");
X      return FALSE;
X    }
X  }
X  start_menu();
X  menu_pen = 2;
X  stat = parse_conf();
X  end_menu();
X  if (conf)
X    fclose(conf);
X  return stat;
X}
SHAR_EOF
echo "extracting README"
sed 's/^X//' << \SHAR_EOF > README
XMyMenu is a program to allow you to create your own menus in the WorkBench
Xto run your own commands.  This can save the hassle of opening up lots
Xof drawers to get to the command you want.  MyMenu will allow you to execute
Xboth CLI and WorkBench programs, and is configured with a normal text
Xfile.  Hopefully, this program will be obsolete when WorkBench 1.4
Xis released, since I hear this capability may be added.
X
XSee the MyMenu.DOC file for complete information.
X
XThis is the first release, so there are bound to be a few bugs.  Let me
Xknow about them.
X
X
XDarin Johnson
Xdarin@laic.UUCP
X{leadsv!laic!darin@pyramid.pyramid.com}
SHAR_EOF
echo "extracting newbugs"
sed 's/^X//' << \SHAR_EOF > newbugs
X- These are relatively minor, but annoying ...
X
X  In order to run a WorkBench program, there has to be a .info file
X  for the tool and/or project.  This means that XIcon and IconX
X  can't be used as is.  However, Execute can be used in their place.
X  
X  If you create a NewCLI or use Execute, you will get an empty
X  path, and the current directory will be the initial boot device.
X  The same sort of problems occur with DMouse and other programs.
SHAR_EOF
echo "End of archive 1 (of 1)"
# if you want to concatenate archives, remove anything after this line
exit