/* MakeMenu V1.0 (14-oct-90) - by Hans Jansen
 *
 * This program will generate C source to build Intuition menus, from
 * a simple description file. See MakeMenu.Doc for details on usage.
 *
 */

#define  DISABLE	  0
#define  DEFAULT	  1
#define  ENABLE		  1
#define  TOGGLE		  2
#define  CHECK		  4
#define  SELECT		  8

#define  CHAR_WIDTH	  8

#include <intuition/intuition.h>
#include <stdio.h>

#define NO_ERROR	  0
#define ERROR_NO_COMMA	  1
#define ERROR_BAD_TEXT	  2
#define ERROR_NO_MENU	  3
#define ERROR_NO_ITEM	  4
#define ERROR_NO_DATA	  5
#define ERROR_BAD_NUMBER  6
#define ERROR_BAD_COMMAND 7
#define MAX_LINE	132

char cline[MAX_LINE];			/* input line			*/
char RecBuf[BUFSIZ];			/* input buffer			*/
char *ifname, *ofname;			/* File name pointers		*/
FILE *ofile;				/* output file handle		*/
int  EndOfFile;				/* true = end of file		*/
int  lcnt;				/* current input line count	*/

struct IntuiText *CText;		/* Current Text Item pointer	*/
struct IntuiText *AllText;		/* All text items		*/
struct MenuItem  *CItem;		/* Current Menu Item		*/
struct MenuItem  *CSItem;		/* Current Sub Menu Item	*/
struct Menu      *CMenu, *AllMenu;	/* Menu pointers		*/

main (argc, argv)
  int   argc;				/*number of arguments		*/
  char  *argv[];			/*array of ptrs to arg strings	*/

{
  FILE	*ifile;				/* input File pointer		*/
  int	i, j;
  int	result, error;
  char	*Scan();
  int	GetLine();
  char	*cptr;
  char  etype[5];

/* get the file names and open the files  */
/* initialize input buffers and variables */

  AllText = NULL;
  CText   = NULL;
  CMenu   = NULL;
  CItem   = NULL;
  CSItem  = NULL;
  AllMenu = NULL;
  result  = NO_ERROR;
  error   = 0;
  lcnt    = 0;

  if (argc != 3)
      printf ("usage: MakeMenu <inputfile> <outputfile>\n");

  for (ifname = NULL, i=1; i<argc && ifname == NULL; i++)
    if ( *argv[i] != '-') ifname = argv[i];

  for (ofname = NULL, i=i; i<argc && ofname == NULL; i++)
    if ( *argv[i] != '-') ofname = argv[i];

  EndOfFile = FALSE; /* no end of file  */
  ifile      = fopen (ifname, "r");  /* open file for reading */
  if (ifile == NULL) {
    printf (" Cannot open file %s for input!\n", ifname);
    exit (10);
  };

  while ((j = GetLine (ifile, cline, sizeof (cline))) > 0) {
    cptr = &cline[0];
    for (i = 0; i < sizeof (etype) -1; i++)
      etype[i] = toupper (*(cptr + i));
    etype[i] = '\0';
    cptr = Scan (cptr, ',', TRUE);
    if (strcmp ("MENU", etype) == 0)
      result = Process_Menu (cptr);		/* do a Menu */
    else if (strcmp ("ITEM", etype) == 0)
      result = Process_Item (cptr, 0);		/* do an Item */
    else if (strcmp ("SUBI", etype) == 0)
      result = Process_Item (cptr, 1);		/* do a SubItem */
    else
	result = ERROR_BAD_COMMAND;
    if (result != NO_ERROR) {
      error++;
      printf (" Line %d has syntax error:%s\n", lcnt, cline);
    };
  };
  fclose (ifile);
  if (error == 0) Do_Output ();
}

char *Scan (ptr, c, control)
  char *ptr;
  char c;
  int control;

{
  while (*ptr != NULL && ((control && *ptr != c) || (! control && *ptr == c)))
     ptr++;
  return (ptr);  /* return the new pointer */
}

GetLine (fptr, line, lsize)	/* get a line from buffer	*/
  FILE *fptr;			/* pointer to file to read from */
  char line[];			/* destination of characters	*/
  int  lsize;			/* maximum size for destination	*/

{
    lcnt++;         /* bump line count */

    if (fgets (line, lsize, fptr) == NULL) {
       EndOfFile = TRUE;
       return (0);
    }
    else {
       line[strlen (line) - 1] = '\0';		/* discard newline character */
       return (strlen (line));
    }
}

int Process_Menu (ptr)
  char *ptr;
  
{
  char *tptr;
  int len;
  struct Menu *last;

  if (AllMenu == NULL) {	/* first menu item */
    AllMenu = (struct Menu *) malloc (sizeof (struct Menu));
    CMenu = AllMenu;		/* Set current pointer */
  }
  else {			/* add new one to list */
    last = CMenu;
    CMenu = (struct Menu *) malloc (sizeof(struct Menu));
    last->NextMenu = CMenu;
  };

/* Build Menu structure */

  if (*ptr != ',') return (ERROR_NO_COMMA);
  ptr++;

/* process disable-flag, if any */

  if (*ptr == 'D' || *ptr == 'd')
      CMenu->Flags = DISABLE;
  else
      CMenu->Flags = ENABLE;
  ptr = Scan (ptr, ',', TRUE);
  if (*ptr != ',') return (ERROR_NO_COMMA);
  ptr++;

  tptr = Scan (ptr, '\0', TRUE); /* find end of string */
  len  = tptr-ptr;
  CMenu->MenuName = (BYTE *) malloc (len+1);
  strcpy (CMenu->MenuName, ptr);
  CMenu->NextMenu = NULL;
  CMenu->FirstItem = NULL;
  CItem = NULL;
  CMenu->Width = len + 3 ;
  return (NO_ERROR);
}

int Process_Item (ptr, type)
  char *ptr;
  int type;    /* 0 = Item, 1 = SubItem */

{
  struct IntuiText *Process_Text();
  struct MenuItem  *ThisItem;
  struct MenuItem  *lastI;
  struct MenuItem  *lastSI;

  if (CMenu == NULL)               return(ERROR_NO_MENU);
  if (type  == 1 && CItem == NULL) return(ERROR_NO_ITEM);
  if (*ptr  != ',')                return(ERROR_NO_COMMA);

  if (type  == 0) {			/* Build a MenuItem */
    lastI  = CItem;
    CSItem = NULL;  /* clear current Sub Item Flag */
    CItem  = (struct MenuItem *) malloc (sizeof (struct MenuItem));

    if (lastI == NULL)			/* First Item for Menu */
      CMenu->FirstItem = CItem;		/* link it to the menu */
    else
      lastI->NextItem = CItem;		/* link it to the item list */
    ThisItem = CItem;			/* build it */
  }
  else {				/* Build a SubItem */
    lastSI = CSItem;
    CSItem = (struct MenuItem *) malloc (sizeof (struct MenuItem));
    if (lastSI == NULL)			/* First Sub Item for Menu Item */
      CItem->SubItem = CSItem;		/* link in the sub items */
    else
      lastSI->NextItem = CSItem;
    ThisItem = CSItem;
  };

/* Process flags, name, select and command */

  ptr++;
  ThisItem->Flags = ENABLE;
  while (*ptr != ',') {
      if (*ptr == 'D' || *ptr == 'd') ThisItem->Flags &= ~ENABLE;
      else if (*ptr == 'T' || *ptr == 't') ThisItem->Flags |= TOGGLE;
      else if (*ptr == 'C' || *ptr == 'c') ThisItem->Flags |= CHECK;
      else if (*ptr == 'S' || *ptr == 's') ThisItem->Flags |= SELECT;
      ptr++;
  }

  ptr++;
   
  ThisItem->ItemFill = (APTR) Process_Text (ptr);
  if (ThisItem->ItemFill == NULL) return (ERROR_BAD_TEXT);

  ptr = Scan (ptr, ',', TRUE); /* find next comma */
  if (*ptr != ',') return (ERROR_NO_COMMA);
  ptr++;

/* Select name? */

  ThisItem->SelectFill = (APTR) Process_Text (ptr);

  ptr = Scan (ptr, ',', TRUE); /* find next comma */
  if (*ptr != ',') return (ERROR_NO_COMMA);
  ptr++;

/* Command ? */
  ThisItem->NextItem      = NULL;
  ThisItem->SubItem       = NULL;
  ThisItem->Command       = *ptr;
  ThisItem->MutualExclude = 0L;
  return (NO_ERROR);
}

struct IntuiText *Process_Text (ptr)
  char *ptr;
{
  
  struct IntuiText *dptr, *lasText;
  char *tptr;

  if (*ptr == ',') return (NULL);

  lasText = AllText;
  while (lasText != NULL) {	 /* search end of saved-texts list */
    CText = lasText;
    lasText = lasText->NextText;
  };
  lasText = CText;
  CText = (struct IntuiText *) malloc (sizeof (struct IntuiText));
  CText->NextText = NULL;
  if( AllText == NULL )
    AllText = CText;
  else
    lasText->NextText = CText;

  tptr = Scan (ptr, ',', TRUE);
  CText->IText = (UBYTE *) malloc (tptr-ptr+1);
  strncpy (CText->IText, ptr, tptr-ptr);
  if (CMenu->Width <= tptr-ptr) CMenu->Width = tptr-ptr;

/* check for duplicate, eliminate this one if duplicate */
  
  dptr = AllText;
  while (dptr != CText) {
    if (strcmp (dptr->IText, CText->IText) == 0 ) {
      free ((char *) (CText->IText));
      free ((char *) CText);
      CText = dptr;
      lasText->NextText = NULL;
    }
    else dptr = dptr->NextText;
  }
  return (CText);
}

Do_Output()

{
  char nxt, quote, Flag[10];
  char Mptr[14];
  int Menu, Item, left, top, width, height;
  int stop;
  char rest[60];

  ofile = fopen (ofname, "w");  /* open file for writing */
  if (ofile == NULL) {
    printf (" Cannot open file %s for output!\n", ofname);
    exit (10);
  }

/* Output the IntuiText structure first */

  quote = '"';
  fprintf (ofile, "/*****************************************/\n");
  fprintf (ofile, "/*      This code was generated by:      */\n");
  fprintf (ofile, "/* MakeMenu V1.0 (Hans Jansen  7-oct-90) */\n");
  fprintf (ofile, "/*****************************************/\n");
  fprintf (ofile, "#include <exec/types.h>\n");
  fprintf (ofile, "#include <intuition/intuition.h>\n");

  CText = AllText;
  if (AllText != NULL) {
    fprintf (ofile, "\nstruct IntuiText IText[] =\n");
    fprintf (ofile, "  {\n");
  };

  while (CText != NULL) {		 /* for all text to output */
    if (CText->NextText == NULL) nxt = ' ';
    else nxt = ',';
    fprintf (ofile, "    { 0, 1, JAM2, CHECKWIDTH, 0, NULL, %c%s%c, NULL }%c\n",
      quote, CText->IText, quote, nxt);
    CText = CText->NextText;
  };

  if (AllText != NULL) fprintf (ofile,"  };\n\n");

  CMenu = AllMenu;
  Menu = 0;
  left = 0;
  Item = 0;
  height = 10;

  while (CMenu != NULL) {	     /* first output the subitems, if any */
    Item = 0;
    CItem = CMenu->FirstItem;
    while (CItem != NULL) {
      CSItem = CItem->SubItem;
      if (CSItem != NULL) Do_Out_Sub (Menu,Item);
      Item++;
      CItem = CItem->NextItem;
    };
    Menu++;
    CMenu = CMenu->NextMenu;
  }

  CMenu = AllMenu;
  Menu = 0;
  left = 0;
  Item = 0;
  height = 10;

  while (CMenu != NULL) {	       /* now output the items */
    Item = 0;
    CItem = CMenu->FirstItem;
    CSItem = CItem->SubItem;
    top  = 0;
    fprintf (ofile, "struct MenuItem M%d[] = \n",Menu);
    fprintf (ofile, "  {\n");
    top  = 0;
    width =  CMenu->Width * CHAR_WIDTH;

    while (CItem != NULL) {
      Item++;
      CSItem = CItem->SubItem;

      if (CItem->NextItem == NULL) fprintf (ofile, "    {NULL, ");
      else fprintf (ofile, "    {&M%d[%2d], ", Menu, Item);
      fprintf (ofile, "%3d, %3d, %3d+CHECKWIDTH, %3d,\n",
			left, top, width, height);
      fprintf (ofile, "      ITEMTEXT|HIGHCOMP");
      if ((CItem->Flags & ENABLE) == ENABLE) fprintf (ofile, "|ITEMENABLED");
      if ((CItem->Flags & TOGGLE) == TOGGLE) fprintf (ofile, "|MENUTOGGLE");
      if ((CItem->Flags & CHECK)  == CHECK)  fprintf (ofile, "|CHECKIT");
      if ((CItem->Flags & SELECT) == SELECT) fprintf (ofile, "|CHECKED");
      if (CItem->Command != NULL && CItem->Command != ' ')
          fprintf (ofile, "|COMMSEQ");
      fprintf (ofile, ",\n      0, (APTR)&IText[%2d], ",
			Index(CItem->ItemFill));
      if (CItem->SelectFill == NULL) fprintf (ofile, "NULL, ");
      else fprintf (ofile, "(APTR)&IText[%2d], ", Index(CItem->SelectFill));
      if (CItem->Command == NULL || CItem->Command == ' ')
          fprintf (ofile, "NULL, ");
      else fprintf (ofile, "'%c', ", CItem->Command);
      if (CSItem == NULL) fprintf (ofile, "NULL, MENUNULL}");
      else fprintf (ofile, "&M%dI%d[0], MENUNULL}", Menu, (Item-1));
      if (CItem->NextItem == NULL) fprintf (ofile, "\n");
      else fprintf (ofile, ",\n");

      CItem = CItem->NextItem;
      top   = top + height;
    };
    fprintf (ofile, "  };\n\n");
    CMenu = CMenu->NextMenu;
    Menu++;
  };

  CMenu = AllMenu;
  fprintf (ofile, "struct Menu TheMenu[] =\n");
  fprintf (ofile, "  {\n");
  left = 0;
  top  = 0;
  Menu = 0;
  
  while (CMenu != NULL) {	       /* finally, the menus themselves */
    Menu++;
    width = CMenu->Width * CHAR_WIDTH;
    if (CMenu->NextMenu == NULL) {	/* the NextMenu pointer */
      sprintf (Mptr, "NULL       ");
      nxt = ' ';
    }
    else {
      sprintf (Mptr, "&TheMenu[%2d]", Menu);
      nxt = ',';
    };
    (void) strncpy (rest, "                               ", 25);
    stop = CMenu->Width - strlen (CMenu->MenuName);    /* the name string */
    if (stop <= 0)
      (void) strncpy (rest, CMenu->MenuName, 25);
    else {
      stop = stop / 2;
      (void) strncpy (&rest[stop], CMenu->MenuName, 25-stop);
      (void) strncat (rest, "                         ", 25);
    };

    quote = '"';
    height = 10;
    fprintf (ofile, "    {%12s, %3d, %3d, %3d+CHECKWIDTH, %3d,\n",
		    Mptr, left, top, width, height);
    if ((CMenu->Flags & ENABLE) == ENABLE)
        fprintf (ofile, "MENUENABLED");
    else
        fprintf (ofile, "0");
    fprintf (ofile, ",%c%s%c, &M%d[0]}%c\n",
		    quote, rest, quote, (Menu-1), nxt);
    left = left + width + 4*CHAR_WIDTH;
    CMenu = CMenu->NextMenu;
  };
  fprintf (ofile, "  };\n\n");
  fprintf (ofile, "struct Menu *MyMenu = &TheMenu[0];\n\n");

  fprintf (ofile, "/*****************************************/\n");
  fprintf (ofile, "/*         End of generated code         */\n");
  fprintf (ofile, "/*****************************************/\n");

  fclose (ofile);
}

int Index (ptr)   /* Find the position index */
  struct IntuiText *ptr;
 {
  int idx;

  idx = 0;
  CText = AllText;

  while (CText != NULL && ptr != CText) {
    idx++;
    CText = CText->NextText;
  };
  return (idx);
}

Do_Out_Sub (Menu, Item)	       /* output an item's SubItems */
  int Menu, Item;
{ 
  int SItem;
  int stop, width, sleft;
  int top, height;
  char Mptr[14];
  char rest[60];
  char nxt, Flag[10];

  SItem = 0;
  top = 0;
  height = 10;
  fprintf (ofile, "struct MenuItem M%dI%d[] = \n", Menu, Item);
  fprintf (ofile, "  {\n");
  width = CMenu->Width*CHAR_WIDTH;
  SItem = 0;
  while (CSItem != NULL) {
    SItem++;
    sleft =  (width * 7) / 10;
    stop  =  top + 2;
    if (CSItem->NextItem == NULL) fprintf (ofile, "    {NULL, ");
    else fprintf (ofile, "    {&M%dI%d[%2d], ", Menu, Item, SItem);
    fprintf (ofile, "%3d, %3d, %3d+CHECKWIDTH, %3d,\n",
		    sleft, stop, width, height);
    fprintf (ofile, "      ITEMTEXT|HIGHCOMP");
    if ((CSItem->Flags & ENABLE) == ENABLE) fprintf (ofile, "|ITEMENABLED");
    if ((CSItem->Flags & TOGGLE) == TOGGLE) fprintf (ofile, "|MENUTOGGLE");
    if ((CSItem->Flags & CHECK)  == CHECK)  fprintf (ofile, "|CHECKIT");
    if ((CSItem->Flags & SELECT) == SELECT) fprintf (ofile, "|CHECKED");
    if (CSItem->Command != NULL && CSItem->Command != ' ')
        fprintf (ofile, "|COMMSEQ");
    fprintf (ofile, ",\n      0, (APTR)&IText[%2d], ",
		    Index(CSItem->ItemFill));
    if (CSItem->SelectFill == NULL) fprintf (ofile, "NULL, ");
    else fprintf (ofile, "(APTR)&IText[%2d], ", Index(CSItem->SelectFill));
    if (CSItem->Command == NULL || CSItem->Command == ' ')
        fprintf (ofile, "NULL, ");
    else fprintf (ofile, "'%c', ", CSItem->Command);
    fprintf (ofile, "NULL, MENUNULL}");
    if (CSItem->NextItem == NULL) fprintf (ofile, "\n");
    else fprintf (ofile, ",\n");

    CSItem = CSItem->NextItem;
    top = top + height;
  };
  fprintf (ofile,"  };\n\n");
}
