home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / listings / v_10_03 / cmenu.exe / CMENU2.C < prev    next >
Text File  |  1992-01-06  |  14KB  |  608 lines

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