  1: /************************************************************
  *  Program: RMENU Menu Interpreter
  *  Module: rmenu1.c
  *      Main and Utility Functions
  *  Written by: Leor Zolman, 7/91
  ************************************************************/
 
 #include "cmenu.h"
 #include "rcmenu.h"
 
 #if __STDC__
 #   pragma hdrstop
 #   include <stdarg.h>
 #else
 #   include <varargs.h>
 #endif
 
 /********************** Global Data *************************/
 
 LEVELS LMenus[MAX_NEST];
 int nestlev;                           /* current nesting level        */
 int echox, echoy;                      /* Location of item # echo area */
 int debug;                             /* true to display sys commands */
 char SysShell[80];                     /* System command interpreter   */
 
 /************************************************************
  * main():
  *  Initialize the program and run
  *  the master menu
  ************************************************************/
 
 main(argc, argv)
 int argc;
 char **argv;
 {
     char *mname = "menu";
     int i, j;
 
     debug = 0;                         /* No debugging by default      */
 
                                       /* Process command line options: */
     for (i = 1; i < argc; i++)
         if (argv[i][0] == '-')
         {
             switch (tolower(argv[i][1]))
             {
                 case 'd':   debug = TRUE;
                             break;
 
                 default:    fprintf(stderr, "Unknown option: '%s'\n",
                                         argv[i]);
                             exit(0);
             }
             for (j = i; j < argc - 1; j++)                 /* compress */
                 argv[j] = argv[j + 1];                     /* arg list */
             argc--;
             i--;
         }
 
     init_win();                                   /* initialize curses */
 
     if (argc == 2)
         mname = argv[1];
 
     nestlev = 0;
     do_menu("", mname);
     free_menus();
     close_win();
     return OK;
 }
 
 
 /************************************************************
  * do_menu(): 
  *  Run a compiled menu file, supporting recursive
  *  calls for nested external menus.
  *  Default command/menu path is supplied as "path".
  ************************************************************/
 
 int do_menu(path, file)
 char *path, *file;
 {
     char pathname[MAX_PATH];
     
     strcpy(pathname, path);
     if (*path) 
         strcat(pathname, "/");
     strcat(pathname, file);
     strcat(pathname, ".mnc");
 
     if (ld_menu(pathname) == ERROR)
         return EXITALL;
 
     return sub_menu(0, path);                 /* run main menu in file */
 }
 
         
 /************************************************************
  * ld_menu():
  *  Load a compiled menu object file from disk,
  *  into nesting level nestlev, allocating memory
  *  as required.
  *  For each menu in the menu file being loaded,
  *  compute screen placement as per spacing/columns
  *  specifications and the total number of items.
  ************************************************************/
 
 int ld_menu(path)
 char *path;
 {
     LEVELS *Levp = &LMenus[nestlev];
     MENU *Mp;
     ITEM *Ip;
     MENU2 *M2p;
 
     FILE *fp;
     int widest;
     int i, j, k, l;
     
     if ((fp = fopen(path, "rb")) == NULL)
         return fatal("Can't open %s", path);
     
     if (fread((Void *) &Levp->n_menus, sizeof (int), 1, fp)
                     != 1)
         return fatal("Error reading menu count from %s", path);
     
     for (i = 0; i < Levp->n_menus; i++)
     {
         if (i < Levp -> max_menus)
             M2p = Levp -> Menus[i];
         else                               /* allocate memory for Menu */
         {
             M2p = Levp -> Menus[i] = (MENU2 *) malloc(sizeof(MENU2));
             if (M2p == NULL)
                 return fatal("Out of memory loading %s", path);
             Levp -> max_menus++;
             M2p -> most_items = 0;
         }
 
         Mp = &M2p -> Menu;
         
         if (fread((Void *) Mp, sizeof(MENU), 1, fp) != 1)
             return fatal("Error reading Menu data from %s", path);
         
                            /* Now determine screen placement strategy. */
 
         placement(Mp);
 
         M2p -> field_len = min(MAX_TXTWID, 
                     (SCREEN_COLS / Mp -> columns) - 5);
 
                /* Read in each item, and assign screen coordinate info */
                /* to each on-the-fly as per spacing/column parameters  */
         
         for (j = 0; j < Mp -> nitems; j++)
         {
             if (j < M2p -> most_items)
                 Ip = M2p -> Items[j];
             else
             {
                 Ip = M2p -> Items[j] = (ITEM *) malloc(sizeof(ITEM));
                 if (Ip == NULL)
                    return fatal("Out of RAM in %s, menu #%d/item #%d",
                         path, i,j);
                 M2p -> most_items++;
             }
             if (fread((Void *) Ip, sizeof(ITEM), 1, fp) != 1)
                 return fatal("Error reading %s", path);
 
             Ip -> text[M2p -> field_len - 1] = '\0';       /* truncate */
 
             if ((Ip -> acttyp == ACT_LMENU ||
                  Ip -> acttyp == ACT_EMENU) &&
                 strlen(Ip -> text) + 6 < M2p -> field_len)
             {
                 int limit;
                 
                 limit = min (Mp -> widest + 2,
                         M2p -> field_len - 7);
                 for (k = strlen(Ip -> text);
                         k < limit && k < (MAX_TXTWID - 6); k++)
                     strcat(Ip -> text, " ");
                 strcat(Ip -> text, "(MENU)");
             }
 
             M2p -> coords[j].ypos = 
                     HOME_Y + (j % (MAX_IROWS / Mp -> spacing))
                                 * Mp -> spacing;
 
             widest = Mp -> widest;
             M2p -> coords[j].xpos = HOME_X + 
                     (
                       (Mp -> columns == 1)
                             ? 
                       (
                         (SCREEN_COLS - HOME_X -
                          (widest + ((widest < 66) ? 14 : 6) )) / 2
                       )
                             :
                       (j / (MAX_IROWS / Mp -> spacing) *
                            (SCREEN_COLS / Mp -> columns))
                     );
 
 
             M2p -> coords[j].spaces_needed = 
                     min(M2p -> field_len, Mp -> widest)
                             - strlen(Ip -> text);
         }
     }
     fclose(fp);
     return OK;
 }
 
 
 /************************************************************
  * placement():
  *  Calculate values for columns and spacing 
  *  for the given Menu:
  ************************************************************/
 
 Void placement(Mp)
 MENU *Mp;
 {
     int columns = Mp -> columns;
     int spacing = Mp -> spacing;
     int nitems = Mp -> nitems;
 
                            /* Step 1: fill in real values if either    */
                            /*    columns or spacing was not specified: */
 
     if (spacing == DEFAULT && columns == DEFAULT)
     {
         if (nitems <= (MAX_IROWS / 2))
         {
             Mp -> columns = 1;
             Mp -> spacing = 2;
         }
         else if (nitems <= MAX_IROWS)
             if ((Mp -> widest * 2 + 5) <= SCREEN_COLS)
                 Mp -> columns = Mp -> spacing = 2;
             else
                 Mp -> columns = Mp -> spacing = 1;
         else
         {
             Mp -> spacing = 1;
             Mp -> columns = (nitems - 1) / MAX_IROWS + 1;
         }
     }
     else if (spacing == DEFAULT)
         Mp -> spacing =
                 (nitems <= (MAX_IROWS / 2)) ? 2 : 1;
     else if (columns == DEFAULT)
         if (Mp -> spacing == 1)
             Mp -> columns = (nitems - 1) / MAX_IROWS + 1;
         else
             Mp -> columns = (nitems - 1) / (MAX_IROWS / 2) + 1;
         
                                     /* Step 2: Adjust if out of range: */
             
     while (MAX_IROWS / Mp -> spacing * Mp -> columns < nitems)
         if (Mp -> spacing != 1)
             Mp -> spacing = 1;
         else
             Mp -> columns++;
     return;
 }
 
 
 /************************************************************
  * free_menus():
  *  Free up memory allocated for ALL menu items:
  ************************************************************/
 
 Void free_menus()
 {
     int i, j, k;
     MENU2 *m2p;
     
     for (i = 0; i < MAX_NEST; i++)
         for (j = 0; j < LMenus[i].max_menus; j++)
         {
             m2p = LMenus[i].Menus[j];
             for (k = 0; k < m2p -> most_items; k++)
                 free(m2p -> Items[k]);
             free(m2p);
         }
 }
 
 
 /************************************************************
  * fatal(): Complain and exit.
  ************************************************************/
 
 #if __STDC__                   /* use ANSI variable-#-of-args method   */
 
 int fatal (char *fmt, ...)
 {
     char ftext[80], ffmt[55];
     va_list arglist;
     
     va_start(arglist, fmt);
 
 #else                          /* or old varargs method:               */
 
 int fatal(fmt, va_alist)
 char *fmt;
 va_dcl
 {
     char ftext[80], ffmt[55];
     va_list arglist;
     
     va_start(arglist);
 #endif
 
     vsprintf(ffmt, fmt, arglist);
     sprintf(ftext, "Fatal error in rmenu: %s", ffmt);
 
     put_msg(1, ftext);
 
     va_end(arglist);
     return ERROR;
 }
 
 
 /************************************************************
  * put_msg(): Display a message on the menu screen
  *  Return the character typed to continue
  ************************************************************/
 
 #if __STDC__
 int put_msg (int bell, char *fmt, ...)
 {
     char ftext[80];
     va_list arglist;
     char c;
     
     va_start(arglist, fmt);
     
 #else
 int put_msg(bell, fmt, va_alist)
 int bell;
 char *fmt;
 va_dcl
 {
     char ftext[80];
     va_list arglist;
     char c;
     
     va_start(arglist);
 #endif
     
     move(ERR_ROW, 0);
     hlight_on();
 
     if (bell)
         beep();
 
 /*  vwprintw(stdscr, fmt, arglist); */
     vsprintf(ftext, fmt, arglist);
     addstr(ftext);
     va_end(arglist);
     refresh();
 
     c = getch();
 
     move (ERR_ROW, 0);
     hlight_end();
     clrtoeol();
     refresh();
     return c;
 }
