/* Copyright 1996 Acorn Computers Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * ctl.c
 *
 * User interface Control file handling.
 *
 * Author: Ran Mokady.
 *
 * History:
 *         09-Feb-90 Linked into Risc_OSLib.
 *
 */


#include <stdio.h>
#include <string.h>

#include <stdlib.h>
#include <ctype.h>

#include "kernel.h"
#include "swis.h"
#include "msgs.h"
#include "wimp.h"
#include "wimpt.h"
#include "win.h"
#include "werr.h"
#include "event.h"
#include "res.h"
#include "menu.h"
#include "dbox.h"
#include "akbd.h"
#include "template.h"
#include "colourmenu.h"

#include "ctl.h"

#ifdef LOG
 static FILE *logfile;
#endif

#ifndef Wimp_GetMenuState
#define Wimp_GetMenuState 0x400f4
#endif


#define MAX(a,b) a>b?a:b
#define MIN(a,b) a<b?a:b


/****************************** Data types *********************************/

typedef enum {
        ctl_ACT_FUNCTION = 1,
        ctl_ACT_STRING   = 2,
        ctl_ACT_LABEL    = 3
} ctl__actiontype;


typedef enum {
        ctl_TOK_STRING     =  -4,
        ctl_TOK_NUMBER     =  -3,
        ctl_TOK_ACTION     =  -2,
        ctl_TOK_UNKNOWN    =  -1,
        ctl_TOK_EOF        =   0,
        ctl_TOK_CTL        =   1,
        ctl_TOK_MENU       =   2,
        ctl_TOK_ENTRY      =   3,
        ctl_TOK_ENDMENU    =   4,
        ctl_TOK_ONOPEN     =   5,
        ctl_TOK_ONCLOSE    =   6,
        ctl_TOK_DBOX       =   7,
        ctl_TOK_ICON       =   8,
        ctl_TOK_ENDDBOX    =   9,
        ctl_TOK_ONKEY      =  10,
        ctl_TOK_EOL        =  11,
        ctl_TOK_OPENBLOCK  =  12,
        ctl_TOK_CLOSEBLOCK =  13,
        ctl_TOK_ENDCTL     =  14,
        ctl_TOK_SUBMENU    =  15,
        ctl_TOK_COMMENT    =  16,
        ctl_TOK_WARNING    =  17,
        ctl_TOK_ONREOPEN   =  18,
        ctl_TOK_TEMPLATE   =  19,
        ctl_TOK_HELP       =  20,
        ctl_TOK_TOKEN      =  21,
        ctl_TOK_HELPACTION =  22,
        ctl_TOK_COLOURMENU =  23,
        ctl_TOK_EXTERNAL   =  24,
        ctl_TOK_NOREOPEN   =  25,
        ctl_TOK_ONCLICK    =  26
} ctl__token;


typedef enum {
        ctl_ST_PRELOAD   = 0,
        ctl_ST_LOADING   = 1,
        ctl_ST_LOADED    = 2,
        ctl_ST_CTLDEF    = 3,
        ctl_ST_MENUDEF   = 4,
        ctl_ST_DBOXDEF   = 5,
        ctl_ST_GETACTION = 6,
        ctl_ST_GETACTIONBLOCK = 7
} ctl__state;

typedef struct ctl__actstr {
        char *name;
        char *syntax;
        BOOL internal;
        BOOL disabled;
        struct{
              ctl_action_proc function;
              void            *handle;
             }function;
        struct ctl__actstr *next;
}ctl__actionstr;

typedef struct ctl__act {
        ctl__actiontype type;
        union
        {
         char           *name;
         ctl__actionstr *address;
        } action;
        char            *args;
        int             line;
        int             nest;
        struct ctl__act *next;
}ctl__action;

typedef union
{
 ctl__action action;
 int         num;
 char        *string;
}ctl__tokenstr;

typedef struct ctl__mentry {
        int          num;
        int          disabled;
        ctl__action  *action;
        ctl__action  *help_action;
        struct ctl__mentry *next;
} ctl__menu_entry;

typedef struct ctl__mnu {
        char *name;
        ctl__action *onopen;
        ctl__action *onreopen;
        ctl__menu_entry *entries;
        menu menu_body;
        char *mih_token;
        int  entrycount;
        BOOL external;
        wimp_menuitem *parent;
        struct ctl__mnu *next;
} ctl__menu;

typedef struct ctl__icn {
        int num;
        int disabled;
        BOOL ticked;
        BOOL writeable; /*for possible future error chk when dbox is opened*/
        ctl__action *action;
        ctl__action *help_action;
        struct ctl__icn *next;
} ctl__icon;

typedef union {
        ctl__menu_entry *menu_entry;
        ctl__icon       *icon;
}ctl__entry;


typedef struct ctl__dbx {
        char *name;
        char *template;
        ctl__action *onopen;
        ctl__action *onreopen;
        ctl__action *onclose;
        ctl__icon   *icons;
        dbox        dbox_body;
        char        *mih_token;
        int         iconcount;
        BOOL        displayed;
        struct ctl__dbx *next;
}ctl__dbox;

typedef union {
        ctl__menu *parent_menu;
        ctl__dbox *parent_dbox;
}ctl__parent;

typedef struct ctl_ky {
        int keycode;
        ctl__action *action;
        struct ctl_ky *next;
        BOOL disabled;
}ctl__key;

typedef struct ctl_ctl {
        char *name;
        ctl__menu   *menus;
        ctl__dbox   *dboxes;
        ctl__key    *keys;
        ctl__action *onclick;
        struct ctl_ctl *next;
}ctl;

typedef struct hndl
{
 event_w     w;
 void        *user_handle;
 ctl         *the_ctl;
 ctl__menu   *mnu;
 ctl__dbox   *the_dbox;
 char        *help_token;
 struct hndl *next;
}ctl__handlestr;

typedef struct
{
 char *name;
 int  code;
} ctl__keyvalstr;

typedef struct {
        ctl__state     state;
        ctl            *ctls;
        ctl__actionstr *actions;
        ctl__handlestr *handles;
        ctl__handlestr *current_handle;
        ctl__menu      *current_menu;
        ctl__menu      *top_menu;
        ctl__dbox      *current_dbox;
        int            current_char;
        int            current_icon;
        FILE           *file;
        int            line;
        int            nest;
        int            return_code;
        char           last_menu_hit[30];
        char           external_hit[30];
        int            skip;
} ctl__statestr;

#define Argbuf_LEN 256

typedef union
{
 int  words[Argbuf_LEN];
 char text[Argbuf_LEN*4];
} ctl__argbuf;


typedef struct
{
 char *buffer;
 int  len;
 char *valid;
 int  flag;
}ctl__indirectstr;

/*************************** Constants ************************************/

static ctl__keyvalstr ctl__keynames(int indx)
{
 ctl__keyvalstr tmp;
 tmp.name="ENDLIST";
 tmp.code=-1;
 switch (indx)
 {
   case  0: tmp.name="<f1>";       tmp.code=   akbd_Fn+1; break;
   case  1: tmp.name="<f2>";       tmp.code=   akbd_Fn+2; break;
   case  2: tmp.name="<f3>";       tmp.code=   akbd_Fn+3; break;
   case  3: tmp.name="<f4>";       tmp.code=   akbd_Fn+4; break;
   case  4: tmp.name="<f5>";       tmp.code=   akbd_Fn+5; break;
   case  5: tmp.name="<f6>";       tmp.code=   akbd_Fn+6; break;
   case  6: tmp.name="<f7>";       tmp.code=   akbd_Fn+7; break;
   case  7: tmp.name="<f8>";       tmp.code=   akbd_Fn+8; break;
   case  8: tmp.name="<f9>";       tmp.code=   akbd_Fn+9; break;
   case  9: tmp.name="<f10>";      tmp.code=   akbd_Fn10; break;
   case 10: tmp.name="<f11>";      tmp.code=   akbd_Fn11; break;
   case 11: tmp.name="<f12>";      tmp.code=   akbd_Fn12; break;
   case 12: tmp.name="<Print>";    tmp.code=   akbd_Fn+0; break;
   case 13: tmp.name="<Esc>";      tmp.code=          27; break;
   case 14: tmp.name="<Insert>";   tmp.code=akbd_InsertK; break;
   case 15: tmp.name="<Home>";     tmp.code=          30; break;
   case 16: tmp.name="<PageUp>";   tmp.code=akbd_PageUpK; break;
   case 17: tmp.name="<PageDown>"; tmp.code=akbd_PageDownK; break;
   case 18: tmp.name="<Delete>";   tmp.code=         127; break;
   case 19: tmp.name="<<-|>";      tmp.code=           8; break;
   case 20: tmp.name="<Return>";   tmp.code=          13; break;
   case 21: tmp.name="<Up>";       tmp.code=    akbd_UpK; break;
   case 22: tmp.name="<Down>";     tmp.code=  akbd_DownK; break;
   case 23: tmp.name="<Left>";     tmp.code=  akbd_LeftK; break;
   case 24: tmp.name="<Right>";    tmp.code=    akbd_UpK; break;
   case 25: tmp.name="<Tab>";      tmp.code=   akbd_TabK; break;
   case 26: tmp.name="<Break>";    tmp.code=          27; break;
   case 27: tmp.name="<Copy>";     tmp.code=  akbd_CopyK; break;
   case 28: tmp.name="<Default>";  tmp.code=          -1; break;
   case 29: tmp.name="ENDLIST";    tmp.code=          -1; break;
  }
  return tmp;
}

static char * ctl__tokens(int indx)
{
 switch (indx)
 {
   case  0: return "ctl";
   case  1: return "menu";
   case  2: return "entry";
   case  3: return "endmenu";
   case  4: return "onopen";
   case  5: return "onclose";
   case  6: return "dbox";
   case  7: return "icon";
   case  8: return "enddbox";
   case  9: return "onkey";
   case 10: return "\n";
   case 11: return "{";
   case 12: return "}";
   case 13: return "endctl";
   case 14: return "submenu";
   case 15: return "#";
   case 16: return "warning";
   case 17: return "onreopen";
   case 18: return "template";
   case 19: return "help";
   case 20: return "token";
   case 21: return "action";
   case 22: return "colourmenu";
   case 23: return "external";
   case 24: return "noreopen";
   case 25: return "onclick";
   case 26: return "ENDLIST";
 }
 return "ENDLIST";
}

/*************************** Variables ************************************/

static ctl__statestr *ctls=NULL;



/******************************* Errors ***********************************/

static char * ctl__errtxt(int num)
{
 switch (num)
 {
 case 0:
 return "ctsyntx:Syntax error in Control file at line %i: %s";    /* 0 */
 case 1:
 return "ctnosp:Control: Not enough space.";                      /* 1 */
 case 2:
 return "ctinit:ctl_init called twice.";                          /* 2 */
 case 3:
 return "ctdupact:Duplicate definision for action %s.";           /* 3 */
 case 4:
 return "ctdupld:ctl_load called twice.";                         /* 4 */
 case 5:
 return "ctnofil:Unable to open Control file.";                   /* 5 */
 case 6:
 return "ctlngth:Token too long.";                                /* 6 */
 case 7:
 return "cteof:Expected arguments but found EOF.";                /* 7 */
 case 8:
 return "ctlnlng:Line too long.";                                 /* 8 */
 case 9:
 return "ctunxeof:Unexpected EOF in Control file.";               /* 9 */
 case 10:
 return "ctunxctl:Ctl not at top level.";                         /* 10 */
 case 11:
 return "ctmisctln:Ctl name not found.";                          /* 11 */
 case 12:
 return "ctunxmnu:Menu not in ctl top level.";                    /* 12 */
 case 13:
 return "ctmismnam:Menu name not found.";                         /* 13 */
 case 14:
 return "ctunxono:Onopen not in Menu or Dbox.";                   /* 14 */
 case 15:
 return "ctdupono:Duplicate Onopen in Menu or Dbox %s.";          /* 15 */
 case 16:
 return "ctunxopb:command expected but found '{'";                /* 16 */
 case 17:
 return "ctunxedbx:enddbox not in dbox.";                         /* 17 */
 case 18:
 return "ctunxemnu:endmenu not in menu.";                         /* 18 */
 case 19:
 return "ctunxectl:endctl not in ctl.";                           /* 19 */
 case 20:
 return "ctunxeblk:unexpected '}'.";                              /* 20 */
 case 21:
 return "ctunxeact:command expected but found action.";           /* 21 */
 case 22:
 return "ctunxestr:command expected but found string.";           /* 22 */
 case 23:
 return "ctundfact:Undefined action.";                            /* 23 */
 case 24:
 return "ctunxent:entry not in menu.";                            /* 24 */
 case 25:
 return "ctbadent:bad entry number.";                             /* 25 */
 case 26:
 return "ctbadentn:invalid entry number (n<1 or n>last entry).";  /* 26 */
 case 27:
 return "ctdupent:duplicate entry number.";                       /* 27 */
 case 28:
 return "ctunxnst:'}' found at top level.";                       /* 28 */
 case 29:
 return "ctuexpnst:'}' expected.";                                /* 29 */
 case 30:
 return "ctunxdbx:dbox not in ctl top level.";                    /* 30 */
 case 31:
 return "ctmisdbxn:dbox name not found.";                         /* 31 */
 case 32:
 return "ctbaddbx:Unable to create dbox %s.";                     /* 32 */
 case 33:
 return "ctunxonc:onclose not in dbox.";                          /* 33 */
 case 34:
 return "ctduponc:duplicate onclose in dbox %s.";                 /* 34 */
 case 35:
 return "ctunxicn:icon not in dbox.";                             /* 35 */
 case 36:
 return "ctbadicn:bad icon number.";                              /* 36 */
 case 37:
 return "ctbadicnn:invalid icon number (n<0 or n>last icon).";    /* 37 */
 case 38:
 return "ctdupicn:duplicate icon number.";                        /* 38 */
 case 39:
 return "ctunxkey:onkey not in ctl top level.";                   /* 39 */
 case 40:
 return "ctbadkey:bad key name.";                                 /* 40 */
 case 41:
 return "ctdupkey:duplicate hot key.";                            /* 41 */
 case 42:
 return "ctctlmis:Ctl %s not defined.";                           /* 42 */
 case 43:
 return "ctunksubm:Unknown submenu type.";                        /* 43 */
 case 44:
 return "ctunkact:Unknown action %s";                             /* 44 */
 case 45:
 return "ctquoeof:EOF found in quoted string.";                   /* 45 */
 case 46:
 return "ctquoeol:EOL found in quoted string.";                   /* 46 */
 case 47:
 return "ctunsmnu:Dbox menus not supported by this wimp version.";/* 47 */
 case 48:
 return "ctnotact:'%s' is not an action.";                        /* 48 */
 case 49:
 return "ctnotact:dbox %s already displayed.";                    /* 49 */
 case 50:
 return "ctnotact:Unknown action %s in enable/disable action.";   /* 50 */
 case 51:
 return "ctnotact:Unknown action %s in make action writeable.";   /* 51 */
 case 52:
 return "ctunxtmp:Template not in dbox.";                         /* 52 */
 case 53:
 return "ctmistmpn:Template %s not found.";                       /* 53 */
 case 54:
 return "ctmistmp:missing template name.";                        /* 54 */
 case 55:
 return "ctmistmpd:No template defined for dbox  %s.";            /* 55 */
 case 56:
 return "ctduptmp:Duplicate template defined for dbox  %s.";      /* 56 */
 case 57:
 return "ctmismnut:Missing menu title.";                          /* 57 */
 case 58:
 return "ctmistbdy:Missing menu body.";                           /* 58 */
 case 59:
 return "ctmistmpd:Help not in menu / dbox.";                     /* 59 */
 case 60:
 return "ctmistmpd:Duplicate help token.";                        /* 60 */
 case 61:
 return "ctmistmpd:Missing help token.";                          /* 61 */
 case 62:
 return "ctmistmpd:Invalid help type.";                           /* 62 */
 case 63:
 return "ctdupcart:Duplicate entries for writeable action %s.";   /* 63 */
 case 64:
 return "ctdupcart:External menu %s not defined by application."; /* 64 */
 case 65:
 return "ctexpextm:Menu %s should be an external menu.";          /* 65 */
 case 66:
 return "ctdupcart:Main attached menu %s cannot be external.";    /* 66 */
 case 67:
 return "ctdbxndisp:Dbox %s not displayed in make writable.";     /* 67 */
 case 68:
 return "ctunxclk:onclick not in ctl top level.";                 /* 68 */
 case 69:
 return "ctdupclk:Duplicate onclick action.";                     /* 69 */
 case 70:
 return "ctunxels:else without if.";                              /* 70 */
 case 71:
 return "ctdupenth:duplicate help entry number.";                 /* 71 */
 }
 return "Internal error.";
}


static void ctl__syntax_error(int errnum,BOOL fatal)
{
 if (fatal) fclose(ctls->file);
 werr(fatal,msgs_lookup(ctl__errtxt(0)),ctls->line,
                             msgs_lookup(ctl__errtxt(errnum)));
}


static void ctl__error(int errnum,char *arg,BOOL fatal)
{
 if (fatal) fclose(ctls->file);
 if ((ctls==NULL) || (ctls->line==0))
          werr(fatal,msgs_lookup(ctl__errtxt(errnum)),arg);
 else
 {
  char errbuf[100];
  sprintf(errbuf,msgs_lookup("ctlat: %s in Control line %%i"),
             msgs_lookup(ctl__errtxt(errnum)));
  werr(fatal,errbuf,arg,ctls->line);
 }
}

/****************************** General Functions *************************/

static void *ctl__malloc(int size)
{
  void *result=malloc(size);
  if (result == NULL) ctl__error(1,NULL,TRUE);
  return result;
}

static void ctl__free(void *ptr)
{
 free(ptr);
}





 /*
  * ctl__iconcount(dbox *d)
  *
  * return number of icons in the dbox.
  *
  */


static int ctl__iconcount(dbox *d)
{
 wimp_i buffer[256];
 wimp_which_block blk;
 int i;
 blk.window=dbox_syshandle(*d);
 blk.bit_mask=0;
 blk.bit_set=0;
 wimp_which_icon(&blk,buffer);

 for (i=0;buffer[i]!=-1;i++);

 return i;
}

 /*
  * ctl__keynum(char *c)
  *
  * return number of a key from its name.
  *
  */


static int ctl__keynum(char *c)
{
 int shift=0,i=0,keynum;

 for (i=0;i<2;i++)
 {
  if (*c=='S')  { shift+=akbd_Sh; c++;  }
  if (*c=='C')  { shift+=akbd_Ctl; c++; }
 }

 /* now we have the shift factor and c points to the name */

 /* first try to find as special name */

 i=0;
 for (i=0;strcmp(ctl__keynames(i).name,"ENDLIST")!=0;i++)
 {
  if (strcmp(ctl__keynames(i).name,c)==0)
            return ctl__keynames(i).code+shift;
 }

 if (*(c+1)!='\0') return -1; /* More than one char must be bad name */

 keynum=*c;
 if (shift & akbd_Sh) { if ((keynum & 32)==0) return -1; else keynum&=~32; }
 if (shift & akbd_Ctl){ if ((keynum & 64)==0) return -1; else keynum&=~96; }
 return keynum;

}



static wimp_menuitem *ctl__menu_item(menu m,int entry)
{
 wimp_menuitem *item;
 item=(wimp_menuitem *)((int)menu_syshandle(m)+sizeof(wimp_menuhdr));
 return item+(entry-1);
}


static int ctl__count_menu_entries(wimp_menustr *m)
{
 int count=1;
 wimp_menuitem *item;
 item=(wimp_menuitem *)((int)m+sizeof(wimp_menuhdr));
 for (count=1;(item->flags & wimp_MLAST)==0;count++) item++;
 return count;
}


static void ctl__read_args(char *syntax,char *line,ctl__argbuf *buffer)
{
 _kernel_swi_regs r;
 _kernel_oserror  *e;

 r.r[0]=(int)syntax;
 r.r[1]=(int)line;
 r.r[2]=(int)buffer;
 r.r[3]=Argbuf_LEN*4;
 if (e=_kernel_swi(XOS_Bit | OS_ReadArgs,&r,&r),e!=NULL)
        werr(TRUE,msgs_lookup(ctl__errtxt(0)),ctls->line,e->errmess);
}

/* some things we need but need us. */


static void ctl__run(ctl__action *act);
static ctl__icon *ctl__find_icon(int num,ctl__dbox *a_dbox);
static BOOL ctl__process_event(wimp_eventstr *e);
static ctl__menu_entry *ctl__find_entry(int num,ctl__menu *a_menu);
static ctl__dbox *ctl__find_dbox(char *name,ctl *a_ctl);



/*************************** Internal action handlers**********************/


static ctl__handlestr *ctl__add_handle(event_w w,
                   void *userhandle,ctl *ct,ctl__menu *mnu)
{
 ctl__handlestr *a_handle;

#ifdef LOG
 fprintf(logfile,"New handle:\n");
#endif

 if (ctls->handles==NULL)
  a_handle = (ctls->handles=ctl__malloc(sizeof(ctl__handlestr)));
 else
 {
  for(a_handle=ctls->handles;
       (a_handle->next !=NULL) && (a_handle->w != w);
                                  a_handle=a_handle->next);
  if (a_handle->w!=w)
       a_handle = (a_handle->next=ctl__malloc(sizeof(ctl__handlestr)));
 }

 a_handle->w=w;
 a_handle->mnu=mnu;
 a_handle->the_ctl=ct;
 a_handle->user_handle=userhandle;
 a_handle->help_token=NULL;
 a_handle->next=NULL;
 a_handle->the_dbox=NULL;
 return a_handle;
}

static void ctl__remove_handle(event_w w)
{
 ctl__handlestr *a_handle,*tmp;

 if (ctls->handles==NULL) return;

 if (ctls->handles->w==w)
 {
  a_handle=ctls->handles->next;
  ctl__free(ctls->handles);
  ctls->handles=a_handle;
  return;
 }

 for(a_handle=ctls->handles;
        (a_handle->next != NULL) && (a_handle->next->w != w);
                                          a_handle=a_handle->next);
 if (a_handle->next==NULL) return;

 tmp=a_handle->next;
 a_handle->next=a_handle->next->next;
 ctl__free(tmp);
}

static ctl__handlestr *ctl__find_handle(event_w w)
{
 ctl__handlestr *a_handle;

 for(a_handle=ctls->handles;a_handle!=NULL;a_handle=a_handle->next)
      if (a_handle->w==w)  return a_handle;

 return NULL;
}

static void ctl__dbox_event_handler(dbox d,ctl__dbox *a_dbox)
{
 int inum=ctls->current_icon;
 ctl__icon *icon;
 d=d;

 if ((inum==-1) && (wimpt_last_event()->e==wimp_EKEY)) return;

 if ((inum==-1) && (wimpt_last_event()->e==wimp_EBUT) &&
                  (inum=wimpt_last_event()->data.but.m.i,inum<0))
                                                            return;           if (inum==-1) return;

 icon=ctl__find_icon(inum,a_dbox);
 ctls->return_code=inum;

 if (icon!=NULL) ctl__run(icon->action);
    else if (icon=ctl__find_icon(-1,a_dbox),icon!=NULL)
            ctl__run(icon->action);

}

static BOOL ctl__dbox_raw_event_handler(dbox d,wimp_eventstr *e,ctl__dbox *handle)
{
 wimp_eventstr ev;
 wimp_caretstr c,c1;
 ctls->current_icon=-1;
 if ((e->e==wimp_EKEY) && (e->data.key.chcode==13))
 {
  wimp_get_caret_pos(&c);
  ctls->current_icon=e->data.key.c.i;
  ctl__dbox_event_handler(d,handle);
  wimp_get_caret_pos(&c1);
  if ((c.w==c1.w) && (c.i==c1.i))
  {
   ctls->current_icon=-1;
   return FALSE;
  } else return TRUE;
 }
 else if ((e->e==wimp_ESEND) || (e->e==wimp_ESENDWANTACK))
         return (ctl__process_event(e));
 else if (e->e!=wimp_EBUT) return FALSE;
 ctls->current_icon=-1;
 if (e->data.but.m.bbits!=2) return FALSE;
 ctls->current_icon=e->data.but.m.i;
 ev.e=wimp_ESEND;
 ev.data.msg.hdr.action=wimp_MMENUWARN;
 ev.data.msg.data.words[1]=e->data.but.m.x;
 ev.data.msg.data.words[2]=e->data.but.m.y;
 wimpt_fake_event(&ev);
 wimpt_poll(0,&ev);
 ctl__dbox_event_handler(d,handle);
 return TRUE;
}

static int ctl__quit_action(void *handle,void *type,void *arg,int rc)
{
 handle=handle;
 type=type;
 arg=arg;
 exit(0);
 return rc;
}

static int ctl__showdbox(void *handle,void *type,void *arg,int rc)
{
 ctl__argbuf *buf=(ctl__argbuf *)arg;
 char dbox_name[20];
 int len,i;
/* wimp_eventstr e; */
 wimp_mousestr m;
 ctl__dbox *a_dbox;
 dbox d;
 BOOL flag;
 wimp_icon icn;
 ctl__icon *icon;

 handle=handle;
 type=type;

 wimp_create_menu((wimp_menustr *)-1,0,0);

 len=*(char *)(buf->words[0]);
 strncpy(dbox_name,(char *)(buf->words[0])+2,MIN(19,len));
 dbox_name[MIN(len,20)]='\0';

 a_dbox=ctl__find_dbox(dbox_name,ctls->current_handle->the_ctl);

 if (a_dbox->displayed)
 {
  ctl__error(49,a_dbox->name,FALSE);
  return 0;
 }
 d=dbox_new(a_dbox->template);
 if (d==NULL) ctl__error(32,a_dbox->name,TRUE);
 icon=ctl__find_icon(-1,a_dbox);

 /* reflect state of actions on the icons. */
 for (i=0;i<a_dbox->iconcount;i++)
 {
  wimp_get_icon_info(dbox_syshandle(d),i,&icn);
  if ((icon!=NULL) || ((icn.flags & (0xf<<12))==wimp_BIGNORE))
              dbox_unfadefield(d,i);
      else dbox_fadefield(d,i);
 }

 for (icon=a_dbox->icons;icon!=NULL;icon=icon->next)
 {
  dbox_unfadefield(d,icon->num);
  if (icon->ticked)
   wimp_set_icon_state(
        dbox_syshandle(d),icon->num,wimp_ISELECTED,wimp_ISELECTED);
  else wimp_set_icon_state(dbox_syshandle(d),
                                icon->num,0,wimp_ISELECTED);
  if (icon->disabled)
  wimp_set_icon_state(
        dbox_syshandle(d),icon->num,wimp_INOSELECT,wimp_INOSELECT);
  else wimp_set_icon_state(dbox_syshandle(d),
                                icon->num,0,wimp_INOSELECT);
 }

 dbox_raw_eventhandler(d,
         (dbox_raw_handler_proc) ctl__dbox_raw_event_handler,
         a_dbox);
 dbox_eventhandler(d,
       (dbox_handler_proc) ctl__dbox_event_handler,a_dbox);

 a_dbox->displayed=TRUE;
 a_dbox->dbox_body=d;

 ctls->current_dbox=a_dbox;

 if (a_dbox->mih_token!=NULL)
 {
   ctl__handlestr *handle=
         ctl__add_handle(dbox_syshandle(d),(void *) NULL,NULL,NULL);
   handle->help_token=a_dbox->mih_token;
   handle->the_ctl=NULL;
   handle->the_dbox=a_dbox;
 }

 ctl__run(a_dbox->onopen);

 wimp_get_point_info(&m);

 dbox_show(d);

 do
 {
  ctls->current_icon=-1;
  if (dbox_fillin(d)==-1) break;
  flag=dbox_persist();
  if (flag) ctl__run(a_dbox->onreopen);
 } while (flag);

 ctl__run(a_dbox->onclose);

 dbox_eventhandler(d,(dbox_handler_proc)0,a_dbox);
 dbox_raw_eventhandler(d,(dbox_raw_handler_proc) 0,a_dbox);

 a_dbox->displayed=FALSE;

 if (a_dbox->mih_token!=NULL)
     ctl__remove_handle(dbox_syshandle(d));

 dbox_dispose(&d);

 ctls->current_dbox=NULL;

 return rc;
}


static int ctl__if_action(void *handle,void *type,void *arg,int rc)
{
 ctl__argbuf *buf=(ctl__argbuf *)arg;
 handle=handle;
 type=type;
 arg=arg;
 if (rc!=*((int *) ( (char *) (buf->words[0])+1) ) )
 {
  ctls->skip=-2;
 } else ctls->skip=-3;
 return rc;
}

static int ctl__else_action(void *handle,void *type,void *arg,int rc)
{
 handle=handle;
 type=type;
 arg=arg;
 if (ctls->skip==-1) ctl__syntax_error(70,TRUE);
 if (ctls->skip==-3) ctls->skip=-2; else ctls->skip=-1;
 return rc;
}

static int ctl__error_action(void *handle,void *type,void *arg,int rc)
{
 handle=handle;
 type=type;
 werr(0,(char *)arg);
 return rc;
}
static int ctl__oscli_action(void *handle,void *type,void *arg,int rc)
{
 handle=handle;
 type=type;
 arg=arg;
 _kernel_oscli((char *)arg);
 return rc;
}


static int ctl__number_action(void *handle,void *type,void *arg,int status)
{
 handle=handle;
 type=type;
 status=status;
 ctls->return_code=(int)arg;
 return (int)arg;
}

static int ctl__processkey_action(void *handle,void *type,void *arg,int status)
{
 handle=handle;
 type=type;
 arg=arg;
 wimp_processkey(status);
 ctls->return_code=status;
 return status;
}

static int ctl__submenu_request(void *handle,void *type,void *arg,int status)
{
 int i,stat;
 ctl__icon *icon;

 stat=(int)type;

 handle=handle;
 status=status;

 if (wimpt_last_event()->e!=wimp_ESEND) return status;

 switch ((int)type)
 {
  case 1:
  case 3:
         ctl__run(((ctl__menu *)arg)->onopen);
         if ((((ctl__menu *)arg)->external) &&
                 (((ctl__menu *)arg)->menu_body==NULL))
                   ctl__error(64,((ctl__menu *)arg)->name,TRUE);

         if (((ctl__menu *)arg)->external)
         {
            ((ctl__menu *)arg)->parent->submenu=
                     (wimp_menustr *)(((ctl__menu *)arg)->menu_body);
            wimp_create_submenu(
            (wimp_menustr *)(((ctl__menu *)arg)->menu_body),
            wimpt_last_event()->data.msg.data.words[1],
            wimpt_last_event()->data.msg.data.words[2]);
         }
         else wimp_create_submenu(
            menu_syshandle(((ctl__menu *)arg)->menu_body),
            wimpt_last_event()->data.msg.data.words[1],
            wimpt_last_event()->data.msg.data.words[2]);

         ctls->current_menu=(ctl__menu *)arg;
         break;
  case 2:
  case 4:
         {
          dbox d;
          BOOL flag;
          wimp_icon icn;
          ctl__handlestr *handle;
          ctl__dbox *a_dbox=(ctl__dbox *)arg;
          if (a_dbox->displayed)
          {
           ctl__error(49,a_dbox->name,FALSE);
           return 0;
          }
          d=dbox_new(a_dbox->template);
          if (d==NULL) ctl__error(32,a_dbox->name,TRUE);
          icon=ctl__find_icon(-1,a_dbox);
          /* reflect state of actions on the icons. */
          for (i=0;i<a_dbox->iconcount;i++)
          {
           wimp_get_icon_info(dbox_syshandle(d),i,&icn);
           if ((icon!=NULL) || ((icn.flags & (0xf<<12))==wimp_BIGNORE))
                       dbox_unfadefield(d,i);
               else dbox_fadefield(d,i);
          }

          for (icon=a_dbox->icons;icon!=NULL;icon=icon->next)
          {
           dbox_unfadefield(d,icon->num);
           if (icon->ticked)
            wimp_set_icon_state(
                 dbox_syshandle(d),icon->num,wimp_ISELECTED,wimp_ISELECTED);
           else wimp_set_icon_state(dbox_syshandle(d),
                                         icon->num,0,wimp_ISELECTED);
           if (icon->disabled)
           wimp_set_icon_state(
                 dbox_syshandle(d),icon->num,wimp_INOSELECT,wimp_INOSELECT);
           else wimp_set_icon_state(dbox_syshandle(d),
                                         icon->num,0,wimp_INOSELECT);
          }
          dbox_raw_eventhandler(d,
                  (dbox_raw_handler_proc) ctl__dbox_raw_event_handler,
                  a_dbox);
          dbox_eventhandler(d,
                (dbox_handler_proc) ctl__dbox_event_handler,a_dbox);
          a_dbox->displayed=TRUE;
          a_dbox->dbox_body=d;
          ctls->current_dbox=a_dbox;
          handle=ctl__add_handle(dbox_syshandle(d),(void *) NULL,NULL,NULL);
          handle->help_token=a_dbox->mih_token;
          handle->the_ctl=NULL;
          ctl__run(a_dbox->onopen);
          dbox_show(d);
          do
          {
           ctls->current_icon=-1;
           if (dbox_fillin(d)==-1) break;
           flag=dbox_persist();
           if (flag)
           {
            ctl__menu *mnu=ctls->current_handle->mnu;
            ctl__menu_entry *entry;
            char *c;
            for (c=ctls->last_menu_hit;*(c+1);c++)
            {
             ctl__run(mnu->onreopen);
             entry=ctl__find_entry(*c,mnu);
             if (entry==NULL) entry=ctl__find_entry(-1,mnu);
             mnu=(ctl__menu *) entry->action->args;
            }
            ctl__run(mnu->onreopen);
            if (!mnu->external)
               ctl__menu_item(mnu->menu_body,*c)->submenu=
              ((a_dbox->onreopen)==(ctl__action *)-1) ?
                    (wimp_menustr *) 1 :
                    (wimp_menustr *) dbox_syshandle(d);
            else
              ((wimp_menuitem *)
               (((wimp_menustr *)mnu->menu_body)+1)+*c-1)
                          ->submenu=
              ((a_dbox->onreopen)==(ctl__action *)-1) ?
                    (wimp_menustr *) 1 :
                    (wimp_menustr *) dbox_syshandle(d);
            wimp_create_menu(
                 menu_syshandle(ctls->current_handle->mnu->menu_body),0,0);
            ctl__run(((ctl__dbox *)arg)->onreopen);
           }
          } while (flag);
          ctl__run(a_dbox->onclose);
          dbox_eventhandler(d,(dbox_handler_proc)0,a_dbox);
          dbox_raw_eventhandler(d,(dbox_raw_handler_proc) 0,a_dbox);
          a_dbox->displayed=FALSE;
          ctl__remove_handle(dbox_syshandle(d));
          dbox_dispose(&d);
          ctls->current_dbox=NULL;
         }
         break;
  default:
         break;
 }

 return status;

}


/*************************** Internal functions ***************************/



/*
 * ctl__find_ctl(char *name):
 *
 * find named ctl.
 *
 */

static ctl *ctl__find_ctl(char *name)
{
 ctl *a_ctl;

 for (a_ctl=ctls->ctls;a_ctl!=NULL;a_ctl=a_ctl->next)
   if (strcmp(a_ctl->name,name)==0) return a_ctl;

 ctl__error(42,name,TRUE);

 /* NEVER */
 return NULL;
}

/*
 * ctl__find_menu(char *name,ctl *a_ctl):
 *
 * find named menu in given ctl.
 *
 */

static ctl__menu *ctl__find_menu(char *name,ctl *a_ctl)
{
 ctl__menu *a_menu;

 for (a_menu=a_ctl->menus;a_menu!=NULL;a_menu=a_menu->next)
   if (strcmp(a_menu->name,name)==0) return a_menu;

 if (ctls->line==0)
          werr(TRUE,msgs_lookup("mismnu:Menu %s mot defined in Ctl %s.")
               ,name,a_ctl->name);
 else
      werr(TRUE,
        msgs_lookup("mismnu1:Menu %s mot defined in Ctl %s. at line %i.")
             ,name,a_ctl->name,ctls->line);


 /* NEVER */
 return NULL;
}

/*
 * ctl__find_dbox(char *name,ctl *a_ctl):
 *
 * find named dbox in given ctl.
 *
 */

static ctl__dbox *ctl__find_dbox(char *name,ctl *a_ctl)
{
 ctl__dbox *a_dbox;

 for (a_dbox=a_ctl->dboxes;a_dbox!=NULL;a_dbox=a_dbox->next)
   if (strcmp(a_dbox->name,name)==0) return a_dbox;

 if (ctls->line==0)
          werr(TRUE,msgs_lookup("misdbx:Dbox %s mot defined in Ctl %s.")
               ,name,a_ctl->name);
 else
      werr(TRUE,
        msgs_lookup("misdbx1:Dbox %s mot defined in Ctl %s. at line %i.")
             ,name,a_ctl->name,ctls->line);


 /* NEVER */
 return NULL;
}

/*
 * ctl__find_entry(int num,ctl__menu *a_menu):
 *
 * find an entry in given menu return NULL if not found.
 *
 */

static ctl__menu_entry *ctl__find_entry(int num,ctl__menu *a_menu)
{
 ctl__menu_entry *an_entry;

 for (an_entry=a_menu->entries;an_entry!=NULL;an_entry=an_entry->next)
   if (an_entry->num==num) return an_entry;

 return NULL;
}

/*
 * ctl__find_icon(int num,ctl__dbox *a_dbox):
 *
 * find an icon in given dbox return NULL if not found.
 *
 */

static ctl__icon *ctl__find_icon(int num,ctl__dbox *a_dbox)
{
 ctl__icon *an_icon;

 for (an_icon=a_dbox->icons;an_icon!=NULL;an_icon=an_icon->next)
   if (an_icon->num==num) return an_icon;

 return NULL;
}




static ctl__actionstr *ctl__find_action(ctl__actionstr *action,
                             ctl__actionstr *list,BOOL add,BOOL internal)
{
 if (add && ctls->actions==NULL)
 {
#ifdef LOG
 fprintf(logfile,"New action %s:\n",action->name);
#endif
  ctls->actions=(ctl__actionstr *)ctl__malloc(sizeof(ctl__actionstr));
  *ctls->actions=*action;
  return NULL;
 }

 if (list==NULL) return NULL;

 else if ( (*(list->name)==*(action->name)) &&
           (strcmp(list->name,action->name)==0) &&
           (internal || !list->internal)) return list;

 if (add && (list->next==NULL))
 {
#ifdef LOG
 fprintf(logfile,"New action %s:\n",action->name);
#endif
  list->next=(ctl__actionstr *)ctl__malloc(sizeof(ctl__actionstr));
  *list->next=*action;
  return NULL;
 }

 return ctl__find_action(action,list->next,add,internal);
}



static void ctl__add_action(char *name,char *syntax,ctl_action_proc func,void *arg,
                     BOOL internal)
{
 ctl__actionstr act;

 act.name=name;
 act.syntax=syntax;
 act.next=NULL;

  act.function.function=func;
  act.function.handle=arg;
  act.internal=internal;
  act.disabled=FALSE;

 if (ctl__find_action(&act,ctls->actions,TRUE,internal)!=NULL)
                 ctl__error(3,name,TRUE);
}

static void ctl__init(void)
{
 if (ctls!=NULL) ctl__error(2,NULL,TRUE);
#ifdef LOG
logfile=fopen("adfs::0.$.ctllog","w");
fprintf(logfile,"Init:\n");
#endif
 ctls=(ctl__statestr *)ctl__malloc(sizeof(ctl__statestr));
                                                     /* Space for state */
 ctls->state=ctl_ST_PRELOAD;
 ctls->ctls=NULL;
 ctls->actions=NULL;
 ctls->current_char=' ';
 ctls->handles=NULL;
 ctls->current_handle=NULL;
 ctls->current_menu=NULL;
 ctls->top_menu=NULL;
 ctls->current_dbox=NULL;
 ctls->line=1;
 ctls->return_code=0;
 ctls->current_icon=-1;
 ctls->external_hit[0]='\0';

 /* Define stanfard actions. */

/*                Name          Sntx   Function            Handle  Intern? */

ctl__add_action("<submenu>"  ,NULL, ctl__submenu_request,  (void *) 1,TRUE);
ctl__add_action("<subdbox>"  ,NULL, ctl__submenu_request,  (void *) 2,TRUE);
ctl__add_action("<dsubmenu>" ,NULL, ctl__submenu_request,  (void *) 3,TRUE);
ctl__add_action("<dsubdbox>" ,NULL, ctl__submenu_request,  (void *) 4,TRUE);
ctl__add_action("<number>"   ,NULL, ctl__number_action  ,  (void *) 0,TRUE);
ctl__add_action("process"    ,NULL, ctl__processkey_action,(void *) 0,FALSE);
ctl__add_action("quit"       ,NULL, ctl__quit_action      ,(void *) 0,FALSE);
ctl__add_action("if"         ,"/e", ctl__if_action        ,(void *) 0,FALSE);
ctl__add_action("else"       ,NULL, ctl__else_action      ,(void *) 0,FALSE);
ctl__add_action("error"      ,NULL, ctl__error_action     ,(void *) 0,FALSE);
ctl__add_action("oscli"      ,NULL, ctl__oscli_action     ,(void *) 0,FALSE);
ctl__add_action("*"          ,NULL, ctl__oscli_action     ,(void *) 0,FALSE);
ctl__add_action("showdbox"   ,"/g", ctl__showdbox         ,(void *) 0,FALSE);

}

#define TOKENLEN 256
#define LINELEN  255

/* ctl__skip_toeol:
 *               skip this line in  input file.
 *               checks for length and eof.
 */

static void ctl__skip_toeol(void)
{
 char c;
 if (ctls->current_char==EOF) ctl__syntax_error(7,TRUE);

 do
 {
  c=ctls->current_char;
  ctls->current_char=fgetc(ctls->file);
  if ((ctls->current_char=='\n') && (c=='\\'))
  {
   ctls->line++;
   ctls->current_char=' ';
  }
 }
 while ((ctls->current_char!='\n') && (ctls->current_char!=EOF));

}


/* ctl__readtoeol:
 *               read next word from input file. skip any leading spaces
 *               and tabs.
 *               checks for length and eof.
 */

static void ctl__readtoeol(char *buffer)
{
 char *c=buffer;

 if (ctls->current_char==EOF) ctl__syntax_error(7,TRUE);

 do
 {
  *c++=ctls->current_char;
  if (ctls->current_char=='\n') break;
  if ((c-buffer)==LINELEN) ctl__syntax_error(8,TRUE);
  ctls->current_char=fgetc(ctls->file);
  if ((ctls->current_char=='\n') && (*(c-1)=='\\'))
  {
   *(c-1)=ctls->current_char=' ';
   ctls->line++;
  }
 }
 while ((ctls->current_char!='\n') && (ctls->current_char!=EOF));
 *c++='\n';
 *c='\0';

}

/* ctl__readword:
 *               read next word from input file. skip any leading spaces
 *               and tabs.
 *               checks for length and eof.
 */

static int ctl__readword(char *buffer)
{
 char *c=buffer,sep;

 if (ctls->current_char==EOF)
 {
  return EOF;
 }

 while (((ctls->current_char==' ') || (ctls->current_char=='\t'))
        && !feof(ctls->file)) ctls->current_char=fgetc(ctls->file);

 if (ctls->current_char=='"')
 {
  sep='"';
  ctls->current_char=fgetc(ctls->file);
 } else sep=' ';

 do
 {
  *c++=ctls->current_char;
  if (c-buffer==TOKENLEN) ctl__syntax_error(6,TRUE);
  ctls->current_char=fgetc(ctls->file);
 }
 while ((ctls->current_char!=sep) && (ctls->current_char!=EOF)
           && (ctls->current_char!='\n') && (*(c-1)!='\n') );

 *c='\0';

 if (sep=='"')
 switch(ctls->current_char)
 {
  case EOF:
           ctl__syntax_error(45,TRUE);
           break;
  case '\n':
           ctl__syntax_error(46,TRUE);
           break;
  default:
          ctls->current_char=fgetc(ctls->file);
          break;
 }

 return 0;

}


/* read next token.
 *       Reads in the next token from the file return token id.
 */

static ctl__token ctl__read_next_token(ctl__tokenstr *value)
{
 char buffer[TOKENLEN];
 int n;

 if (ctl__readword(buffer)==EOF)  return ctl_TOK_EOF;

 n=0;
 while (strcmp(ctl__tokens(n),"ENDLIST")!=0)
 {
  if (strcmp(ctl__tokens(n),buffer)==0) return n+1;
  n++;
 }

 if (sscanf(buffer,"%i",&value->num)==1)
 {
   if ((ctls->state!=ctl_ST_GETACTION) &&
        (ctls->state!=ctl_ST_GETACTIONBLOCK)) return ctl_TOK_NUMBER;
   else
   {
    ctl__actionstr act;
    act.name="<number>";
    value->action.args=(char *)(value->num);
    value->action.type=ctl_ACT_FUNCTION;
    value->action.action.address=
        ctl__find_action(&act,ctls->actions,FALSE,TRUE);
    return ctl_TOK_ACTION;
   }
 }
 else
 {
  ctl__actionstr act;
  act.name=buffer;
  if (
     (value->action.action.address=
        ctl__find_action(&act,ctls->actions,FALSE,FALSE))!=NULL)
  {
   value->action.type=ctl_ACT_FUNCTION;
   ctl__readtoeol(buffer);
#ifdef LOG
 fprintf(logfile,"Action arguments:\n");
#endif
   value->action.args=ctl__malloc(strlen(buffer)+1);
   strcpy(value->action.args,buffer);
   return ctl_TOK_ACTION;
  }
  else
  {
#ifdef LOG
 fprintf(logfile,"String:\n");
#endif
   value->string=ctl__malloc(strlen(buffer)+1);
   strcpy(value->string,buffer);
   return ctl_TOK_STRING;
  }
 }
 /* SHOULD NEVER GET HERE !!!!! */

 return ctl_TOK_UNKNOWN;
}

/*
 * ctl__new_ctl():
 *              Define a new ctl.
 */

static ctl *ctl__new_ctl(void)
{
 ctl *a_ctl;
 ctl__tokenstr tokenval;
#ifdef LOG
 fprintf(logfile,"New ctl:\n");
#endif
 if (ctls->ctls==NULL) /* First one */
 {
  ctls->ctls=(ctl *)ctl__malloc(sizeof(ctl));
  a_ctl=ctls->ctls;
 }
 else
 {
  a_ctl=ctls->ctls;
  while (a_ctl->next != NULL) a_ctl=a_ctl->next;
  a_ctl->next=(ctl *)ctl__malloc(sizeof(ctl));
  a_ctl=a_ctl->next;
 }

 /* Now a_ctl points to new space. */

 if (ctl__read_next_token(&tokenval)!=ctl_TOK_STRING)
         ctl__syntax_error(11,TRUE);

 a_ctl->name=tokenval.string;

 a_ctl->menus=NULL;
 a_ctl->dboxes=NULL;
 a_ctl->keys=NULL;
 a_ctl->onclick=NULL;
 a_ctl->next=NULL;

 return a_ctl;
}

/* ctl__new_menu(ctl *current_ctl)
 *
 * define a new menu in the given ctl.
 *
 */

static ctl__menu *ctl__new_menu(ctl *cctl)
{
 ctl__menu *a_menu;
 char *c,*body,*title;
 ctl__tokenstr tokenval;
 ctl__token token;
 int           i;
 BOOL external=FALSE;

#ifdef LOG
 fprintf(logfile,"New Menu:\n");
#endif
 if (cctl->menus==NULL) /* First one */
 {
  cctl->menus=(ctl__menu *)ctl__malloc(sizeof(ctl__menu));
  a_menu=cctl->menus;
 }
 else
 {
  a_menu=cctl->menus;
  while (a_menu->next != NULL) a_menu=a_menu->next;
  a_menu->next=(ctl__menu *)ctl__malloc(sizeof(ctl__menu));
  a_menu=a_menu->next;
 }

 /* Now a_menu points to new space. */

 if (ctl__read_next_token(&tokenval)!=ctl_TOK_STRING)
         ctl__syntax_error(13,TRUE);

 a_menu->name=tokenval.string;

 a_menu->onopen=NULL;
 a_menu->onreopen=NULL;
 a_menu->entries=NULL;
 a_menu->next=NULL;
 a_menu->mih_token=NULL;

 if ((token=ctl__read_next_token(&tokenval))==ctl_TOK_EXTERNAL)
 {
  a_menu->menu_body=NULL;
  a_menu->entrycount=0;
  external=TRUE;
  a_menu->external=external;
  return a_menu;

 } else if (token!=ctl_TOK_STRING) ctl__syntax_error(57,TRUE);

 title=tokenval.string;

 if ((token=ctl__read_next_token(&tokenval))==ctl_TOK_COLOURMENU)
 {
  a_menu->menu_body=colourmenu_make(msgs_lookup(title),FALSE);
  a_menu->entrycount=16;
  ctl__free(title);
 } else if (token!=ctl_TOK_STRING)
         ctl__syntax_error(58,TRUE);
 else
 {
  body=tokenval.string;
  a_menu->menu_body=menu_new(msgs_lookup(title),msgs_lookup(body));
  a_menu->entrycount=1;
  for(c=msgs_lookup(body);*c!='\0';c++)
     if ((*c==',') || (*c=='|')) a_menu->entrycount++;
  ctl__free(title);
  ctl__free(body);
 }
 a_menu->external=external;
 if (!external) for (i=1;i<=a_menu->entrycount;i++)
                menu_setflags(a_menu->menu_body,i,0,1);
 return a_menu;

}

/* ctl__new_dbox(ctl *current_ctl)
 *
 * define a new dbox in the given ctl.
 *
 */

static ctl__dbox *ctl__new_dbox(ctl *cctl)
{
 ctl__dbox *a_dbox;
 ctl__tokenstr tokenval;

#ifdef LOG
 fprintf(logfile,"New Dbox:\n");
#endif

 if (cctl->dboxes==NULL) /* First one */
 {
  cctl->dboxes=(ctl__dbox *)ctl__malloc(sizeof(ctl__dbox));
  a_dbox=cctl->dboxes;
 }
 else
 {
  a_dbox=cctl->dboxes;
  while (a_dbox->next != NULL) a_dbox=a_dbox->next;
  a_dbox->next=(ctl__dbox *)ctl__malloc(sizeof(ctl__dbox));
  a_dbox=a_dbox->next;
 }

 /* Now a_dbox points to new space. */

 if (ctl__read_next_token(&tokenval)!=ctl_TOK_STRING)
         ctl__syntax_error(31,TRUE);

 a_dbox->name=tokenval.string;

 a_dbox->onopen  =NULL;
 a_dbox->onreopen  =NULL;
 a_dbox->onclose =NULL;
 a_dbox->icons   =NULL;
 a_dbox->next    =NULL;
 a_dbox->mih_token=NULL;
 a_dbox->displayed=FALSE;
 a_dbox->template=NULL;

 return a_dbox;

}



/* ctl__add_to_actionlist
 *
 * add an action at the end of an action list.
 *
 */

static ctl__action *ctl__add_to_actionlist(ctl__action *list,ctl__tokenstr *act)
{

 if (list->action.name!=NULL)
 {
#ifdef LOG
 fprintf(logfile,"New action in action list:\n");
#endif
  list->next=ctl__malloc(sizeof(ctl__action));
  list=list->next;
 }

  *list=act->action;
  list->line=ctls->line;
  list->nest=ctls->nest;
  list->next=NULL;
  return list;
}

/*
 * ctl__add_string_to_actionlist
 *
 * add a string as an action at the end of the actionlist.
 *
 */

static ctl__action *ctl__add_string_to_actionlist(ctl__action *list,
                                                     ctl__tokenstr *act)
{
 char buffer[LINELEN];

#ifdef LOG
 fprintf(logfile,"String in action list:\n");
#endif

 ctl__readtoeol(buffer);
 act->action.action.name=act->string;
 act->action.args=ctl__malloc(strlen(buffer)+1);
 strcpy(act->action.args,buffer);
 if ((act->string[0]=='(') && (act->string[strlen(act->string)-1]==')'))
      act->action.type=ctl_ACT_LABEL;
 else act->action.type=ctl_ACT_STRING;

 return ctl__add_to_actionlist(list,act);
}




/*
 * ctl__new_entry
 *
 * add new entry to given menu.
 * return pointer to action list for the new entry.
 *
 */

static ctl__action *ctl__new_entry(ctl__menu *menu,int entry)
{
 ctl__menu_entry *an_entry;
 int i;

#ifdef LOG
 fprintf(logfile,"New Menu Entry:\n");
#endif

 if (
     (entry!=-1) &&
     (!menu->external) &&
     ((entry<1) || (entry>menu->entrycount))
    )
      ctl__syntax_error(26,TRUE);

 else if (!menu->external)
 {
  if (entry>0) menu_setflags(menu->menu_body,entry,0,0);
      else for (i=1;i<=menu->entrycount;i++)
             menu_setflags(menu->menu_body,i,0,0);
 }

 if (menu->entries==NULL) /* first entry */
 {
  menu->entries=(ctl__menu_entry *)ctl__malloc(sizeof(ctl__menu_entry));
  an_entry=menu->entries;
 }
 else
 {
  an_entry=menu->entries;
  if (an_entry->num==entry)
   {
    if (an_entry->action->action.name!=NULL) ctl__syntax_error(27,TRUE);
       else return an_entry->action;
   }

  while (an_entry->next != NULL)
  {
   an_entry=an_entry->next;
   if (an_entry->num==entry)
   {
    if (an_entry->action->action.name!=NULL) ctl__syntax_error(27,TRUE);
       else return an_entry->action;
   }
  }

  an_entry->next=(ctl__menu_entry *)ctl__malloc(sizeof(ctl__menu_entry));
  an_entry=an_entry->next;
 }

 an_entry->num=entry;
 an_entry->action=(ctl__action *)ctl__malloc(sizeof(ctl__action));
 an_entry->action->action.name=NULL;
 an_entry->disabled=0;
 an_entry->next=NULL;
 an_entry->help_action=NULL;

 return an_entry->action;

}


/* add a new entry help action entry number is next token  in file.
 *
 *
 *
 */


static ctl__action *ctl__add_entry_help(ctl__menu *menu)
{
 ctl__tokenstr tokenval;
 ctl__menu_entry *an_entry;

#ifdef LOG
 fprintf(logfile,"New Entry Help Action:\n");
#endif

 if (ctl__read_next_token(&tokenval)!=ctl_TOK_NUMBER)
      ctl__syntax_error(25,TRUE);

 if ((an_entry=ctl__find_entry(tokenval.num,menu))==NULL)
 {
  ctl__new_entry(menu,tokenval.num);
  an_entry=ctl__find_entry(tokenval.num,menu);
 }

 if (an_entry->help_action==NULL)
 {
   an_entry->help_action=(ctl__action *)ctl__malloc(sizeof(ctl__action));
   an_entry->help_action->action.name=NULL;
   return an_entry->help_action;
 }
 else ctl__syntax_error(71,TRUE);

 /* Should never get here. */

 return NULL;
}

/* add a new entry, entry number is next token  in file.
 *
 *
 *
 */


static ctl__action *ctl__add_new_entry_from_file(ctl__menu *menu)
{
 ctl__tokenstr tokenval;
 ctl__token token;

 if ((token=ctl__read_next_token(&tokenval))==ctl_TOK_HELP)
    return ctl__add_entry_help(menu);

 if (token!=ctl_TOK_NUMBER)
      ctl__syntax_error(25,TRUE);

 return ctl__new_entry(menu,tokenval.num);
}




/*
 * ctl__new_icon
 *
 * add new icon to given dbox.
 *
 */

static ctl__action *ctl__new_icon(ctl__dbox *dbox,int inum)
{
 ctl__icon     *an_icon;

#ifdef LOG
 fprintf(logfile,"New Icon:\n");
#endif


 if ((inum!=-1) && ((inum<0) || (inum>=dbox->iconcount)))
      ctl__syntax_error(37,TRUE);

 if (dbox->icons==NULL) /* first icon */
 {
  dbox->icons=(ctl__icon *)ctl__malloc(sizeof(ctl__icon));
  an_icon=dbox->icons;
 }
 else
 {
  an_icon=dbox->icons;

  if (an_icon->num==inum)
   {
    if (an_icon->action->action.name!=NULL) ctl__syntax_error(38,TRUE);
       else return an_icon->action;
   }

  while (an_icon->next != NULL)
  {
   an_icon=an_icon->next;
  if (an_icon->num==inum)
   {
    if (an_icon->action->action.name!=NULL) ctl__syntax_error(38,TRUE);
       else return an_icon->action;
   }
  }

  an_icon->next=(ctl__icon *)ctl__malloc(sizeof(ctl__icon));
  an_icon=an_icon->next;
 }

 an_icon->num=inum;
 an_icon->action=(ctl__action *)ctl__malloc(sizeof(ctl__action));
 an_icon->action->action.name=NULL;
 an_icon->disabled=0;
 an_icon->ticked=FALSE;
 an_icon->help_action=NULL;
 an_icon->next=NULL;

 return an_icon->action;

}

static ctl__action *ctl__add_icon_help(ctl__dbox *dbox)
{
 ctl__tokenstr tokenval;
 ctl__icon *an_icon;

#ifdef LOG
 fprintf(logfile,"New Icon Help:\n");
#endif
 if (ctl__read_next_token(&tokenval)!=ctl_TOK_NUMBER)
      ctl__syntax_error(36,TRUE);

 if ((an_icon=ctl__find_icon(tokenval.num,dbox))==NULL)
 {
  ctl__new_icon(dbox,tokenval.num);
  an_icon=ctl__find_icon(tokenval.num,dbox);
 }

 if (an_icon->help_action==NULL)
 {
   an_icon->help_action=(ctl__action *) ctl__malloc(sizeof(ctl__action));
   an_icon->help_action->action.name=NULL;
   return an_icon->help_action;
 }
 else ctl__syntax_error(71,TRUE);

 /* Should never get here. */

 return NULL;
}

/*
 * ctl__add_new_icon_from_file
 *
 * add new icon to given dbox. icon number is next token in file.
 *
 */

static ctl__action *ctl__add_new_icon_from_file(ctl__dbox *dbox)
{
 ctl__tokenstr tokenval;
 ctl__token token;

 /* get icon number */

 if ((token=ctl__read_next_token(&tokenval))==ctl_TOK_HELP)
    return ctl__add_icon_help(dbox);

 if (token!=ctl_TOK_NUMBER)
      ctl__syntax_error(36,TRUE);

  return ctl__new_icon(dbox,tokenval.num);
}

/*
 * ctl__new_key
 *
 * add new entry to given ctl.
 *
 */

static ctl__action *ctl__new_key(ctl *cctl)
{
 ctl__tokenstr tokenval;
 ctl__key      *a_key;
 int keynum;
 ctl__token token;


#ifdef LOG
 fprintf(logfile,"New Hot Key:\n");
#endif

 /* get entry number */


 keynum=-1;
 if ((token=ctl__read_next_token(&tokenval))==ctl_TOK_NUMBER)
     keynum=tokenval.num;
 else if (token==ctl_TOK_STRING) keynum=ctl__keynum(tokenval.string);

 if (keynum==-1) ctl__syntax_error(40,TRUE);

 if (cctl->keys==NULL) /* first entry */
 {
  cctl->keys=(ctl__key *)ctl__malloc(sizeof(ctl__key));
  a_key=cctl->keys;
 }
 else
 {
  a_key=cctl->keys;
  if (a_key->keycode==keynum)  ctl__syntax_error(41,TRUE);

  while (a_key->next != NULL)
  {
   a_key=a_key->next;
   if (a_key->keycode==keynum) ctl__syntax_error(41,TRUE);
  }

  a_key->next=(ctl__key *)ctl__malloc(sizeof(ctl__key));
  a_key=a_key->next;
 }

 a_key->keycode=keynum;
 a_key->action=(ctl__action *)ctl__malloc(sizeof(ctl__action));
 a_key->action->action.name=NULL;
 a_key->action->next=NULL;
 a_key->disabled=FALSE;
 a_key->next=NULL;

 return a_key->action;

}

/*
 * ctl__submenu
 *
 * attach submenu to given menu.
 *
 */

static ctl__action *ctl__submenu(ctl *cctl,ctl__menu *a_menu)
{
 ctl__token token;
 ctl__tokenstr tokenval;
 ctl__action     *actionlist;
 ctl__actionstr  act;
 int             entry;
 ctl__menu       *submenu;
 ctl__dbox       *a_dbox;

 /* get entry number */

 if (ctl__read_next_token(&tokenval)!=ctl_TOK_NUMBER)
      ctl__syntax_error(25,TRUE);

 if (
     (tokenval.num!=-1) &&
     (!a_menu->external) &&
     ((tokenval.num<1) || (tokenval.num>a_menu->entrycount))
    )
      ctl__syntax_error(26,TRUE);

 if (ctl__find_entry(tokenval.num,a_menu)!=NULL)
      ctl__syntax_error(27,TRUE);

 entry=tokenval.num;

 if ((token=ctl__read_next_token(&tokenval))==ctl_TOK_MENU)
 {
  if (ctl__read_next_token(&tokenval)!=ctl_TOK_STRING)
      ctl__syntax_error(13,TRUE);

  submenu=ctl__find_menu(tokenval.string,cctl);
  if (!a_menu->external)
  {
   int en;
   for (en=((entry==-1)?1:entry);
        en<=((entry==-1)?a_menu->entrycount:entry);
        en++)
   {
     ctl__menu_item(a_menu->menu_body,en)->flags|=wimp_MSUBLINKMSG;
     ctl__menu_item(a_menu->menu_body,en)->submenu=
          (submenu->external ? (wimp_menustr *)1 :
                                menu_syshandle(submenu->menu_body));
     if (submenu->external)
        submenu->parent=ctl__menu_item(a_menu->menu_body,en);
     menu_setflags(a_menu->menu_body,en,0,0);
    }
  }
  actionlist=ctl__new_entry(a_menu,entry);
  actionlist->type=ctl_ACT_FUNCTION;
  act.name="<submenu>";
  actionlist->action.address=
       ctl__find_action(&act,ctls->actions,FALSE,TRUE);
  actionlist->args=(char *)submenu;
  actionlist->line=ctls->line;
  actionlist->nest=ctls->nest;
  actionlist->next=NULL;

  return actionlist;
 }
  else if (token==ctl_TOK_DBOX)
 {
  if (ctl__read_next_token(&tokenval)!=ctl_TOK_STRING)
      ctl__syntax_error(31,TRUE);

  a_dbox=ctl__find_dbox(tokenval.string,cctl);
  if (!a_menu->external)
  {
   int en;
   for (en=((entry==-1)?1:entry);
        en<=((entry==-1)?a_menu->entrycount:entry);
        en++)
   {
     ctl__menu_item(a_menu->menu_body,en)->submenu=(wimp_menustr *) a_dbox;
     ctl__menu_item(a_menu->menu_body,en)->flags|=wimp_MSUBLINKMSG;
     menu_setflags(a_menu->menu_body,en,0,0);
   }
  }

  actionlist=ctl__new_entry(a_menu,entry);
  actionlist->type=ctl_ACT_FUNCTION;
  act.name="<subdbox>";
  actionlist->action.address=
       ctl__find_action(&act,ctls->actions,FALSE,TRUE);
  actionlist->args=(char *)a_dbox;
  actionlist->line=ctls->line;
  actionlist->nest=ctls->nest;
  actionlist->next=NULL;

  return actionlist;
 }
 else if (token==ctl_TOK_WARNING)
 {
  if (!a_menu->external)
  {
   int en;
   for (en=((entry==-1)?1:entry);
        en<=((entry==-1)?a_menu->entrycount:entry);
        en++)
   {
    ctl__menu_item(a_menu->menu_body,en)->flags|=wimp_MSUBLINKMSG;
    menu_setflags(a_menu->menu_body,en,0,0);
    ctl__menu_item(a_menu->menu_body,en)->submenu=(wimp_menustr *)1;
   }
  }

  return ctl__new_entry(a_menu,entry);

 } else ctl__syntax_error(43,TRUE);

 /*NEVER */

 return NULL;

}


/*
 * ctl__subdboxmenu
 *
 * attach submenu to given dbox.
 *
 */

static ctl__action *ctl__subdboxmenu(ctl *cctl,ctl__dbox *dbx)
{
 ctl__token token;
 ctl__tokenstr tokenval;
 ctl__action     *actionlist;
 ctl__actionstr  act;
 int             icon;
 ctl__dbox       *a_dbox;
#ifdef DBOXSUBMENUS
 ctl__menu       *submenu;
#endif

 /* get icon number */

 if (ctl__read_next_token(&tokenval)!=ctl_TOK_NUMBER)
      ctl__syntax_error(25,TRUE);

 if ((tokenval.num!=-1) &&
       ((tokenval.num<0) || (tokenval.num>=dbx->iconcount)))
                                        ctl__syntax_error(26,TRUE);

 if (ctl__find_icon(tokenval.num,dbx)!=NULL)
      ctl__syntax_error(27,TRUE);

 icon=tokenval.num;

 if ((token=ctl__read_next_token(&tokenval))==ctl_TOK_MENU)
 {
#ifndef DBOXMENUS
     ctl__syntax_error(47,TRUE);
#else
  if (ctl__read_next_token(&tokenval)!=ctl_TOK_STRING)
      ctl__syntax_error(13,TRUE);

  submenu=ctl__find_menu(tokenval.string,cctl);
  actionlist=ctl__new_icon(dbx,icon);
  actionlist->type=ctl_ACT_FUNCTION;
  act.name="<dsubmenu>";
  actionlist->action.address=
       ctl__find_action(&act,ctls->actions,FALSE,TRUE);
  actionlist->args=(char *)submenu;
  actionlist->line=ctls->line;
  actionlist->nest=ctls->nest;
  actionlist->next=NULL;
  return actionlist;
#endif /* DBOXMENUS */
 }
  else if (token==ctl_TOK_DBOX)
 {
  if (ctl__read_next_token(&tokenval)!=ctl_TOK_STRING)
      ctl__syntax_error(31,TRUE);

  a_dbox=ctl__find_dbox(tokenval.string,cctl);
  actionlist=ctl__new_icon(dbx,icon);
  actionlist->type=ctl_ACT_FUNCTION;
  act.name="<dsubdbox>";
  actionlist->action.address=
       ctl__find_action(&act,ctls->actions,FALSE,TRUE);
  actionlist->args=(char *)a_dbox;
  actionlist->line=ctls->line;
  actionlist->nest=ctls->nest;
  actionlist->next=NULL;
  return actionlist;
 }
  else ctl__syntax_error(43,TRUE);

 /*NEVER */

 return NULL;

}



static int ctl__exec(ctl__action *action)
{
 ctl__argbuf argbuf;
 if (action->type==ctl_ACT_FUNCTION)
 {
  if (action->action.address->syntax==NULL)
  return (int)  (action->action.address->function.function(
                 ctls->current_handle->user_handle,
                 action->action.address->function.handle,
                 action->args,
                 ctls->return_code));
  else
  {
   ctl__read_args(action->action.address->syntax,action->args,&argbuf);
   return (int)(action->action.address->function.function(
                 ctls->current_handle->user_handle,
                 action->action.address->function.handle,
                 (char *) &argbuf,
                 ctls->return_code));
  }
 }
 else
 {
  ctl__actionstr act,*actpos;
  act.name=action->action.name;
  actpos=ctl__find_action(&act,ctls->actions,FALSE,FALSE);
  if (actpos==NULL)
  {
   if (action->type==ctl_ACT_LABEL) return 0;
   ctls->line=action->line;
   ctl__error(44,act.name,FALSE);
   return 0;
  }
  else
  {
    if (actpos->syntax==NULL)


    return (int)(actpos->function.function(
                 ctls->current_handle->user_handle,
                 actpos->function.handle,
                 action->args,
                 ctls->return_code));
  else
  {
   ctl__read_args(actpos->syntax,action->args,&argbuf);
   return (int)(actpos->function.function(
                 ctls->current_handle->user_handle,
                 actpos->function.handle,
                 (char *) &argbuf,
                 ctls->return_code));
  }

  }
 }
 return 0;
}

static void ctl__run(ctl__action *action)
{
 BOOL submenu=(wimpt_last_event()->e!=wimp_EMENU);
 BOOL skipflag=FALSE;
 ctl__action *an_action=action;
 int tmp_skip=ctls->skip;
#ifdef LOG
 if (logfile!=NULL)
 {
  fclose(logfile);
  logfile=NULL;
 }
#endif
 if ((action==NULL) || (action==(ctl__action *)-1)) return;
 ctls->skip=-1;
 for (an_action=action;an_action!=NULL;an_action=an_action->next)
 {
  if (skipflag)
  {
   skipflag=FALSE;
   continue;
  }
  if ((ctls->skip<0) || (ctls->skip>=an_action->nest))
  {
   ctls->return_code=ctl__exec(an_action);

   if (ctls->skip==-2)
   {
    ctls->skip=an_action->nest;
    skipflag=TRUE;
   } else if (ctls->skip!=-3) ctls->skip=-1;
   if ((an_action->type==ctl_ACT_FUNCTION) &&
      (an_action->action.address->function.function==ctl__submenu_request) &&
      (submenu)
     ) break;
  }
 }
 ctls->skip=tmp_skip;
}


static void ctl__get_template(ctl__dbox *a_dbox)
{
 ctl__tokenstr token;

 if (ctl__read_next_token(&token)!=ctl_TOK_STRING)
     ctl__syntax_error(54,TRUE);

 if (template_find(token.string)==NULL)
             ctl__error(53,token.string,TRUE);

 a_dbox->template=token.string;
 a_dbox->dbox_body=dbox_new(token.string);
 if (a_dbox->dbox_body==NULL) ctl__error(32,a_dbox->name,TRUE);
 a_dbox->iconcount=ctl__iconcount(&a_dbox->dbox_body);
 dbox_dispose(&a_dbox->dbox_body);
}

static void ctl__get_dbox_help(ctl__dbox *a_dbox)
{
 ctl__tokenstr token;
 ctl__token    type;

 type=ctl__read_next_token(&token);
 if (type==ctl_TOK_HELPACTION) werr(1,"Action help not implemented.");

 if (type!=ctl_TOK_TOKEN) ctl__syntax_error(62,TRUE);

 if (a_dbox->mih_token!=NULL) ctl__syntax_error(60,TRUE);
 if (ctl__read_next_token(&token)!=ctl_TOK_STRING)
     ctl__syntax_error(61,TRUE);
 a_dbox->mih_token=token.string;
}

static void ctl__get_menu_help(ctl__menu *a_menu)
{
 ctl__tokenstr token;
 ctl__token    type;

 type=ctl__read_next_token(&token);
 if (type==ctl_TOK_HELPACTION) werr(1,"Action help not implemented.");

 if (type!=ctl_TOK_TOKEN) ctl__syntax_error(62,TRUE);
 if (a_menu->mih_token!=NULL) ctl__syntax_error(60,TRUE);
 if (ctl__read_next_token(&token)!=ctl_TOK_STRING)
     ctl__syntax_error(61,TRUE);
 a_menu->mih_token=token.string;
}


/* ctl__load :
 *            open and load the control file.
 *            if errors is TRUE generate errors for non exsistant actions.
 */

static void ctl__load(BOOL errors)
{
 FILE          *control;
 ctl__token    token;
 ctl__tokenstr value;
 ctl           *current_ctl=NULL;
 ctl__menu     *current_menu=NULL;
 ctl__dbox     *current_dbox=NULL;
 ctl__action   *action_list=NULL;
 ctl__state    saved_state=ctl_ST_LOADING;

 if (control=res_openfile("Control","r"),control==NULL)
 {
  ctl__error(5,NULL,TRUE);
  return;
 }

 ctls->file=control;
 ctls->state=ctl_ST_LOADING;
 ctls->nest=0;

 do
 {
  token=ctl__read_next_token(&value);
  switch(token)
  {
   case ctl_TOK_EOF:
        fclose(ctls->file);
        if (ctls->state!=ctl_ST_LOADING) ctl__error(9,NULL,TRUE);
        if (ctls->nest!=0) ctl__syntax_error(29,TRUE);
        break;
   case ctl_TOK_CTL:
        if (ctls->state!=ctl_ST_LOADING) ctl__syntax_error(10,TRUE);
        if (ctls->nest!=0) ctl__syntax_error(29,TRUE);
        current_ctl=ctl__new_ctl();
        ctls->state=ctl_ST_CTLDEF;
        break;

   case ctl_TOK_MENU:
        if (ctls->state!=ctl_ST_CTLDEF) ctl__syntax_error(10,TRUE);
        if (ctls->nest!=0) ctl__syntax_error(29,TRUE);
        current_menu=ctl__new_menu(current_ctl);
        ctls->state=ctl_ST_MENUDEF;
        break;

   case ctl_TOK_DBOX:
        if (ctls->state!=ctl_ST_CTLDEF) ctl__syntax_error(30,TRUE);
        if (ctls->nest!=0) ctl__syntax_error(29,TRUE);
        current_dbox=ctl__new_dbox(current_ctl);
        ctls->state=ctl_ST_DBOXDEF;
        break;

   case ctl_TOK_ONOPEN:
        if (ctls->nest!=0) ctl__syntax_error(29,TRUE);
        if ((ctls->state!=ctl_ST_MENUDEF) &&
            (ctls->state!=ctl_ST_DBOXDEF)) ctl__syntax_error(14,TRUE);

        if (((ctls->state==ctl_ST_MENUDEF) && (current_menu->onopen!=NULL))
           ||((ctls->state==ctl_ST_DBOXDEF) && (current_dbox->onopen!=NULL)))
               ctl__error(15,(ctls->state==ctl_ST_MENUDEF)?
                             current_menu->name:current_dbox->name,TRUE);
#ifdef LOG
 fprintf(logfile,"Onopen action:\n");
#endif
        action_list=(ctl__action *)ctl__malloc(sizeof(ctl__action));
        action_list->action.name=NULL;

        if (ctls->state==ctl_ST_MENUDEF) current_menu->onopen=action_list;
              else current_dbox->onopen=action_list;

        saved_state=ctls->state;
        ctls->state=ctl_ST_GETACTION;
        break;

   case ctl_TOK_ONREOPEN:
        if (ctls->nest!=0) ctl__syntax_error(29,TRUE);
        if ((ctls->state!=ctl_ST_MENUDEF) &&
            (ctls->state!=ctl_ST_DBOXDEF)) ctl__syntax_error(14,TRUE);

        if (((ctls->state==ctl_ST_MENUDEF) && (current_menu->onreopen!=NULL))
        ||((ctls->state==ctl_ST_DBOXDEF) && (current_dbox->onreopen!=NULL)))
               ctl__error(15,(ctls->state==ctl_ST_MENUDEF)?
                             current_menu->name:current_dbox->name,TRUE);
#ifdef LOG
 fprintf(logfile,"Onreopen action:\n");
#endif

        action_list=(ctl__action *)ctl__malloc(sizeof(ctl__action));
        action_list->action.name=NULL;

        if (ctls->state==ctl_ST_MENUDEF) current_menu->onreopen=action_list;
              else current_dbox->onreopen=action_list;

        saved_state=ctls->state;
        ctls->state=ctl_ST_GETACTION;
        break;

   case ctl_TOK_NOREOPEN:
        if (ctls->nest!=0) ctl__syntax_error(29,TRUE);
        if (ctls->state!=ctl_ST_DBOXDEF) ctl__syntax_error(14,TRUE);
        if (current_dbox->onreopen!=NULL)
               ctl__error(15,current_dbox->name,TRUE);
        current_dbox->onreopen=(ctl__action *)-1;
        break;

   case ctl_TOK_ONCLOSE:
        if (ctls->nest!=0) ctl__syntax_error(29,TRUE);
        if (ctls->state!=ctl_ST_DBOXDEF) ctl__syntax_error(33,TRUE);

        if (current_dbox->onclose!=NULL)
                   ctl__error(34,current_dbox->name,TRUE);

#ifdef LOG
 fprintf(logfile,"Onclose action:\n");
#endif
        action_list=(ctl__action *)ctl__malloc(sizeof(ctl__action));
        action_list->action.name=NULL;

        current_dbox->onclose=action_list;

        saved_state=ctls->state;
        ctls->state=ctl_ST_GETACTION;
        break;
   case ctl_TOK_ENDDBOX:
        if (ctls->nest!=0) ctl__syntax_error(29,TRUE);
        if (ctls->state!=ctl_ST_DBOXDEF) ctl__syntax_error(17,TRUE);
        ctls->state=ctl_ST_CTLDEF;
        break;

   case ctl_TOK_ENDMENU:
        if (ctls->nest!=0) ctl__syntax_error(29,TRUE);
        if ((ctls->state!=ctl_ST_MENUDEF) && (ctls->state!=ctl_ST_GETACTION))
            ctl__syntax_error(18,TRUE);
        ctls->state=ctl_ST_CTLDEF;
        break;

   case ctl_TOK_ENDCTL:
        if (ctls->nest!=0) ctl__syntax_error(29,TRUE);
        if (ctls->state!=ctl_ST_CTLDEF) ctl__syntax_error(19,TRUE);
        ctls->state=ctl_ST_LOADING;
        break;

   case ctl_TOK_OPENBLOCK:
        if ((ctls->state!=ctl_ST_GETACTION) &&
           (ctls->state!=ctl_ST_GETACTIONBLOCK)) ctl__syntax_error(16,TRUE);
        ctls->nest++;
        ctls->state=ctl_ST_GETACTIONBLOCK;
        break;

   case ctl_TOK_CLOSEBLOCK:
        if (ctls->state!=ctl_ST_GETACTIONBLOCK) ctl__syntax_error(20,TRUE);
        if (--ctls->nest<0) ctl__syntax_error(28,TRUE);
        if (ctls->nest==0) ctls->state=saved_state;
        break;

   case ctl_TOK_ENTRY:
        if (ctls->state==ctl_ST_GETACTION) ctls->state=saved_state;
        if (ctls->nest!=0) ctl__syntax_error(29,TRUE);
        if (ctls->state!=ctl_ST_MENUDEF) ctl__syntax_error(24,TRUE);
        action_list=ctl__add_new_entry_from_file(current_menu);
        saved_state=ctls->state;
        ctls->state=ctl_ST_GETACTION;
        break;

   case ctl_TOK_SUBMENU:
        if (ctls->state==ctl_ST_GETACTION) ctls->state=saved_state;
        if (ctls->nest!=0) ctl__syntax_error(29,TRUE);
        else if (ctls->state==ctl_ST_MENUDEF)
              action_list=ctl__submenu(current_ctl,current_menu);
        else if (ctls->state==ctl_ST_DBOXDEF)
              action_list=ctl__subdboxmenu(current_ctl,current_dbox);
        else ctl__syntax_error(24,TRUE);
        if (action_list!=NULL)
        {
         saved_state=ctls->state;
         ctls->state=ctl_ST_GETACTION;
        }
        break;

   case ctl_TOK_ONKEY:
        if (ctls->nest!=0) ctl__syntax_error(29,TRUE);
        if (ctls->state!=ctl_ST_CTLDEF) ctl__syntax_error(39,TRUE);
        action_list=ctl__new_key(current_ctl);
        saved_state=ctls->state;
        ctls->state=ctl_ST_GETACTION;
        break;

   case ctl_TOK_ONCLICK:
        if (ctls->nest!=0) ctl__syntax_error(29,TRUE);
        if (ctls->state!=ctl_ST_CTLDEF) ctl__syntax_error(68,TRUE);
        if (current_ctl->onclick!=NULL) ctl__syntax_error(69,TRUE);
#ifdef LOG
 fprintf(logfile,"Onclick action list:\n");
#endif
        action_list=(current_ctl->onclick=
            (ctl__action *)ctl__malloc(sizeof(ctl__action)));
        action_list->action.name=NULL;
        action_list->next=NULL;
        saved_state=ctls->state;
        ctls->state=ctl_ST_GETACTION;
        break;

   case ctl_TOK_ICON:
        if (ctls->nest!=0) ctl__syntax_error(29,TRUE);
        if (ctls->state!=ctl_ST_DBOXDEF) ctl__syntax_error(35,TRUE);
        if (current_dbox->template==NULL)
               ctl__error(55,current_dbox->name,TRUE);
        action_list=ctl__add_new_icon_from_file(current_dbox);
        saved_state=ctls->state;
        ctls->state=ctl_ST_GETACTION;
        break;

   case ctl_TOK_ACTION:
        if (ctls->state==ctl_ST_GETACTION) ctls->nest++;
        if ((ctls->state!=ctl_ST_GETACTION) &&
            (ctls->state!=ctl_ST_GETACTIONBLOCK)) ctl__syntax_error(21,TRUE);
        action_list=ctl__add_to_actionlist(action_list,&value);
        if (ctls->state==ctl_ST_GETACTION)
        {
         ctls->state=saved_state;
         ctls->nest--;
        }
        break;

   case ctl_TOK_STRING:
        if ((ctls->state!=ctl_ST_GETACTION) &&
            (ctls->state!=ctl_ST_GETACTIONBLOCK)) ctl__syntax_error(22,TRUE);
        if ((errors) &&
            ((value.string[0] != '(') ||
             (value.string[strlen(value.string)-1] != ')')
            )
           )  ctl__syntax_error(23,TRUE);
        if (ctls->state==ctl_ST_GETACTION) ctls->nest++;
        action_list=ctl__add_string_to_actionlist(action_list,&value);
        if (ctls->state==ctl_ST_GETACTION)
        {
         ctls->state=saved_state;
         ctls->nest--;
        }
        break;
   case ctl_TOK_TEMPLATE:
        if (ctls->nest!=0) ctl__syntax_error(29,TRUE);
        if (ctls->state!=ctl_ST_DBOXDEF) ctl__syntax_error(52,TRUE);
        if (current_dbox->template!=NULL)
               ctl__error(56,current_dbox->name,TRUE);
        ctl__get_template(current_dbox);
        break;
   case ctl_TOK_HELP:
        if (ctls->nest!=0) ctl__syntax_error(29,TRUE);
        if ((ctls->state!=ctl_ST_DBOXDEF) && (ctls->state!=ctl_ST_MENUDEF))
                              ctl__syntax_error(59,TRUE);
        switch(ctls->state)
        {
         case ctl_ST_DBOXDEF:
              ctl__get_dbox_help(current_dbox);
              break;
         case ctl_ST_MENUDEF:
              ctl__get_menu_help(current_menu);
              break;
        }
        break;
   case ctl_TOK_COMMENT:
        ctl__skip_toeol();
        break;
   case ctl_TOK_EOL:
        ctls->line++;
        if (ctls->current_char=='#') ctl__skip_toeol();
        break;
  }
 } while (token!=ctl_TOK_EOF);

 ctls->state=ctl_ST_LOADED;
 ctls->line=0;

}

static menu ctl__menu_maker(ctl__handlestr *handle)
{
 ctls->current_handle=handle;
 ctls->current_menu=handle->mnu;
 ctls->top_menu=handle->mnu;
 ctls->return_code=(wimpt_last_event()->data.but.m.i);
 if (!event_is_menu_being_recreated()) ctl__run(handle->mnu->onopen);

 return handle->mnu->menu_body;
}

static void ctl__menu_select(ctl__handlestr *handle,char *hit)
{
 ctl__menu *mnu;
 ctl__menu_entry *entry;
 char *c;
 BOOL recreate=event_is_menu_being_recreated();

 if (wimpt_last_event()->e!=wimp_EMENU) recreate=FALSE;
 else
 {
  wimp_mousestr m;
  wimp_get_point_info(&m);
  recreate=((m.bbits & wimp_BRIGHT) != 0);
 }
 ctls->current_menu=handle->mnu;

 for (c=hit;*(c+1);c++)
 {
  if (ctls->current_menu->external)
  {
   if ((wimpt_last_event()->e==wimp_ESEND) && !*(c+2)) *(c+1)='\0';
   strcpy(ctls->external_hit,c);
  }
  if ((wimpt_last_event()->e==wimp_ESEND) && !*(c+2))
  {
   *(c+1)='\0';
   break;
  }
  entry=ctl__find_entry(*c,ctls->current_menu);
  if (entry==NULL) entry=ctl__find_entry(-1,ctls->current_menu);
  if (entry==NULL) break;
  if (entry->action->action.address->function.function!=
           ctl__submenu_request)  break;
  ctls->current_menu=(ctl__menu *) entry->action->args;
 }

 strcpy(ctls->last_menu_hit,hit);

 entry=ctl__find_entry(*c,ctls->current_menu);
 ctls->return_code=*c;
 if (entry!=NULL) ctl__run(entry->action);
    else if (entry=ctl__find_entry(-1,ctls->current_menu),entry!=NULL)
         ctl__run(entry->action);

if (recreate)
{
 mnu=handle->mnu;
 for (c=ctls->last_menu_hit;*(c+1);c++)
 {
  entry=ctl__find_entry(*c,mnu);
  if (entry==NULL) entry=ctl__find_entry(-1,ctls->current_menu);
  if (entry==NULL) break;
  if (mnu->external) break;
  ctl__run(mnu->onreopen);
  mnu=(ctl__menu *) entry->action->args;
 }
 ctl__run(mnu->onreopen);
 wimp_create_menu(menu_syshandle(handle->mnu->menu_body),0,0);
}
}

/* ctl__find_action_in_list.
 *
 * given an action name and an action list returns
 * TRUE if action is in the list, FALSE if not.
 *
 *
 */
static BOOL ctl__find_action_in_list(char *name,ctl__action *list)
{
 ctl__action *act;
 static ctl__actionstr the_action,*the_action_address;

 if (the_action.name!=name)
 {
  the_action.name=name;
  the_action_address=
           ctl__find_action(&the_action,ctls->actions,FALSE,FALSE);
 }

 if (list==NULL) return FALSE;
 else if (list==(ctl__action *)-1) return FALSE;


 for (act=list;act!=NULL;act=act->next)
 {
  if ((act->type==ctl_ACT_LABEL) &&
           (strcmp(act->action.name,the_action.name)==0))  return TRUE;
  if ((act->type==ctl_ACT_STRING) &&
           (strcmp(act->action.name,the_action.name)==0))
     {
      act->type=ctl_ACT_LABEL;
      return TRUE;
     }

  if (act->type!=ctl_ACT_FUNCTION) continue;

  if (act->action.address==the_action_address) return TRUE;

  if (act->action.address->function.function!=ctl__submenu_request)
      continue;

  if ( ( (act->action.address->function.handle==(void *) 1) ||
         (act->action.address->function.handle==(void *) 3)
       )  &&
       (
        (ctl__find_action_in_list(name,((ctl__menu *)act->args)->onopen)) ||
        (ctl__find_action_in_list(name,((ctl__menu *)act->args)->onreopen))
       )
     )
        return TRUE;

  if ( (  (act->action.address->function.handle==(void *) 2) ||
          (act->action.address->function.handle==(void *) 4)
       )  &&
      (
       (ctl__find_action_in_list(name,((ctl__dbox *)act->args)->onopen)) ||
       (ctl__find_action_in_list(name,((ctl__dbox *)act->args)->onreopen)) ||
       (ctl__find_action_in_list(name,((ctl__dbox *)act->args)->onclose))
      )
     )
        return TRUE;
 }

 return FALSE;
}


/*
 * ctl__find action.
 *
 * Find all places in given ctl where the action given is executed
 * for every place call func with arg arg, pointer to entry/icon,
 * entry/icon flag.
 *
 */
static void ctl__find_action_in_ctl(ctl *a_ctl,char *name,void (func()),void *arg)
{
 ctl__menu_entry *entry;
 ctl__menu       *a_menu;
 ctl__dbox       *a_dbox;
 ctl__icon       *icon;

 /*Must do dboxes first because of writeable icons */

 for (a_dbox=a_ctl->dboxes;a_dbox!=NULL;a_dbox=a_dbox->next)
     for (icon=a_dbox->icons;icon!=NULL;icon=icon->next)
 {
   if ((icon->action!=NULL) && (ctl__find_action_in_list(name,icon->action)))
   {
    func(arg,a_dbox,icon,ctl_ENTRY_ICON,a_dbox->displayed);
   }
 }
 for (a_menu=a_ctl->menus;a_menu!=NULL;a_menu=a_menu->next)
     for (entry=a_menu->entries;entry!=NULL;entry=entry->next)
 {
  if ((entry->action!=NULL)&&(ctl__find_action_in_list(name,entry->action)))
     {
      func(arg,a_menu,entry,ctl_ENTRY_MENUENTRY,FALSE);
     }
 }

}

/* ctl__tick_entry
 *
 * Tick the given entry / select icons if in a displayed dbox.
 *
 */

static void ctl__tick_entry(void *arg,void *parent,
                    void *entry,ctl_entrytype type,BOOL displayed)
{
 switch(type)
 {
  case ctl_ENTRY_MENUENTRY:
       if ((BOOL)arg)
       ctl__menu_item(((ctl__menu  *)parent)->menu_body,
             ((ctl__menu_entry *)entry)->num)->flags|=wimp_MTICK;
       else ctl__menu_item(((ctl__menu  *)parent)->menu_body,
             ((ctl__menu_entry *)entry)->num)->flags&=~wimp_MTICK;
       break;
  case ctl_ENTRY_ICON:
       if (displayed)
       {
       if ((BOOL)arg) wimp_set_icon_state(
                     dbox_syshandle(((ctl__dbox  *)parent)->dbox_body),
                   ((ctl__icon *)entry)->num,wimp_ISELECTED,wimp_ISELECTED);
       else  wimp_set_icon_state(
                     dbox_syshandle(((ctl__dbox  *)parent)->dbox_body),
                   ((ctl__icon *)entry)->num,0,wimp_ISELECTED);
       }
       ((ctl__icon *)entry)->ticked=(BOOL)arg;
       break;
 }
}


/* ctl__apply_function
 *
 * Apply the given function on the given action entries / icons.
 *
 */

static void ctl__apply_function(void (arg()),void *parent,
                    void *entry,ctl_entrytype type,BOOL displayed)
{
 ctl_parentstr the_parent;
 switch(type)
 {
  case ctl_ENTRY_MENUENTRY:
       the_parent.parent_menu=
                menu_syshandle(((ctl__menu  *)parent)->menu_body);
       arg(the_parent,
             ((ctl__menu_entry *)entry)->num,ctl_ENTRY_MENUENTRY);
       break;
  case ctl_ENTRY_ICON:
       the_parent.parent_window=
                  dbox_syshandle(((ctl__dbox  *)parent)->dbox_body);
       if (displayed)
       arg(the_parent,
             ((ctl__icon *)entry)->num,ctl_ENTRY_ICON);
       break;
 }
}



/* ctl__make_entry_writeable
 *
 * Make the given entry /icon writeable.
 *
 */

static void ctl__make_entry_writeable(void *arg,void *parent,
                    void *entry,ctl_entrytype type,BOOL displayed)
{
 ctl__indirectstr *data=(ctl__indirectstr *)arg;
 switch(type)
 {
  case ctl_ENTRY_MENUENTRY:
     if (data->flag)      /* already created buffer */
        menu_make_writeable(((ctl__menu  *)parent)->menu_body,
       ((ctl__menu_entry *)entry)->num,data->buffer,data->len,data->valid);
     else /* This is the first menu entry and there were no icons !
             We know this because we know the order find_action_in_ctl
             works which is very important !! */
     {
      data->buffer=ctl__malloc(data->len+5);
      *data->buffer='\0';
       menu_make_writeable(((ctl__menu  *)parent)->menu_body,
       ((ctl__menu_entry *)entry)->num,data->buffer,data->len,data->valid);
       data->flag=TRUE;
     }
     break;
  case ctl_ENTRY_ICON:
      ((ctl__icon *)entry)->writeable=TRUE;
      if (data->flag)
       werr(TRUE,
         msgs_lookup(
         "ctdupind:Duplicate writeable action icon (Icon %i in dbox %s)"),
         ((ctl__icon *)entry)->num,((ctl__dbox  *)parent)->name);
      if (displayed)
      {
       wimp_icon icon;
       wimp_get_icon_info(dbox_syshandle(((ctl__dbox  *)parent)->dbox_body),
                    ((ctl__icon *)entry)->num,&icon);
       if
        (((icon.flags & wimp_ITEXT)==0) || ((icon.flags & wimp_INDIRECT)==0))
       werr(TRUE,
         msgs_lookup(
         "ctindicn:Icon %i in dbox %s should be  an indirected text icon"),
         ((ctl__icon *)entry)->num,((ctl__dbox  *)parent)->name);
       if ((icon.flags & (wimp_BWRITABLE<<12))!=(wimp_BWRITABLE<<12))
       werr(TRUE,
         msgs_lookup(
         "ctwrticn:Icon %i in dbox %s should be a writeable icon"),
         ((ctl__icon *)entry)->num,((ctl__dbox  *)parent)->name);
       if (icon.data.indirecttext.bufflen<data->len)
       werr(TRUE,
         msgs_lookup(
         "cticnbuf:Icon %i in dbox %s should have a buffer of at least %i positions."),
         ((ctl__icon *)entry)->num,((ctl__dbox  *)parent)->name,data->len);
       if ( ((int)icon.data.indirecttext.validstring>0) &&
            (strncmp(icon.data.indirecttext.validstring,data->valid,
             strlen(data->valid))!=0))
       werr(TRUE,
         msgs_lookup(
         "cticnval:Icon %i in dbox %s should have a validation string of %s"),
         ((ctl__icon *)entry)->num,((ctl__dbox  *)parent)->name,data->valid);
       data->buffer=icon.data.indirecttext.buffer;
       data->flag=TRUE;
       }
       else
       {
       wimp_wind *the_template=
              template_syshandle(((ctl__dbox  *)parent)->template);
       wimp_icon *iconptr=(wimp_icon *)((int)the_template+sizeof(wimp_wind));
       iconptr+=((ctl__icon *)entry)->num;
       ctl__error(67,((ctl__dbox  *)parent)->name,TRUE);
       if
        (((iconptr->flags & wimp_ITEXT)==0) ||
             ((iconptr->flags & wimp_INDIRECT)==0))
       werr(TRUE,
         msgs_lookup(
         "ctindicn:Icon %i in dbox %s should be  an indirected text icon"),
         ((ctl__icon *)entry)->num,((ctl__dbox  *)parent)->name);
       if ((iconptr->flags & (wimp_BWRITABLE<<12))!=(wimp_BWRITABLE<<12))
       werr(TRUE,
         msgs_lookup(
         "ctwrticn:Icon %i in dbox %s should be a writeable icon"),
         ((ctl__icon *)entry)->num,((ctl__dbox  *)parent)->name);
       if (iconptr->data.indirecttext.bufflen<data->len)
       werr(TRUE,
         msgs_lookup(
         "cticnbuf:Icon %i in dbox %s should have a buffer of at least %i positions."),
         ((ctl__icon *)entry)->num,((ctl__dbox  *)parent)->name,data->len);
       if ( ((int)(iconptr->data.indirecttext.validstring)>0) &&
     (strncmp(iconptr->data.indirecttext.validstring,data->valid,
        strlen(data->valid))!=0))
       werr(TRUE,
         msgs_lookup(
         "cticnval:Icon %i in dbox %s should have a validation string of %s"),
         ((ctl__icon *)entry)->num,((ctl__dbox  *)parent)->name,data->valid);
       data->buffer=iconptr->data.indirecttext.buffer;
       data->flag=TRUE;
       }
       break;
 }
}

/* ctl__set_entry_text
 *
 * Set the given entry /icon text.
 *
 */

static void ctl__set_entry_text(void *arg,void *parent,
                    void *entry,ctl_entrytype type,BOOL displayed)
{
 switch(type)
 {
  case ctl_ENTRY_MENUENTRY:
       {
        wimp_menuitem *item;
        item=ctl__menu_item(((ctl__menu  *)parent)->menu_body,
                                        ((ctl__menu_entry *)entry)->num);
        item->iconflags|=wimp_INDIRECT | wimp_ITEXT;
        item->data.indirecttext.buffer=(char *)arg;
        item->data.indirecttext.bufflen=strlen((char *)arg+1);
        item->data.indirecttext.validstring="";
       }
     break;
  case ctl_ENTRY_ICON:
      if (displayed)
      {
       wimp_icon icon;
       wimp_get_icon_info(dbox_syshandle(((ctl__dbox  *)parent)->dbox_body),
                    ((ctl__icon *)entry)->num,&icon);
       if
        (((icon.flags & wimp_ITEXT)==0) || ((icon.flags & wimp_INDIRECT)==0))
       werr(TRUE,
         msgs_lookup(
         "ctindicn:Icon %i in dbox %s should be  an indirected text icon"),
         ((ctl__icon *)entry)->num,((ctl__dbox  *)parent)->name);
        dbox_setfield(((ctl__dbox  *)parent)->dbox_body,
                      ((ctl__icon *)entry)->num,
                      (char *)arg);
      } else
      {
       wimp_wind *the_template=
              template_syshandle(((ctl__dbox  *)parent)->template);
       wimp_icon *iconptr=(wimp_icon *)((int)the_template+sizeof(wimp_wind));
       iconptr+=((ctl__icon *)entry)->num;
       if
        (((iconptr->flags & wimp_ITEXT)==0) ||
                 ((iconptr->flags & wimp_INDIRECT)==0))
          werr(TRUE,
         msgs_lookup(
         "ctindicn:Icon %i in dbox %s should be  an indirected text icon"),
         ((ctl__icon *)entry)->num,((ctl__dbox  *)parent)->name);
       strncpy(iconptr->data.indirecttext.buffer,
              (char *)arg,
              iconptr->data.indirecttext.bufflen);
      }
     break;
 }
}

/* ctl__set_caret_in_entry
 *
 * Set the caret in the given entry /icon.
 *
 */

static void ctl__set_caret_in_entry(void *arg,void *parent,
                    void *entry,ctl_entrytype type,BOOL displayed)
{
 if ((*(int *)arg)++) return;
 if ((type==ctl_ENTRY_ICON) && (displayed))
  {
   wimp_caretstr c;
   c.w=dbox_syshandle(((ctl__dbox  *)parent)->dbox_body);
   c.i=((ctl__icon *)entry)->num;
   c.height=-1;
   c.index=0;
   wimp_set_caret_pos(&c);
  }
}

/*
 *
 * BOOL ctl__chk_disabled(ctl__action *list)
 *
 * Return true if any of the actions on the list are disabled.
 *
 */
static BOOL ctl__chk_disabled(ctl__action *list)
{
 ctl__actionstr act,*actadr;

 if (list==NULL) return FALSE;

 if ((list->type==ctl_ACT_FUNCTION) &&
         ((list->action.address)->disabled)) return TRUE;
 else if ((list->type==ctl_ACT_STRING) || (list->type==ctl_ACT_LABEL))
 {
  act.name=list->action.name;
  actadr=ctl__find_action(&act,ctls->actions,FALSE,FALSE);
  if ((actadr!=NULL) && (actadr->disabled)) return TRUE;
 }

 return ctl__chk_disabled(list->next);
}



/* ctl__enable_entry
 *
 * Fade the given entry / icon if in a displayed dbox.
 *
 */

static void ctl__enable_entry(void *arg,void *parent,
                    void *entry,ctl_entrytype type,BOOL displayed)
{
 switch(type)
 {
  case ctl_ENTRY_MENUENTRY:
       if (!(BOOL)arg)
       {
       ctl__menu_item(((ctl__menu  *)parent)->menu_body,
             ((ctl__menu_entry *)entry)->num)->iconflags|=wimp_INOSELECT;

       }
       else if (!ctl__chk_disabled(((ctl__menu_entry *)entry)->action))
          ctl__menu_item(((ctl__menu  *)parent)->menu_body,
             ((ctl__menu_entry *)entry)->num)->iconflags&=~wimp_INOSELECT;
       break;
  case ctl_ENTRY_ICON:
       if (!(BOOL)arg)
       if (displayed)
       {
       if ((BOOL)arg) wimp_set_icon_state(
                     dbox_syshandle(((ctl__dbox  *)parent)->dbox_body),
                   ((ctl__icon *)entry)->num,wimp_INOSELECT,wimp_INOSELECT);
       else  if (!ctl__chk_disabled(((ctl__icon *)entry)->action))
             wimp_set_icon_state(
                     dbox_syshandle(((ctl__dbox  *)parent)->dbox_body),
                   ((ctl__icon *)entry)->num,0,wimp_INOSELECT);
       }
       ((ctl__icon *)entry)->disabled=(BOOL)arg;
       break;
 }
}

static BOOL ctl__process_help_event(wimp_eventstr *e)
{
 ctl__handlestr *handle=ctl__find_handle(e->data.msg.data.helprequest.m.w);

 if (e->data.msg.data.helprequest.m.w<0) return FALSE;
 if (handle==NULL) /* Not in a window is it in our menu ? */
 {
  _kernel_swi_regs r;
  wimp_msgstr m;
  char token[50];
  int buf[30],i;
  ctl__menu *a_menu;
  ctl__menu_entry *an_entry;
  r.r[0]=1;
  r.r[1]=(int)buf;
  r.r[2]=e->data.msg.data.helprequest.m.w;
  r.r[3]=e->data.msg.data.helprequest.m.i;
  _kernel_swi(XOS_Bit | Wimp_GetMenuState,&r,&r);
  if (buf[0]==-1) return FALSE; /* No Menu Open */
  a_menu=ctls->top_menu;
  for (i=0;buf[i+1]!=-1;i++)
     a_menu=(ctl__menu *)(ctl__find_entry(buf[i]+1,a_menu)->action->args);

  an_entry=ctl__find_entry(buf[i]+1,a_menu);
  if (an_entry==NULL) an_entry=ctl__find_entry(-1,a_menu);

  if ((an_entry!=NULL) && (an_entry->help_action!=NULL))
  {
   ctl__run(an_entry->help_action);
   return TRUE;
  }

  if (a_menu->mih_token==NULL) return FALSE;
  sprintf(token,"%s%i\0",a_menu->mih_token,1+buf[i]);
  strcpy(m.data.helpreply.text,msgs_lookup(token));
  m.hdr.action=wimp_MHELPREPLY;
  m.hdr.your_ref=e->data.msg.hdr.my_ref;
  m.hdr.my_ref=0;
  m.hdr.size=220;
  wimp_sendmessage(wimp_ESEND,&m,e->data.msg.hdr.task);
  return TRUE;
 }
 else if (handle->help_token==NULL) return FALSE;
 else
 {
  wimp_msgstr m;
  char token[50];
  int inum;
  if ((inum=e->data.msg.data.helprequest.m.i)>=0)
  {
   if (handle->the_dbox!=NULL)
   {
    ctl__icon *an_icon=ctl__find_icon(inum,handle->the_dbox);
    if (an_icon==NULL) an_icon=ctl__find_icon(-1,handle->the_dbox);
    if ((an_icon!=NULL) && (an_icon->help_action!=NULL))
    {
     ctl__run(an_icon->help_action);
     return TRUE;
    }
   }
   sprintf(token,"%s%i\0",handle->help_token,inum);
   strcpy(m.data.helpreply.text,msgs_lookup(token));
  } else strcpy(m.data.helpreply.text,msgs_lookup(handle->help_token));
  m.hdr.action=wimp_MHELPREPLY;
  m.hdr.your_ref=e->data.msg.hdr.my_ref;
  m.hdr.my_ref=0;
  m.hdr.size=220;
  wimp_sendmessage(wimp_ESEND,&m,e->data.msg.hdr.task);
 }
 return TRUE;
}




static BOOL ctl__process_key_event(wimp_eventstr *e)
{
 ctl__handlestr *handle;
 ctl__key *key;
 if (handle=ctl__find_handle(e->data.key.c.w),handle==NULL) return FALSE;
 ctls->current_handle=handle;
 if (ctls->current_handle->the_ctl==NULL) return FALSE;
 for (key=ctls->current_handle->the_ctl->keys;key!=NULL;key=key->next)
 if (key->keycode==e->data.key.chcode)
 {
  ctls->return_code=e->data.key.chcode;
  if (!key->disabled) ctl__run(key->action);
  return TRUE;
 } else
 if (key->keycode==-1)
 {
  ctls->return_code=e->data.key.chcode;
  if (!key->disabled) ctl__run(key->action);
  return TRUE;
 }
 return FALSE;
}

static BOOL ctl__process_mouse_event(wimp_eventstr *e)
{
 ctl__handlestr *handle;
 if (e->data.but.m.bbits & 2) return FALSE;
 handle=ctl__find_handle((e->data.but.m.w<0)?win_ICONBAR:e->data.but.m.w);
 if (handle==NULL) return FALSE;
 ctls->current_handle=handle;
 if (ctls->current_handle->the_ctl==NULL) return FALSE;
 if (ctls->current_handle->the_ctl->onclick==NULL) return FALSE;
 if (!ctl__chk_disabled(ctls->current_handle->the_ctl->onclick))
 {
  ctls->return_code=e->data.but.m.bbits;
  ctl__run(ctls->current_handle->the_ctl->onclick);
  return TRUE;
 }
 return FALSE;
}

static BOOL ctl__process_event(wimp_eventstr *e)
{
 switch (e->e)
 {
  default:
          return FALSE;
  case wimp_EKEY:
          return ctl__process_key_event(e);
  case wimp_EBUT:
          return ctl__process_mouse_event(e);

  case wimp_ESEND:
  case wimp_ESENDWANTACK:
          if (e->data.msg.hdr.action==wimp_MHELPREQUEST)
          return ctl__process_help_event(e); else return FALSE;
 }
}



/************************* External functions ****************************/

/* ctl_init():
 *          BOOL load - if true load control file and ignore any
 *                      undefined actions.
 *
 */

void ctl_init(BOOL load)
{
 ctl__init();
 if (load) ctl__load(FALSE);
}

/* ctl_load()
 *           Load control file (Can only be called if ctl_init was
 *           called with FALSE. Generate FATAL error if undefined action
 *           in control file.
 */

void ctl_load()
{
 if (ctls->state!=ctl_ST_PRELOAD) ctl__error(4,NULL,TRUE);
 ctl__load(TRUE);
}

/*
 * BOOL ctl_attach_menu(event_w,char ctlname,char menuname);
 *
 *
 *
 */

BOOL ctl_attach_menu(event_w w,char *ctlname,char *menuname,void *userhandle)
{
 ctl            *the_ctl;
 ctl__menu      *the_menu;
 ctl__handlestr *handle;

 the_ctl=ctl__find_ctl(ctlname);
 the_menu=ctl__find_menu(menuname,the_ctl);

 if (the_menu->external)
      ctl__error(66,menuname,TRUE);

 handle=ctl__add_handle(w,userhandle,the_ctl,the_menu);

 return event_attachmenumaker(w,
                  (event_menu_maker) ctl__menu_maker,
                  (event_menu_proc)  ctl__menu_select,
                  handle);
}


void ctl_add_action(char *name,char *syntax,ctl_action_proc func,void *arg)
{
 ctl__add_action(name,syntax,func,arg,FALSE);
}

void ctl_tick_action(char *ctlname,char *actname)
{
 ctl *a_ctl;
 a_ctl=ctl__find_ctl(ctlname);
 ctl__find_action_in_ctl(a_ctl,actname,ctl__tick_entry,(void *)TRUE);
}

void ctl_untick_action(char *ctlname,char *actname)
{
 ctl *a_ctl;
 a_ctl=ctl__find_ctl(ctlname);
 ctl__find_action_in_ctl(a_ctl,actname,ctl__tick_entry,(void *)FALSE);
}

void ctl_set_action_text(char *ctlname,char *actname,char *text)
{
 ctl *a_ctl;
 a_ctl=ctl__find_ctl(ctlname);
 ctl__find_action_in_ctl(a_ctl,actname,ctl__set_entry_text,(void *)text);
}

void ctl_set_caret_in_action(char *ctlname,char *actname)
{
 int f=0;
 ctl *a_ctl;
 a_ctl=ctl__find_ctl(ctlname);
 ctl__find_action_in_ctl(a_ctl,actname,ctl__set_caret_in_entry,(void *)&f);
 if (f>1) ctl__error(63,actname,TRUE);
}

void ctl_enable_action(char *ctlname,char *actname)
{
 ctl *a_ctl;
 ctl__key *key;
 ctl__actionstr the_action,*the_address;
 a_ctl=ctl__find_ctl(ctlname);
 the_action.name=actname;
 the_address=ctl__find_action(&the_action,ctls->actions,FALSE,FALSE);
 if (the_address==NULL) ctl__error(50,actname,TRUE);
 the_address->disabled=FALSE;
 ctl__find_action_in_ctl(a_ctl,actname,ctl__enable_entry,(void *)TRUE);
 for (key=a_ctl->keys;key!=NULL;key=key->next)
  key->disabled=ctl__chk_disabled(key->action);
}

void ctl_disable_action(char *ctlname,char *actname)
{
 ctl *a_ctl;
 ctl__actionstr the_action,*the_address;
 ctl__key *key;
 a_ctl=ctl__find_ctl(ctlname);
 the_action.name=actname;
 the_address=ctl__find_action(&the_action,ctls->actions,FALSE,FALSE);
 if (the_address==NULL) ctl__error(50,actname,TRUE);
 the_address->disabled=TRUE;
 ctl__find_action_in_ctl(a_ctl,actname,ctl__enable_entry,(void *)FALSE);
 for (key=a_ctl->keys;key!=NULL;key=key->next)
    if (ctl__find_action_in_list(actname,key->action)) key->disabled=TRUE;
}

char *ctl_make_action_writeable(
                   char *ctlname,
                   char *actname,
                   int len,
                   char *valid)
{
 ctl *a_ctl;
 ctl__actionstr the_action /*,*the_address*/;
 ctl__indirectstr data;
 data.len=len;
 data.valid=valid;
 data.buffer=NULL;
 data.flag=FALSE;
 a_ctl=ctl__find_ctl(ctlname);
 the_action.name=actname;
/* the_address=ctl__find_action(&the_action,ctls->actions,FALSE,FALSE);
 if (the_address==NULL) ctl__error(51,actname,TRUE); */
 ctl__find_action_in_ctl(a_ctl,actname,ctl__make_entry_writeable,
                                                        (void *)&data);
 return data.buffer;
}

void ctl_find_entries(char *ctlname,char *actname,void ((*func)()))
{
 ctl *a_ctl;
 a_ctl=ctl__find_ctl(ctlname);
 ctl__find_action_in_ctl(a_ctl,actname,ctl__apply_function,(void *)func);
}

void ctl_process(void)
{
 wimp_eventstr e;
 wimpt_poll(event_getmask(),&e);
 if (!ctl__process_event(&e))
 {
  wimpt_fake_event(&e);
  event_process();
 }
}

void ctl_attach_external_menu(char *ctlname,char *menuname,wimp_menustr *m)
{
 ctl             *a_ctl;
 ctl__menu       *a_menu;
 ctl__menu_entry *an_entry;
 wimp_menuitem   *item;

 a_ctl=ctl__find_ctl(ctlname);
 a_menu=ctl__find_menu(menuname,a_ctl);
 if (!a_menu->external) ctl__error(65,menuname,TRUE);
 a_menu->menu_body=(menu)m;
 a_menu->entrycount=ctl__count_menu_entries(m);
 an_entry=ctl__find_entry(-1,a_menu);
 item=(wimp_menuitem *)(m+1);
 do
   {
     if (an_entry==NULL) item->iconflags|=wimp_INOSELECT;
     else
     {
/*    item->iconflags&=~wimp_INOSELECT; */
      if ((an_entry->action != NULL) &&
          (an_entry->action->type==ctl_ACT_FUNCTION) &&
          (an_entry->action->action.address->function.function==
                                 ctl__submenu_request)
         )
       {
        item->flags|=wimp_MSUBLINKMSG;
      if ((an_entry->action->action.address->function.handle==(void *)1) ||
          (an_entry->action->action.address->function.handle==(void *)3)
         )
       item->submenu=
            menu_syshandle(((ctl__menu *)an_entry->action->args)->menu_body);
       else item->submenu=(wimp_menustr *)1;
       }
      }
      if ((item->flags & wimp_MLAST)!=0) break;
      item++;
    }
     while (TRUE);

 item=(wimp_menuitem *)(m+1);
 for (an_entry=a_menu->entries;an_entry!=NULL;an_entry=an_entry->next)
 {
  if ((an_entry->num!=-1) &&
       ((an_entry->num<0) || (an_entry->num>a_menu->entrycount)))
        werr(TRUE,
        msgs_lookup("inveent:Invalid entry number (%i) in external menu %s.")
        ,an_entry->num,menuname);
  if (an_entry->num>0)
  {
   (item+an_entry->num-1)->iconflags&=~wimp_INOSELECT;
   if ((an_entry->action != NULL) &&
      (an_entry->action->action.name!=NULL) &&
      (an_entry->action->type==ctl_ACT_FUNCTION) &&
      (an_entry->action->action.address->function.function==
                                 ctl__submenu_request) &&
      ((an_entry->action->action.address->function.handle==(void *)1) ||
       (an_entry->action->action.address->function.handle==(void *)3)
      )
     )
    {
     ctl__menu *submenu=(ctl__menu *) an_entry->action->args;
     if ((submenu->external) && (submenu->menu_body==NULL))
     {
      submenu->parent=(item+an_entry->num-1);
     }
     else
     {
      (item+an_entry->num-1)->submenu=menu_syshandle(submenu->menu_body);
     }
   }
  }
 }
}


char * ctl_return_external_hit(void)
{
 return ctls->external_hit;
}

void ctl_help_reply(char *text)
{
 wimp_msgstr m;

 strcpy(m.data.helpreply.text,text);
 m.hdr.action=wimp_MHELPREPLY;
 m.hdr.your_ref=wimpt_last_event()->data.msg.hdr.my_ref;
 m.hdr.my_ref=0;
 m.hdr.size=220;
 wimp_sendmessage(wimp_ESEND,&m,wimpt_last_event()->data.msg.hdr.task);
}