Listing 2

  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("Colunms 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_CHD)
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_L_MENU:
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:
/* End of File */