home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / listings / v_10_03 / 1003095a < prev    next >
Text File  |  1992-01-13  |  18KB  |  601 lines

  1.  
  2.  /************************************************************
  3.   *  Program: CMENU Menu Compiler
  4.   *  Module: cmenu2.c    
  5.   *      Menu Compiler:
  6.   *      Menu/Item Token Processing Functions
  7.   *  Written by: Leor Zolman, 7/91
  8.   ************************************************************/
  9.  
  10.  #include "cmenu.h"
  11.  #include "ccmenu.h"
  12.  
  13.  #include <ctype.h>
  14.  
  15.  
  16.  /************************************************************
  17.   * do_menu():
  18.   *  Process the MENU keyword
  19.   ************************************************************/
  20.  
  21.  int do_menu()
  22.  {
  23.      int tok;
  24.  
  25.      if (in_menu)               /* Are we currently processing a menu?  */
  26.      {                          /* yes.                                 */
  27.          warning("Endmenu missing from previous menu");
  28.          do_endmenu();          /* give them the benefit of the doubt   */
  29.      }
  30.  
  31.      if ((tok = gettok()) != T_STRING)
  32.      {
  33.          if (n_menus)
  34.              error("Menu name missing; menu unreachable");
  35.          sprintf(tparam, "menu%d", n_menus + 1);    /* force a name     */
  36.          ungettok(tok);
  37.      }
  38.      
  39.      if (strlen(tparam) > MAX_NAME)
  40.      {
  41.          error("The name '%s' is too long (%d chars max)",
  42.                  tparam, MAX_NAME);
  43.          tparam[MAX_NAME] = '\0';                   /* truncate name    */
  44.      }
  45.      
  46.      if ((MIp = find_menu(tparam)) == NULL)         /* menu exist?      */
  47.      {
  48.          MInfo[n_menus] = create_menu(tparam);      /* no.              */
  49.          if (fatal)
  50.              return ERROR;                          /* creation error   */
  51.          else
  52.              MIp = &MInfo[n_menus++];               /* OK, bump count   */
  53.      }
  54.      else
  55.          if (MIp -> Processed)                      /* redefinition?    */
  56.              return fatalerr("Duplicate Menu definition");    /* yes.   */
  57.          
  58.      Mp = &MIp -> Menu;
  59.      *Mp->title = *Mp->path = '\0';
  60.      Mp->nitems = Mp->widest = 0;
  61.      Mp->spacing = Mp->columns = Mp->escape = DEFAULT;
  62.      Mp->align = DEFAULT;
  63.  
  64.      in_item = FALSE;               /* state variables describing the   */
  65.      in_menu = TRUE;                /* current menu being processed     */
  66.      n_items = 0;                
  67.      n_refs = 0;                    /* no forward item references yet   */
  68.  
  69.      if ((tok = gettok()) != T_COLON)               /* optional colon   */
  70.          ungettok(tok);
  71.      
  72.      return OK;
  73.  }
  74.  
  75.  
  76.  /************************************************************
  77.   * do_title():
  78.   *  Process the TITLE clause for a menu.
  79.   ************************************************************/
  80.  
  81.  int do_title()
  82.  {
  83.      int tok;
  84.  
  85.      if ((tok = gettok()) != T_STRING)
  86.      {
  87.          error("Title text missing");
  88.          ungettok(tok);
  89.      }
  90.          
  91.      if (!in_item)                          /* Before all items?        */
  92.      {                                      /* yes.                     */
  93.          if (*Mp->title)
  94.              return error("A Menu Title has already been specified");
  95.          strcpy(Mp->title, tparam);
  96.      }
  97.      else
  98.          return error("The Menu Title must precede all Menu Items.");
  99.  
  100.      return OK;
  101.  }
  102.  
  103.  
  104.  /************************************************************
  105.   * do_path():
  106.   *  Process the PATH option.
  107.   *  Note that the PATH option may apply to an entire
  108.   *  menu or just to a single menu item (determined
  109.   *  by context.)
  110.   ************************************************************/
  111.  
  112.  int do_path()
  113.  {
  114.      int tok;
  115.      char *cp;
  116.      
  117.      if ((tok = gettok()) != T_STRING)
  118.      {
  119.          error("Path text missing");
  120.          ungettok(tok);
  121.      }
  122.          
  123.      if (tparam[strlen(tparam)-1]=='/' || tparam[strlen(tparam)-1]=='\\')
  124.          tparam[strlen(tparam) - 1] = '\0';    /* delete traling slash  */
  125.  
  126.      if (!in_item)                          /* Referring to the menu?   */
  127.      {                                      /* yes.                     */
  128.          if (*Mp->path)
  129.              return error("A Menu Path has already been specified");
  130.          strcpy(Mp->path, tparam);
  131.      }
  132.      else
  133.      {                                      /* Must be for the item.    */
  134.          if (*Ip->path)
  135.              return error("An Item Path has already been specified");
  136.          strcpy(Ip->path, tparam);
  137.      }
  138.      return OK;
  139.  }
  140.  
  141.  
  142.  /************************************************************
  143.   * do_align():
  144.   *  Process text alignment option.
  145.   *  Note: this option is a no-op. I decided there wasn't
  146.   *  any real need for any other than left-justified item
  147.   *  alignment. But, in case anyone thinks of a use for it,
  148.   *  I'm leaving in the ability to process the option.
  149.   ************************************************************/
  150.  
  151.  int do_align()
  152.  {
  153.      int tok;
  154.      
  155.      if (in_item)
  156.          return error("The Align clause must precede all Menu Items.");
  157.  
  158.      if (Mp->align)
  159.          return error("Align option already specified for this menu");
  160.      
  161.      switch (tok = gettok())
  162.      {
  163.          case T_LEFT:
  164.              Mp->align = 'L'; 
  165.              break;
  166.              
  167.          case T_RIGHT:
  168.              Mp->align = 'R';
  169.              break;
  170.              
  171.          case T_CENTER:
  172.              Mp->align = 'C';
  173.              break;
  174.              
  175.          default:
  176.              ungettok(tok);
  177.              return error("Align missing valid modifier");
  178.      }
  179.      return OK;
  180.  }
  181.  
  182.  
  183.  /************************************************************
  184.   * do_spacing():
  185.   *  Process the SPACING option (applies
  186.   *  to menus only.)
  187.   ************************************************************/
  188.  
  189.  int do_spacing()
  190.  {
  191.      int tok;
  192.  
  193.      if ((tok = gettok()) != T_VALUE)
  194.      {
  195.          error("Spacing value missing");
  196.          ungettok(tok);
  197.      }
  198.  
  199.      if (in_item)
  200.          return error("Spacing option must precede all menu items");
  201.  
  202.      if (Mp->spacing)
  203.          return error("Spacing option already specified");
  204.  
  205.                          /* only single and double spacing supported    */
  206.      if (vparam != 1 && vparam != 2)
  207.          return error("Spacing value must be either 1 or 2");
  208.  
  209.      Mp->spacing = vparam;
  210.      return OK;
  211.  }
  212.  
  213.  
  214.  /************************************************************
  215.   * do_columns():
  216.   *  Process the COLUMNS option
  217.   ************************************************************/
  218.  
  219.  int do_columns()
  220.  {
  221.      int tok;
  222.      
  223.      if ((tok = gettok()) != T_VALUE)
  224.      {
  225.          error("Columns value missing");
  226.          ungettok(tok);
  227.      }
  228.  
  229.      if (in_item)
  230.          return error("Columns option must precede all menu items");
  231.  
  232.      if (Mp->columns)
  233.          return error("Columns option already specified");
  234.  
  235.      if (vparam < 1 || vparam > 6)        /*  6 seems a reasonable max. */
  236.          return error("Columns value must be between 1 and 6");
  237.      Mp->columns = vparam;
  238.      return OK;
  239.  }
  240.  
  241.  
  242.  /************************************************************
  243.   * do_escape():
  244.   *  Process "escape" and "noescape" menu options
  245.   ************************************************************/
  246.  
  247.  int do_escape()
  248.  {
  249.      if (in_item)
  250.          return error("\"%s\" must appear before all menu items",
  251.              keywords[token].keyword);
  252.      
  253.      if (Mp->escape)
  254.          return error("Escape option already specified");
  255.      Mp->escape = (token == T_ESCAPE) ? YES : NO;
  256.  
  257.      return OK;
  258.  }
  259.  
  260.  
  261.  /************************************************************
  262.   * do_endmenu():
  263.   *  Process ENDMENU keyword
  264.   ************************************************************/
  265.  
  266.  int do_endmenu()
  267.  {
  268.      int i;
  269.      
  270.      if (!n_items)
  271.          error("No menu items specified for this menu");
  272.      
  273.      for (i = 0; i < n_refs; i++)           /* check for unresolved     */
  274.      {                                      /* forward Item references  */
  275.          if (*fwd_refs[i].refp == UNDEF_FWD)
  276.          {
  277.              int save_lineno = lineno;
  278.              lineno = fwd_refs[i].lineno;
  279.              error("Unresolved reference to Item \"%s\"",
  280.                  fwd_refs[i].iname);
  281.              lineno = save_lineno;
  282.          }
  283.      }
  284.      
  285.      in_menu = in_item = FALSE;             /* done with current menu   */
  286.      MIp -> Processed = TRUE;               /* it is now processed      */
  287.      Mp -> nitems = n_items;
  288.      return OK;
  289.  }
  290.  
  291.  
  292.  /************************************************************
  293.   * do_item():
  294.   *  Process the ITEM clause. Create a new ite
  295.   *  and fill in any existing forward references to it.
  296.   ************************************************************/
  297.  
  298.  int do_item()
  299.  {
  300.      int tok, i;
  301.      char *cp, c;
  302.      
  303.      if (n_items)
  304.          itemcheck();           /* check for previous item's completion */
  305.  
  306.      if ((tok = gettok()) != T_STRING)          /* label specified?     */
  307.      {                                          /* If not, stuff unique */
  308.          sprintf(tparam,"dummy!%d", n_items);         /* dummy name in  */
  309.              ungettok(tok);
  310.      }
  311.      else
  312.      {
  313.          if (strlen(tparam) > MAX_NAME)
  314.          {
  315.              error("Item name \"%s\" too long. Max %d chars.",
  316.                          tparam, MAX_NAME);
  317.              tparam[MAX_NAME] = '\0';
  318.          }
  319.          else for (cp = tparam; c = *cp; cp++)
  320.              if (!(isalpha(c) || isdigit(c) || c == '_'))
  321.              {
  322.                  error("Invalid char in identifier name: \"%s\"",
  323.                              tparam);
  324.                  *cp = '\0';
  325.                  break;
  326.              }
  327.      }
  328.      
  329.      if ((IIp = find_item(tparam)) != NULL)         /* Item name found? */
  330.          return error("Item name previously used.");
  331.  
  332.      if ((MIp->Items[n_items] = IIp = create_item(tparam))
  333.                  == NULL)
  334.          return ERROR;
  335.  
  336.      in_item = TRUE;
  337.      Ip = &IIp->Item;
  338.  
  339.      for (i = 0; i < n_refs; i++)               /* check for fwd refs   */
  340.          if (!strcmp(fwd_refs[i].iname, tparam))
  341.              *fwd_refs[i].refp = n_items;        /* fill in with item # */
  342.  
  343.      n_items++;                                 /* bump item count      */
  344.  
  345.      if ((token = gettok()) != T_COLON)         /* optional colon?      */
  346.      {
  347.          ungettok(token);                       /* if not, all done     */
  348.          return OK;
  349.      }
  350.      
  351.      if ((token = gettok()) == T_STRING)        /* short-form text?     */
  352.          return do_text2();                     /* if so, go process    */
  353.      else
  354.      {
  355.          ungettok(token);                       /* else all done        */
  356.          return OK;
  357.      }
  358.  }
  359.  
  360.  
  361.  /************************************************************
  362.   * do_opts():
  363.   *  Process simple "binary" options for prompt,
  364.   *  pre- and post-clear specifications.
  365.   *  Note: upon entry, global "token" contains the 
  366.   *  value of the token to be processed.
  367.   ************************************************************/
  368.  
  369.  int do_opts()
  370.  {
  371.      if (!in_item)
  372.          return error("\"%s\" only valid within an item",
  373.              keywords[token].keyword);
  374.      
  375.      switch(token)
  376.      {
  377.          case T_PROMPT: case T_PAUSE:
  378.          case T_NOPROMPT: case T_NOPAUSE:
  379.              if (Ip->prompt != DEFAULT)
  380.                  return error("Prompt option already specified");
  381.              Ip->prompt= (token==T_PROMPT || token==T_PAUSE)? YES :NO;
  382.              break;
  383.              
  384.          case T_POSTCLEAR:              /* these are actually no-ops,   */
  385.          case T_NOPOSTCLEAR:            /* but again, I've left them in */
  386.              if (Ip->post_clear != DEFAULT)
  387.                  return error("Postclear option already specified");
  388.              Ip->post_clear = (token == T_POSTCLEAR) ? YES : NO;
  389.              break;
  390.  
  391.          case T_PRECLEAR:
  392.          case T_NOPRECLEAR:
  393.              if (Ip->pre_clear != DEFAULT)
  394.                  return error("Preclear option already specified");
  395.              Ip->pre_clear = (token == T_PRECLEAR) ? YES : NO;
  396.              break;
  397.      }
  398.      return OK;
  399.  }
  400.  
  401.  
  402.  /************************************************************
  403.   * do_nextitem():
  404.   *  Process NEXTIEM option.
  405.   ************************************************************/
  406.  
  407.  int do_nextitem()
  408.  {
  409.      int tok;
  410.      
  411.      if (Ip->nextcode != DEFAULT)
  412.          error("Nextitem option already specified");
  413.      
  414.      switch (tok = gettok())
  415.      {
  416.          case T_FIRST:
  417.              Ip->nextcode = NXT_FIRST;
  418.              break;
  419.              
  420.          case T_LAST:
  421.              Ip->nextcode = NXT_LAST;
  422.              break;
  423.              
  424.          case T_NEXT:
  425.              Ip->nextcode = NXT_NEXT;
  426.              break;
  427.              
  428.          case T_STRING:
  429.              Ip->nextcode = NXT_DIRECT;
  430.              if (find_item(tparam))
  431.                  Ip->nextitem = item_num;
  432.              else
  433.              {                         /* record forward item reference */
  434.                  strcpy(fwd_refs[n_refs].iname, tparam);
  435.                  fwd_refs[n_refs].refp = &Ip->nextitem;
  436.                  fwd_refs[n_refs++].lineno = lineno;
  437.                  Ip->nextitem = UNDEF_FWD;
  438.              }
  439.              break;
  440.          
  441.          default:
  442.              ungettok(tok);
  443.              return error("Bad Nextitem specification");
  444.      }
  445.      return OK;
  446.  }
  447.  
  448.  
  449.  /************************************************************
  450.   * do_text():
  451.   *  Process Text parameter
  452.   ************************************************************/
  453.  
  454.  int do_text()
  455.  {
  456.      int tok;
  457.      
  458.      if (!in_item)
  459.          return error("Text clause must be within an item");
  460.      if (*Ip->text)
  461.          return error("Text clause already specified for this item");
  462.      
  463.      if ((tok = gettok()) != T_STRING)
  464.      {
  465.          ungettok(tok);
  466.          return error("Text clause specified without the text.");
  467.      }
  468.      
  469.      return do_text2();
  470.  }
  471.  
  472.  
  473.  /************************************************************
  474.   * do_text():
  475.   *  Continued TEXT clause processing, shared between
  476.   *  do_text() and do_item().
  477.   ************************************************************/
  478.  
  479.  int do_text2()
  480.  {
  481.      if (strlen(tparam) > MAX_TXTWID)
  482.      {
  483.          *Ip->text = 'x';              /* to avoid "missing text" error */
  484.          return error("Text is too long; maximum %d chars",
  485.              MAX_TXTWID);
  486.      }
  487.      else
  488.          strcpy(Ip->text, tparam);
  489.  
  490.      if (strlen(tparam) > Mp -> widest)
  491.          Mp -> widest = strlen(tparam);
  492.  
  493.      return OK;
  494.  }
  495.  
  496.  
  497.  /************************************************************
  498.   * do_action():
  499.   *  Process standard action, Exit, Lmenu or Emenu clause
  500.   ************************************************************/
  501.  
  502.  int do_action()
  503.  {
  504.      int tok;
  505.      int old_acttyp = Ip->acttyp;
  506.      
  507.      if (!in_item)
  508.          return error("%s clause only valid within an item",
  509.              keywords[token].keyword);
  510.      
  511.      if (token == T_EXIT || (tok = gettok()) == T_EXIT)
  512.          Ip->acttyp = ACT_EXIT;
  513.      else
  514.          if (tok != T_STRING)
  515.          {
  516.              ungettok(tok);
  517.              error("Incomplete %s specification",
  518.              keywords[token].keyword);
  519.          }
  520.      else 
  521.          if (strlen(tparam) > MAX_CMD)
  522.              error("%s parameter too long (max %d chars)",
  523.                  keywords[token].keyword, MAX_CMD);
  524.      else
  525.          switch(token)
  526.          {
  527.            case T_ACTION:
  528.              strcpy(Ip->action, tparam);
  529.              Ip->acttyp = ACT_CMND;
  530.              break;
  531.              
  532.            case T_LMENU:
  533.              if (find_menu(tparam) != NULL)     /* named menu defined?  */
  534.                  Ip->lmenunum = menu_num;       /* yes.                 */
  535.              else
  536.              {                                  /* no. create entry     */
  537.                  MInfo[n_menus] = create_menu(tparam);
  538.                  if (fatal)
  539.                      return ERROR;              /* creation error       */
  540.                  else
  541.                      Ip->lmenunum = n_menus++;      /* OK; assign.      */
  542.              }
  543.              
  544.              Ip->acttyp = ACT_LMENU;
  545.              break;
  546.              
  547.            case T_EMENU:
  548.              strcpy(Ip->action, tparam);
  549.              Ip->acttyp = ACT_EMENU;
  550.              break;
  551.          }
  552.  
  553.      if (old_acttyp)
  554.          return error("Only one Action clause allowed per item");
  555.      
  556.      return OK;
  557.  }
  558.  
  559.  
  560.  /************************************************************
  561.   * do_help():
  562.   *  Process help clause.
  563.   ************************************************************/
  564.  
  565.  int do_help()
  566.  {
  567.      int tok;
  568.      
  569.      if (!in_item)
  570.          return error("Help clause only valid within an item");
  571.  
  572.      if ((tok = gettok()) != T_STRING)
  573.      {
  574.          ungettok(tok);
  575.          return error("No Help text specified");
  576.      }
  577.      
  578.      if (strlen(tparam) > MAX_HELP)
  579.          return error("Help text too long (max %d chars)",
  580.              MAX_HELP);
  581.          
  582.      if (*Ip->help)
  583.          return error("Only one help line allowed per item");
  584.      
  585.      strcpy(Ip->help, tparam);
  586.      return OK;
  587.  }
  588.  
  589.  
  590.  /************************************************************
  591.   * do_err():
  592.   *  Diagnose hopelessly bad syntax (i.e., encountering a
  593.   *  totally unexpected keyword)
  594.   ************************************************************/
  595.  
  596.  int do_err()
  597.  {
  598.      return fatalerr("Unrecoverable Syntax error.");
  599.  }
  600.  
  601.