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