1 /********************************************************************\
2 **                             _________________________________    **
3 **   A n t h o n y            |________    __    __    _________|   **
4 **                                     |  |o_|  |o_|  |             **
5 **           T h y s s e n           __|   __    __   |__           **
6 **                                __|   __|  |  |  |__   |__        **
7 **  `` Dragon Computing! ''    __|   __|     |  |     |__   |__     **
8 **                            |_____|        |__|        |_____|    **
9 **                                                                  **
10 \********************************************************************/
11 /*
12 ** user-menu.c
13 **
14 **   This module reads and handles a user configuration (RC) file defining
15 ** a number of user menus.  The module is application indepentant only
16 ** requiring application specific declarations at the top of the file.
17 ** The following features are provided by this module.
18 **
19 **  * The "menu" keyword in the config file (see example below) is used
20 **    define the menu to which new menu items are to be added. Only the
21 **    menus named in the ``decl_menus'' structure below can be given by the
22 **    user. If the menu widget variable pointed to in this structure is
23 **    currently set to ``None'', then the module assumes it needs to create
24 **    the menu and does so, using the same menu name the user uses in the
25 **    config file.
26 **
27 **    The order in which menus are created in the config file is not
28 **    important, and it is even posible for the user to switch back and
29 **    forth between menus as desired. If the program did not pre-create the
30 **    menu and the user decided not to use that menu, that menu will not be
31 **    created.
32 **
33 **    If the programmer pre-created the menu widget pointed to, with some
34 **    items in it also pre-added, the user specified items will be appended
35 **    to these items.
36 **
37 **    It is up to the other parts of the application to popup and popdown
38 **    the created menus. The actual menu items added by the user however
39 **    will call an internal routine which calles the function sequence the
40 **    user wanted for this menu item. (see below)
41 **
42 **  * Each item read from the config file call a number of functions whose
43 **    names are declared in the ``decl_functs'' structure below.  These
44 **    functions are to be defined in another module of the application. Each
45 **    function must be of type `void', and can have upto TWO string
46 **    arguments.  These arguments are given by the user in the menu
47 **    configuraion file and certian string substitutions are pre-performed
48 **    before the function is called (see below).
49 **
50 **    If the function fails for some reason and wants to abort the current
51 **    it can abort the current function sequence on this menu item, it can
52 **    do so by calling menu_item_abort() before it returns.  This abort
53 **    function will clear the current function sequence and free the menu
54 **    handler to accept new menu selections by the user.
55 **
56 **    A function that has the input flag set to TRUE is expected to popup a
57 **    dialog widget of some type before returning. The callbacks for this
58 **    input widget, after setting appropiate substition variables, is
59 **    expected to call either  menu_item_abort()  or  menu_item_continue()
60 **    as appropiate, just before it returns back to the mail application
61 **    processing loop.  This method will allow the application to ask the
62 **    user for some input or some confirmation before continuing the menu
63 **    function sequence, as set by the user in configuration file.
64 **
65 **  * The arguments are strings provided by the user in the config file.
66 **    Each is surrounded by either double quotes `"' or single quotes `''
67 **    and can contain any number of string substitution ( % ) escapes in a
68 **    simular fashion to the C library function printf().  These substition
69 **    escape characters are replaced by string variables, declared in the
70 **    ``decl_subchars'' structure below, before calling the appropiate
71 **    function. The substitution charcater will escape itself.
72 **
73 **  * The module also provides a `line' keyword as an menu item seperator
74 **    within the user menu config file.
75 **
76 **  * External Functions provided are :-
77 **
78 **    read_user_menu( parent_widget, file_ptr, file_name)
79 **       Parse the open file, createing the user menus as declared in the
80 **       top section of this module.
81 **
82 **    menu_item_abort()
83 **       Notify the item handler not to continue the current menu_item
84 **       when the current routine returns.
85 **
86 **    menu_item_continue()
87 **       Continue processing user functions after the user has returned
88 **       from a input dialog user function. The only action the caller
89 **       should do after calling this function is to return back to the
90 **       main application loop. Any other action performed may have
91 **       undefined results.
92 **
93 ** * FUTURE:-
94 **      * Figure out how to add `title' elements which do not highlight
95 **      * Addition of user defined Sub-Menus (what a lot of work).
96 **
97 ** # Example application user menu configuration `RC' file.
98 ** # This may not be valid for the current application.
99 ** #
100 ** # Any line starting with a `#' is a comment line and is ignored
101 ** # Any lines ending with a `\' as the very last character of a
102 ** # line is continued onto the next line.
103 ** #
104 ** menu "usermenu" "User Menu"
105 ** #   Create the menu `usermenu' if required. This name must match one of
106 ** # declared menu names given below. Any other menu items following will
107 ** # be added to this menu. The second string is optional but if present
108 ** # will be added as a title for the menu.
109 ** #
110 ** line
111 ** #  line adds extra line across the menus
112 ** #
113 ** item "Quit"		quit()
114 ** #   Add a user menu item with the given functions to be called if
115 ** # this menu item is selected.
116 ** #
117 ** item "Save"		save('%f')
118 ** # function arguments are defined by the user but can have substitions
119 ** # In this example  %f  is the current filename within the application
120 ** #
121 ** item "Save As"       input("Save As", '%f') save('%i')
122 ** # Multiple functions can be called, and are executed in the order given.
123 ** # Note that the arguments can use either double or single quotes.
124 ** #
125 ** # In this example input() is a external function which sets the %i to
126 ** # some user input (somehow), while this is happing the current function
127 ** # sequence is put on hold the input widget either continues or aborts
128 ** # the sequence.
129 ** #
130 ** item "Load"   input("File to load", '') \
131 **               load('%i')
132 ** #  a line can be continued on the next line by backslashing the EOL.
133 ** #
134 */
135 /* ====================================================================== */
136 /* =================  Application Specific Definitions  ================= */
137 /* ====================================================================== */
138 
139 #include "xbmbrowser.h"
140 #include <ctype.h>
141 
142 #define QUIT_APP    quit_browser   /* The applications quit routine */
143 #define SET_WAIT    set_busywait   /* routine to set a wait cursor */
144 #define CLEAR_WAIT  clear_busywait /* routine to clear a wait cursor */
145 
146 #define MAX_STRING  1024           /* max length of a string argument */
147 #define SUBSTITION  '%'            /* the substitution character */
148 #define MAX_ARGS    2              /* this hardcoded in usr_item_handler() */
149 
150 /* Declare the menus the users are allowed to define (though may not) */
151 /* WARNING: the first menu must be the main menu which must be defined */
152 static struct {
153   char       *name;     /* name of this menu the user can define */
154   Widget     *menu;     /* ptr to variable holding this user-menu */
155 } decl_menus[] = {
156    { "main",      &menu_main      },  /* main menu -- menu button at top */
157    { "global",    &menu_global    },  /* global menu -- button 2 */
158    { "bitmap",    &menu_bitmap    },  /* bitmap menu -- but 3 on xbm files */
159    { "pixmap",    &menu_pixmap    },  /* pixmap menu */
160    { "directory", &menu_directory },  /* directory menu */
161    { "other",     &menu_other     },  /* other files menu */
162    { NULL, NULL }
163 };
164 
165 /* Declare functions the user menu items can call */
166 /* WARNING: the first function must be the quit function */
167 static struct {
168   char       *name;        /* the name of function in the RC file */
169   void      (*funct)();    /* the function Pointer */
170   Cardinal    numargs;     /* number of arguments this function needs */
171   Boolean     input;       /* this function performs a input popup sequence */
172 } decl_functs[] = {
173    { "quit",     quit_browser,    0, False },   /* cleanup and exit program */
174    { "scan",     scan_images,     0, False },   /* full directory scan */
175    { "rescan",   rescan_images,   0, False },   /* fast dir rescan */
176    { "chdir",    change_dir,      1, False },   /* change directory */
177    { "exec",     exec_string,     1, False },   /* external command */
178    { "confirm",  user_confirm,    1, True  },   /* user confirm dialog */
179    { "input",    input_string,    2, True  },   /* user input dialog */
180    { "selected", file_selected,   0, False },   /* abort on no selection */
181    { NULL, NULL, 0 }
182 };
183 
184 
185 /* Declare the % substitutions in function arguments
186 ** (NOTE %% is predefined) */
187 static struct {
188   char    subchar;     /* % escaped substition character for function args */
189   char   *string;      /* char ptr to substitution string */
190 } decl_subchars[] = {
191    { 'h',  home_dir  },  /* %h = the users home directory */
192    { 'd',  dir_name  },  /* %d = the current directory */
193    { 'f',  file_name },  /* %f = the file selected in this directory */
194    { 'b',  base_name },  /* %b = basename of the selected file */
195    { 's',  suffix    },  /* %s = suffix of the selected file (EG: ".xbm" ) */
196    { 'i',  input     },  /* %i = User input from input() function */
197    { 'D',  init_dir  },  /* %D = initial startup directory */
198    { '\0', NULL }
199 };
200 /* Full path of selected file  = "%d%f". Also  "%f" = "%b%s" */
201 
202 
203 /* ====================================================================== */
204 /* =============  End of Application Specific Definitions  ============== */
205 /* ====================================================================== */
206 
207 /* --------------- Menu Handler and Argument Substition ----------------- */
208 
209 /* define the functions and argument structure each menu item is to call */
210 typedef struct _Functs {
211   void           (*funct)();      /* the function to be called */
212   char            *arg[MAX_ARGS]; /* the argument strings the user gave */
213   Boolean          input;         /* function popups up a input dialog */
214   struct _Functs  *next;          /* next function and args in sequence */
215 } Functs;
216 
217 /* --------------------------- */
218 
219 /* current function list in progress */
220 static Functs  *next_function = NULL;    /* next function to be called */
221 static char    *item_in_progress = NULL; /* the menu item selected */
222 
223 void
menu_item_abort()224 menu_item_abort()
225 /* Abort the current user-item-handler calling sequence */
226 {
227   next_function = NULL;   /* no more functions need to be called */
228 }
229 
230 
231 void
menu_item_continue()232 menu_item_continue()
233 /* perferm the string substitions for function arguments and call the
234 ** functions asked for by the user in sequence. This routine is also
235 ** called by the return callbacks of any user input dialogs that
236 ** one of these functions may have popped up.
237 */
238 {
239   char         arg[MAX_ARGS][MAX_STRING]; /* arguments to pass to user functs */
240   char        *a, *d, *s;        /* string pointers arg, dest, sub-string */
241   int          i, j, left, len;  /* temp variables */
242 
243 
244   /* while functions are present in the current function sequence */
245   SET_WAIT();
246   while( next_function != NULL ) {
247 
248     /* prepare each argument */
249     for( i = 0; i < MAX_ARGS; i++ ) {
250       left = MAX_STRING;          /* count of length of destinition string */
251       a = next_function->arg[i];  /* user supplied argument pointer */
252       d = arg[i];                 /* set the destination pointer */
253 
254       if( a == NULL ) {  /* no argument given */
255         *d = '\0';           /* set empty string */
256         continue;            /* goto next argument */
257       }
258 
259       while( *a != '\0' ) {
260 
261         /* while next char is the substitution char -- substitute */
262         while( *a == SUBSTITION ) {
263           a++;  /* skip substitution char */
264           /* check is it escapes another substition char */
265           if ( *a == SUBSTITION ) {
266             *d++ = *a++; continue;
267           }
268           /* find the external variable to substute */
269           for( j = 0; decl_subchars[j].subchar; j++ )
270             if( *a == decl_subchars[j].subchar )
271               break;
272           if( decl_subchars[j].subchar == '\0' ) {
273             fprintf(stderr,
274                 "Substitution %c%c in menu item \"%s\" is invalid\n",
275                      SUBSTITION,  *a,  item_in_progress );
276             /* just abort current function sequence */
277             menu_item_abort();
278             goto menu_return;
279           }
280           a++;  /* skip subitution type character */
281           len = strlen( decl_subchars[j].string );
282           if( (left -= len) <= 0 )       /* test if it will fit */
283             goto argument_overflow;
284           strcpy(d, decl_subchars[j].string);
285           d += len;   /* increment string pointers */
286         }
287 
288         /* copy up to the next substition char */
289         s = index( a, SUBSTITION );      /* scan for next substition char */
290         len =  (s == NULL) ? strlen(a)   /* not found -- just copy rest */
291                            : s - a;      /* copy upto the substition char */
292         if( (left -= len) <= 0 )         /* test if it will fit */
293           goto argument_overflow;
294         strncpy(d, a, len);              /* copy from the user argument */
295         d += len;                        /* increment string pointers */
296         a += len;
297       }
298       *d = '\0';  /* finish of the string */
299     } /* for each argument */
300 
301     /* FUNCTION CALL */
302     /* Make the actual function call requested by the user */
303     /* HARDCODED -- MAX_ARGS is limited by this bit of code */
304     (next_function->funct)(arg[0], arg[1]);
305 
306     /* if the user called  menu_item_abort()  abort sequence now */
307     if( next_function == NULL )
308       goto menu_return;
309 
310     /* do we have to wait for a popup before continuing? */
311     if( next_function->input ) {
312       /* Presumably the function just called popped up a input dialog of
313       ** some kind, and was successful in doing so.  OK, increment the
314       ** next_function pointer and return to the main application loop,
315       ** to wait for the dialog to re-call this routine, menu_item_continue(),
316       ** when ready to continue the current function sequence.
317       */
318       next_function = next_function->next;
319       goto menu_return;
320     }
321 
322     /* increment to the next function (if any) */
323     next_function = next_function->next;
324 
325   } /* while next function is not an empty list */
326 
327   /* finished */
328   goto menu_return;
329 
330 argument_overflow:  /* the argument after substitutions is too big! */
331   fprintf(stderr, "Argument for user menu function overflows argument\n");
332   fprintf(stderr, "buffer after all substutions are made. -- ABORTING\n" );
333   /* just abort this function sequence */
334   menu_item_abort();
335 
336 menu_return:
337   CLEAR_WAIT();
338   return;
339 }
340 
341 
342 static void
menu_item_handler(widget,client_data,call_data)343 menu_item_handler(widget, client_data, call_data)
344 /* Callback for the menu items. This will prepare for the current
345 ** function sequence required by this menu item before calling the
346 ** menu_item_continue() routine above, which actually performs the
347 ** function sequence in order.
348 ** NOTE: the function sequence to be called is expected in the call_data.
349 */
350   Widget  widget;
351   XtPointer client_data, call_data;
352 {
353   char *item_name;
354 
355   XtVaGetValues(widget, XtNlabel, &item_name, NULL);  /* get widgets name */
356 
357   if( next_function != NULL ) {
358     fprintf(stderr, "User menu selection \"%s\" ignored.\n", item_name );
359     fprintf(stderr,
360            "\"%s\" is currently in progress, waiting for user input.\n",
361            item_in_progress );
362     return;
363   }
364   item_in_progress = item_name;
365   next_function = (Functs *)client_data;
366 
367   /* start calling functions */
368   menu_item_continue();
369 }
370 
371 
372 /* ---------------------- Parsing and Setup Code ------------------------- */
373 
374 #define IS_SPACE(x)        ( (x)==' ' || (x)=='\t' || (x)=='\r' )
375 #define IS_ALPHA(x)        ( 'a'<=(x)&&(x)<='z' || 'A'<=(x)&&(x)<='Z' )
376 #define IS_DIGIT(x)        ( '0'<=(x)&&(x)<='9' )
377 #define IS_ALPHANUMERIC(x) ( IS_ALPHA(x) || IS_DIGIT(x) )
378 
379 typedef enum {
380    KEYWORD, STRING, CHAR, EOL
381 } Token;
382 
383 static FILE   *file_ptr;                    /* given filename being read */
384 static char   *configname;                  /* given filename being read */
385 static char    token_string[MAX_STRING+1];  /* the actual token read */
386 static int     line = 1;                    /* the current line number */
387 static Widget  cur_menu = NULL;             /* the current menu for items */
388 /* static Boolean  firstitem = FALSE;       /* item is the first in menu */
389 
390 #ifdef PARSE
391 #define  PARSE_OUTPUT(a)  printf a
392 #else
393 #define  PARSE_OUTPUT(a)  /**/
394 #endif
395 
396 
397 /* ----------------------------- */
398 
399 static char
get()400 get()
401 /* Read a character from the RC file and update the current
402 ** line number as appropiate. NOTE EOF is treated as an
403 ** infinite End-Of-Line. The main loop tests EOF specifically.
404 */
405 { int c;
406 
407   c = fgetc(file_ptr);
408   if( c == EOF || c == '\n' ) {
409     c = '\n';   line++;
410   }
411   return c;
412 }
413 
414 
415 static void
unget(c)416 unget( c )
417 /* push the character back to be retrived again latter
418 ** Update the current line count as appropiately
419 */
420   char c;
421 {
422   if( c == '\n' ) line--;
423   ungetc( c, file_ptr );
424 }
425 
426 
427 /* Marco inline function for use within the get_token() function below */
428 #define read_token_string(test) \
429   { int  i = 0;                 \
430     while( c = get(), (test) && i < MAX_STRING ) \
431       token_string[i++] = c;    \
432     if( i >= MAX_STRING )       \
433       goto token_overflow;      \
434     token_string[i] = '\0';      \
435   }
436 
437 static Token
get_token()438 get_token()
439 /* Read the given file stream and return the first valid token found.
440 ** NOTE: This proceedure will just return EOL when the end of file is
441 ** reached
442 */
443 {
444   char  c;   /* character just read (not int) */
445 
446 retry_get_token:
447   while( c = get(), IS_SPACE(c) ) ;   /* skip leading space */
448 
449   /* test for specific characters */
450   switch( c ) {
451 
452     /* end-of-line and end-of-file conditions */
453     case '\n':
454       token_string[0] = '\n';
455       token_string[1] = '\0';
456       PARSE_OUTPUT(("EOL\n"));
457       return EOL;
458 
459     /* comment skipping */
460     case '#':    /* skip to next line */
461       while( c = get(), c != '\n' ) ;  /* loop until EOL */
462       unget( c );                      /* push the return back */
463       goto retry_get_token;
464 
465     /* do special escape of the EOL */
466     case '\\': /* get and test EOL */
467       if( (c = get()) == '\n' )
468         goto retry_get_token;
469       /* not a EOL! -- return this char */
470       unget( c ); /* unget bad char */
471       c = '\\';   /* restore backslash */
472       break;      /* exit to the CHAR return */
473 
474     /* single quoted string */
475     case '\'':
476       /* skip the quote */
477       read_token_string( c != '\'' );
478       /* skip the quote */
479       PARSE_OUTPUT(("STRING \"%s\"\n", token_string));
480       return STRING;
481 
482     /* double quoted string */
483     case '"':
484       /* skip the quote */
485       read_token_string( c != '"' );
486       /* skip the quote */
487       PARSE_OUTPUT(("STRING \"%s\"\n", token_string));
488       return STRING;
489   }
490 
491   /* test if a keyword is found */
492   if( IS_ALPHA(c) ) {
493     unget( c ); /* this is the first char of token */
494     read_token_string( IS_ALPHANUMERIC(c) );
495     unget( c );    /* return this char to input stream */
496     PARSE_OUTPUT(("KEYWORD \"%s\"\n", token_string));
497     return KEYWORD;
498   }
499 
500   /* At this point any unreconised character is just returned */
501   token_string[0] = c;
502   token_string[1] = '\0';
503   PARSE_OUTPUT(("CHAR '%s'\n", token_string));
504   return CHAR;
505 
506 token_overflow:  /* handle the event that the current token is too big */
507   token_string[40] = '\0';  /* limit the length of the long string */
508   fprintf(stderr, "Token found in \"%s\" on line %d is too long to handle!\n",
509                      configname, line );
510   fprintf(stderr, "Token starts with -->%s...\n", token_string );
511   QUIT_APP();
512 }
513 /* this marco is no longer needed */
514 #undef read_token_string
515 
516 
517 static void
parse_line()518 parse_line()
519 {
520   if( cur_menu == NULL ) {
521     fprintf(stderr, "Missing menu declaration for line, \"%s\" at line %d.\n",
522                         configname, line );
523     QUIT_APP();
524   }
525 
526   XtVaCreateManagedWidget(
527            "line", smeLineObjectClass, cur_menu,
528            XtNheight,     2,     /* allow multiple lines to be spaced */
529            NULL );
530 }
531 
532 
533 static void
parse_menu(parent)534 parse_menu(parent)
535 /* Parse a `menu' line and create the user menu pre-declared */
536   Widget parent;   /* the parent of the menu */
537 {
538   Token     token;
539   int       i;              /* junk looping variable */
540   Boolean   title = FALSE;  /* is a title present for this menu? */
541 
542   token = get_token();
543   if( token != STRING ) {
544     fprintf(stderr, "Menu name not found in \"%s\" at line %d.\n",
545                         configname, line );
546     QUIT_APP();
547   }
548 
549   for( i = 0; decl_menus[i].name; i++ )
550     if( strcmp( token_string, decl_menus[i].name ) == 0 )
551       break;
552 
553   if( decl_menus[i].name == NULL ) {
554     fprintf(stderr, "Unknown menu \"%s\", \"%s\" on line %d.\n",
555                      token_string, configname, line );
556     QUIT_APP();
557   }
558 
559   switch ( get_token() ) {
560   case STRING:  title = TRUE;
561                 break;
562   case EOL:     break;  /* this is valid too */
563   default:
564     fprintf(stderr,
565        "Menu \"%s\", \"%s\" is not a title or EOL, \"%s\" at line %d.\n",
566             decl_menus[i].name, token_string, configname, line );
567     QUIT_APP();
568   }
569 
570   /* only create the actual menu if it is not already created */
571   if( *(decl_menus[i].menu) == NULL )
572     *(decl_menus[i].menu) = XtVaCreatePopupShell(
573              decl_menus[i].name, simpleMenuWidgetClass, parent,
574              XtNlabel,  title ? token_string : NULL,
575              NULL );
576 
577   /* add new items to this menu */
578   cur_menu = *(decl_menus[i].menu);
579 
580   /* add an extra line after a title */
581   if( title ) parse_line();
582 }
583 
584 
585 static Functs *
parse_item_functs()586 parse_item_functs()
587 {
588   Token     token;
589   Functs   *functs = NULL;
590   Functs  **lastlink = &functs;
591   int       startline;
592   int       i,j;   /* junk looping variables */
593 
594   /* while end of line is not reached */
595   while( token = get_token(),  token != EOL ) {
596 
597     if( token != KEYWORD ) {
598       fprintf(stderr, "Unknown function \"%s\", \"%s\" at line %d.\n",
599                            token_string, configname, line );
600       QUIT_APP();
601     }
602     startline = line;  /* save a copy of the line this function starts on */
603 
604     for( i = 0; decl_functs[i].name; i++ )
605       if( strcmp( token_string, decl_functs[i].name ) == 0 )
606         break;
607 
608     if( decl_functs[i].name == NULL ) {
609       fprintf(stderr, "Unknown function \"%s\", \"%s\" on line %d.\n",
610                      token_string, configname, startline );
611       QUIT_APP();
612     }
613 
614     if( decl_functs[i].numargs > MAX_ARGS ) {
615       fprintf(stderr,
616            "Programming Error, function \"%s\" has too many arguments\n",
617                      token_string );
618       QUIT_APP();
619     }
620 
621 
622     /* Malloc, set defaults and link the Functs Structure */
623     *lastlink = (Functs *) XtMalloc( sizeof( Functs ) );
624     (*lastlink)->next   = NULL;
625     (*lastlink)->funct  = decl_functs[i].funct;
626     (*lastlink)->input  = decl_functs[i].input;
627 
628     /* read in the argument strings supplied by the user */
629     for( j = 0; j < MAX_ARGS; j++ )
630       (*lastlink)->arg[j] = NULL;
631 
632     token = get_token();
633     if( token != CHAR || token_string[0] != '(' ) {
634       fprintf(stderr, "Function \"%s\" missing '(', \"%s\" at line %d.\n",
635                           decl_functs[i].name, configname, startline );
636       QUIT_APP();
637     }
638 
639     /* gather the arguments */
640     for( j = 0; j < decl_functs[i].numargs; j++ ) {
641 
642       /* get intervening ',' */
643       if( j > 0 ) {
644         token = get_token();
645         if( token != CHAR || token_string[0] != ',' ) {
646           if( token == CHAR && token_string[0] == ')' )
647             fprintf(stderr,
648                "Function \"%s\" requires %d arguments, \"%s\" at line %d.\n",
649                 decl_functs[i].name,  decl_functs[i].numargs,
650                 configname,  startline );
651           else
652             fprintf(stderr,
653                "Missing ',' for function \"%s\", \"%s\" at line %d.\n",
654                     decl_functs[i].name, configname, startline );
655           QUIT_APP();
656         }
657       }
658 
659       /* get the argument string and save it in Functs call structure */
660       token = get_token();
661       if( token != STRING ) {
662         fprintf(stderr,
663                "Function \"%s\" requires %d arguments, \"%s\" at line %d.\n",
664                 decl_functs[i].name,  decl_functs[i].numargs,
665                 configname,  startline );
666         QUIT_APP();
667       }
668       (*lastlink)->arg[j] = XtMalloc( strlen( token_string ) + 1 );
669       strcpy( (*lastlink)->arg[j],  token_string );
670     } /* for each arg */
671 
672     token = get_token();
673     if( token != CHAR || token_string[0] != ')' ) {
674       if ( token == STRING || token == CHAR && token_string[0] != ',' )
675         fprintf(stderr,
676                "Function \"%s\" only needs %d arguments, \"%s\" at line %d.\n",
677                 decl_functs[i].name,  decl_functs[i].numargs,
678                 configname,  startline );
679       else
680         fprintf(stderr, "Function \"%s\" missing ')', \"%s\" at line %d.\n",
681                           decl_functs[i].name, configname, startline );
682       QUIT_APP();
683     }
684 
685     /* move the pointer to the lastlink of Functs list */
686     lastlink = &(*lastlink)->next;
687 
688   }  /* loop until EOL */
689 
690   return functs;
691 }
692 
693 
694 static void
parse_item()695 parse_item()
696 {
697   Token  token;
698   Widget item;
699 
700   if( cur_menu == NULL ) {
701     fprintf(stderr, "Missing menu declaration for item, \"%s\" at line %d.\n",
702                         configname, line );
703     QUIT_APP();
704   }
705 
706   token = get_token();
707   if( token != STRING ) {
708     fprintf(stderr, "Item name not found in \"%s\" at line %d.\n",
709                         configname, line );
710     QUIT_APP();
711   }
712 
713   /* create item for current menu */
714   item = XtVaCreateManagedWidget(
715            token_string, smeBSBObjectClass, cur_menu,
716            XtNlabel,    token_string,
717            XtNjustify,  XtJustifyLeft,
718            NULL );
719   XtAddCallback(item, XtNcallback, menu_item_handler, parse_item_functs());
720 
721 }
722 
723 
724 void
read_user_menus(parent,rc,name)725 read_user_menus(parent, rc, name)
726 /* Read the config file and setup all menus required
727 ** from the given file name and descriptor
728 */
729   Widget  parent;   /* parent widget for user menus */
730   FILE   *rc;       /* file to read config from */
731   char   *name;     /* config filename for errors */
732 {
733   Token  token;     /* the current token from config file */
734 
735   file_ptr   = rc;    /* save these for use by other paseing routines */
736   configname = name;
737 
738   while( !feof(rc) ) {   /* while not end of file and no error */
739     token = get_token();
740 
741     switch ( token ) {
742     case EOL:
743       continue;
744     case STRING:
745       fprintf(stderr, "Unexpected String \"%s\" in \"%s\" at line %d.\n",
746             token_string, configname, line );
747       QUIT_APP();
748     case CHAR:
749       fprintf(stderr, "Unexpected Character `%c' in \"%s\" at line %d.\n",
750            token_string[0], configname, line );
751       QUIT_APP();
752     }
753 
754     /* handle KEYWORD */
755 
756     if( strcmp( token_string, "menu" ) == 0 ) {
757       /* find and create this usermenu */
758       parse_menu(parent);
759       continue;
760     }
761     if( strcmp( token_string, "line" ) == 0 ) {
762       /* find and create this usermenu */
763       parse_line();
764       continue;
765     }
766     if( strcmp( token_string, "item" ) == 0 ) {
767       /* find and create this usermenu */
768       parse_item();
769       continue;
770     }
771 
772     fprintf(stderr, "Unknown config line `%s' in \"%s\" at line %d.\n",
773          token_string, configname, line );
774     QUIT_APP();
775   }
776 
777 
778   /* If no user menu "main" was created -- create a plain quit menu */
779   if ( *(decl_menus[0].menu) == NULL ) {
780     Widget    item;
781     Functs   *quit;
782     int       j;
783 
784     fprintf(stderr, "WARNING: menu \"main\" not created in \"%s\"\n",
785                        configname );
786     fprintf(stderr, "         creating default quit menu\n");
787 
788     /* create default menu for "main" with a title */
789     *(decl_menus[0].menu) = XtVaCreatePopupShell(
790              decl_menus[0].name, simpleMenuWidgetClass, parent,
791              XtNlabel,  "No Main Menu Found",
792              NULL );
793     cur_menu = *(decl_menus[0].menu);
794     parse_line();
795 
796     /* create a quit menu item */
797     item = XtVaCreateManagedWidget(
798            token_string, smeBSBObjectClass, cur_menu,
799            XtNlabel,    "Quit Application..",
800            XtNjustify,  XtJustifyLeft,
801            NULL );
802 
803     /* which calls the quit function (and on others) */
804     quit = (Functs *) XtMalloc( sizeof( Functs ) );
805     quit->next   = NULL;
806     quit->funct  = decl_functs[0].funct;
807     quit->input  = decl_functs[0].input;
808 
809     /* and which contains no arguments */
810     for( j = 0; j < MAX_ARGS; j++ )
811       quit->arg[j] = NULL;
812 
813     /* assign it to the item in the menu */
814     XtAddCallback(item, XtNcallback, menu_item_handler, quit);
815   }
816 
817 }
818 
819