dtcm: Coverity 88353
[oweals/cde.git] / cde / programs / dtwm / WmResParse.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* 
24  * (c) Copyright 1989, 1990, 1991, 1992, 1993, 1994 OPEN SOFTWARE FOUNDATION, INC. 
25  * ALL RIGHTS RESERVED 
26 */ 
27 /* 
28  * Motif Release 1.2.4
29 */ 
30 /*
31  * (c) Copyright 1987, 1988, 1989, 1990, 1993, 1994 Hewlett-Packard Company
32  * (c) Copyright 1993, 1994 International Business Machines Corp.
33  * (c) Copyright 1993, 1994 Sun Microsystems, Inc.
34  * (c) Copyright 1993, 1994 Novell, Inc.
35  */
36 /*
37  * (c) Copyright 1987, 1988 DIGITAL EQUIPMENT CORPORATION */
38 /*
39  * (c) Copyright 1988 MASSACHUSETTS INSTITUTE OF TECHNOLOGY */
40
41 /*
42  * Included Files:
43  */
44
45 #include "WmGlobal.h"
46 #include "WmResNames.h"
47 #include <Dt/UserMsg.h>
48 #include <Dt/Connect.h>
49 #include <Tt/tt_c.h>
50 #include "WmParse.h"
51 #include "WmParseP.h"
52 #include "WmPanelP.h"
53 #include "WmResource.h"
54
55 #include <codelibs/shellutils.h>    /* shellscan */
56
57 #include <Xm/VirtKeysP.h>
58 #include <Xm/XmPrivate.h>      /* _XmVirtKeysLoadFileBindings,
59                                 * _XmVirtKeysLoadFallbackBindings */
60
61
62 #include <X11/cursorfont.h>
63 #include <X11/keysym.h>
64 #include <X11/Xatom.h>
65 #include <ctype.h>
66
67 #include <locale.h>
68
69 #include <stdlib.h>
70
71 #include <Xm/XmP.h>             /* for XmeGetHomeDirName */
72 #include <signal.h>
73
74 /* maximum string lengths */
75
76 #define MAX_KEYSYM_STRLEN    100
77 #define MAX_EVENTTYPE_STRLEN  20
78 #define MAX_MODIFIER_STRLEN   20
79 #define MAX_CONTEXT_STRLEN    20
80 #define MAX_GROUP_STRLEN      20
81
82 #define min(a,b)        ((a)>(b) ? (b) : (a))
83
84 #define MAXLINE     (MAXWMPATH+1)
85
86 #define MBBSIZ      4096
87
88 #define PARSE_MENU_ITEMS(pSD, mSpec) ParseMenuItems(pSD)
89
90 /*
91  * include extern functions
92  */
93 #include "WmResParse.h"
94 #include "WmWrkspace.h"
95 #include "WmError.h"
96 #include "WmFunction.h"
97 #include "WmImage.h"
98 #include "WmXSMP.h"
99
100 # include <errno.h>
101 # ifdef X_NOT_STDC_ENV
102 extern int errno;
103 # endif
104 # define HOME_DT_WMRC    "/.dt/dtwmrc"
105 # define LANG_DT_WMRC    "/dtwmrc"
106 # define SYS_DT_WMRC     CDE_CONFIGURATION_TOP "/sys.dtwmrc"
107
108 /*
109  * Global Variables And Tables:
110  */
111 static char cfileName[MAXWMPATH+1];
112 #ifndef NO_MESSAGE_CATALOG
113 char * pWarningStringFile;
114 char * pWarningStringLine;
115 #else
116 char pWarningStringFile[] = "%s: %s on line %d of configuration file %s\n";
117 char pWarningStringLine[] = "%s: %s on line %d of specification string\n";
118 #endif
119 #define cfileP  (wmGD.pWmPB->pFile)
120 #define parseP  (wmGD.pWmPB->pchNext)
121 #define line    (wmGD.pWmPB->pchLine)
122 #define linec   (wmGD.pWmPB->lineNumber)
123
124
125 typedef struct {
126    char         *name;
127    unsigned int  mask;
128 } MaskTableEntry;
129
130 static MaskTableEntry modifierStrings[] = {
131
132     {"none",    None},
133     {"ctrl",    ControlMask},
134     {"shift",   ShiftMask},
135     {"alt",     Mod1Mask},
136     {"meta",    Mod1Mask},
137     {"lock",    LockMask},
138     {"mod1",    Mod1Mask},
139     {"mod2",    Mod2Mask},
140     {"mod3",    Mod3Mask},
141     {"mod4",    Mod4Mask},
142     {"mod5",    Mod5Mask},
143     {NULL,      0},
144 };
145
146 #define ALT_INDEX 3
147 #define META_INDEX 4
148
149 typedef struct {
150    char         *event;
151    unsigned int  eventType;
152    Boolean       (*parseProc)();
153    unsigned int  closure;
154    Boolean       fClick;
155 } EventTableEntry;
156
157 static String GetNetworkFileName (char *pchFile);
158 FILE *FopenConfigFile (void);
159 void SaveMenuAccelerators (WmScreenData *pSD, MenuSpec *newMenuSpec);
160 static void ParseMenuSet (WmScreenData *pSD, unsigned char *lineP);
161 MenuItem *ParseMwmMenuStr (WmScreenData *pSD, unsigned char *menuStr);
162 static MenuItem *ParseMenuItems (WmScreenData *pSD);
163 static Boolean ParseWmLabel (WmScreenData *pSD, MenuItem *menuItem, 
164                              unsigned char *string);
165 static void ParseWmMnemonic (unsigned char **linePP, MenuItem *menuItem);
166 static Boolean ParseWmAccelerator (unsigned char **linePP, MenuItem *menuItem);
167 int ParseWmFunction (unsigned char **linePP, unsigned int res_spec, 
168                             WmFunction *pWmFunction);
169 static Boolean ParseWmFuncNoArg (unsigned char **linePP, WmFunction wmFunction,
170                                  String *pArgs);
171 void FreeMenuItem (MenuItem *menuItem);
172 static Boolean ParseWmFuncGrpArg (unsigned char **linePP, 
173                                   WmFunction wmFunction, GroupArg *pGroup);
174 static Boolean ParseWmFuncNbrArg (unsigned char **linePP, 
175                                   WmFunction wmFunction, 
176                                   unsigned long *pNumber);
177 void ParseButtonStr (WmScreenData *pSD, unsigned char *buttonStr);
178 static void ParseButtonSet (WmScreenData *pSD, unsigned char *lineP);
179 static Boolean ParseContext (unsigned char **linePP, Context *context, 
180                              Context *subContext);
181 void
182 ParseKeyStr (WmScreenData *pSD, unsigned char *keyStr);
183 static void ParseKeySet (WmScreenData *pSD, unsigned char *lineP);
184 Boolean ParseBtnEvent (unsigned char  **linePP,
185                        unsigned int *eventType,
186                        unsigned int *button,
187                        unsigned int *state,
188                        Boolean      *fClick);
189 Boolean ParseKeyEvent (unsigned char **linePP, unsigned int *eventType,
190                        KeyCode *keyCode,  unsigned int *state);
191 static Boolean ParseEvent (unsigned char **linePP, EventTableEntry *table,
192                            unsigned int *eventType, unsigned int *detail,
193                            unsigned int *state, Boolean *fClick);
194 static Boolean ParseModifiers(unsigned char **linePP, unsigned int *state);
195 static Boolean LookupModifier (unsigned char *name, unsigned int *valueP);
196 static Boolean ParseEventType (unsigned char **linePP, EventTableEntry *table,
197                                unsigned int *eventType, Cardinal *ix);
198 static Boolean ParseImmed (unsigned char **linePP, unsigned int closure,
199                            unsigned int  *detail);
200 static Boolean ParseKeySym (unsigned char **linePP, unsigned int closure,
201                             unsigned int *detail);
202 static unsigned int StrToNum(unsigned char *str);
203 static unsigned int StrToHex(unsigned char *str);
204 static unsigned int StrToOct(unsigned char *str);
205 void ScanAlphanumeric (unsigned char **linePP);
206 void ScanWhitespace(unsigned char  **linePP);
207 void ToLower (char  *string);
208 void
209 PWarning (char *message);
210 static void ProcessAccelText (unsigned char *startP, unsigned char *endP,
211                               unsigned char *destP);
212 void ProcessCommandLine (int argc,  char *argv[]);
213 static void ParseScreensArgument (int argc, char *argv[], int *pArgnum,
214                                   unsigned char *lineP);
215 void ProcessMotifBindings (void);
216 static void ParseIncludeSet (WmScreenData *pSD, unsigned char *lineP);
217 static void ConfigStackInit (char *pchFileName);
218 static FILE *ConfigStackPush (unsigned char *pchFileName);
219 static void ConfigStackPop (void);
220 Boolean ParseWmFuncActionArg (unsigned char **linePP, 
221                                   WmFunction wmFunction, String *pArgs);
222 static void PreprocessConfigFile (void);
223
224 static EventTableEntry buttonEvents[] = {
225
226     {"btn1down",    ButtonPress,    ParseImmed,    SELECT_BUTTON,  FALSE},
227     {"btn1up",      ButtonRelease,  ParseImmed,    SELECT_BUTTON,  FALSE},
228     {"btn1click",   ButtonRelease,  ParseImmed,    SELECT_BUTTON,  TRUE},
229     {"btn1click2",  ButtonPress,    ParseImmed,    SELECT_BUTTON,  TRUE},
230     {"btn2down",    ButtonPress,    ParseImmed,    DMANIP_BUTTON,  FALSE},
231     {"btn2up",      ButtonRelease,  ParseImmed,    DMANIP_BUTTON,  FALSE},
232     {"btn2click",   ButtonRelease,  ParseImmed,    DMANIP_BUTTON,  TRUE},
233     {"btn2click2",  ButtonPress,    ParseImmed,    DMANIP_BUTTON,  TRUE},
234     {"btn3down",    ButtonPress,    ParseImmed,    BMENU_BUTTON,  FALSE},
235     {"btn3up",      ButtonRelease,  ParseImmed,    BMENU_BUTTON,  FALSE},
236     {"btn3click",   ButtonRelease,  ParseImmed,    BMENU_BUTTON,  TRUE},
237     {"btn3click2",  ButtonPress,    ParseImmed,    BMENU_BUTTON,  TRUE},
238     {"btn4down",    ButtonPress,    ParseImmed,    Button4,  FALSE},
239     {"btn4up",      ButtonRelease,  ParseImmed,    Button4,  FALSE},
240     {"btn4click",   ButtonRelease,  ParseImmed,    Button4,  TRUE},
241     {"btn4click2",  ButtonPress,    ParseImmed,    Button4,  TRUE},
242     {"btn5down",    ButtonPress,    ParseImmed,    Button5,  FALSE},
243     {"btn5up",      ButtonRelease,  ParseImmed,    Button5,  FALSE},
244     {"btn5click",   ButtonRelease,  ParseImmed,    Button5,  TRUE},
245     {"btn5click2",  ButtonPress,    ParseImmed,    Button5,  TRUE},
246     { NULL, 0, (Boolean(*)())NULL,  0, FALSE}
247 };
248
249
250 static EventTableEntry keyEvents[] = {
251
252     {"key",         KeyPress,    ParseKeySym,    0,  FALSE},
253     { NULL,         0,    (Boolean(*)())NULL,    0,  FALSE}
254 };
255
256 typedef struct _ConfigFileStackEntry {
257     char                *fileName;
258     char                *tempName;
259     char                *cppName;
260     char                *wmgdConfigFile;
261     long                offset;
262     DtWmpParseBuf       *pWmPB;
263     struct _ConfigFileStackEntry        *pIncluder;
264     
265 } ConfigFileStackEntry;
266
267 static ConfigFileStackEntry *pConfigStack = NULL;
268 static ConfigFileStackEntry *pConfigStackTop = NULL;
269
270
271 unsigned int buttonModifierMasks[] = {
272     0,
273     SELECT_BUTTON_MASK,
274     DMANIP_BUTTON_MASK,
275     BMENU_BUTTON_MASK,
276     Button4Mask,
277     Button5Mask
278 };
279
280 /*
281  * FUNCTION PARSER TABLE (function names must be in alphabetic order)
282  */
283
284 typedef struct {
285    char         * funcName;
286    Context        greyedContext;
287    unsigned int   resource;
288    long           mgtMask;
289    WmFunction     wmFunction;
290    Boolean       (*parseProc)();
291 } FunctionTableEntry;
292
293
294 /*
295  * NOTE: New functions MUST be added in ALPHABETICAL order.  A binary search
296  *       is used to find the correct function name.
297  */
298
299 FunctionTableEntry functionTable[] = {
300     {"f.action",        0,
301                         CRS_ANY,
302                         0,
303                         F_Action,
304                         ParseWmFuncActionArg},
305     {"f.beep",          0,
306                         CRS_ANY,
307                         0,
308                         F_Beep,
309                         ParseWmFuncNoArg},
310     {"f.circle_down",   F_SUBCONTEXT_IB_IICON|F_SUBCONTEXT_IB_WICON,
311                         CRS_ANY,
312                         0,
313                         F_Circle_Down,
314                         ParseWmFuncGrpArg},
315     {"f.circle_up",     F_SUBCONTEXT_IB_IICON|F_SUBCONTEXT_IB_WICON,
316                         CRS_ANY,
317                         0,
318                         F_Circle_Up,
319                         ParseWmFuncGrpArg},
320     {"f.create_workspace", 0,
321                         CRS_ANY,
322                         0,
323                         F_CreateWorkspace,
324                         ParseWmFuncNoArg},
325     {"f.delete_workspace", 0,
326                         CRS_ANY,
327                         0,
328                         F_DeleteWorkspace,
329                         ParseWmFuncNoArg},
330     {"f.exec",          0,
331                         CRS_ANY,
332                         0,
333                         F_Exec,
334                         ParseWmFuncStrArg},
335     {"f.focus_color",   F_SUBCONTEXT_IB_IICON|F_SUBCONTEXT_IB_WICON,
336                         CRS_ANY,
337                         0,
338                         F_Focus_Color,
339                         ParseWmFuncNoArg},
340     {"f.focus_key",     F_SUBCONTEXT_IB_IICON|F_SUBCONTEXT_IB_WICON,
341                         CRS_ANY,
342                         0,
343                         F_Focus_Key,
344                         ParseWmFuncNoArg},
345     {"f.goto_workspace", 0,
346                         CRS_ANY,
347                         0,
348                         F_Goto_Workspace,
349                         ParseWmFuncStrArg},
350     {"f.help",          0,
351                         CRS_ANY,
352                         0,
353                         F_Help,
354                         ParseWmFuncStrArg},  /* [helpvolume && helptopic] */
355     {"f.help_mode",     0,
356                         CRS_ANY,
357                         0,
358                         F_Help_Mode,
359                         ParseWmFuncNoArg},  /* for now */
360     {"f.kill",          F_CONTEXT_ROOT,
361                         CRS_ANY,
362                         MWM_FUNC_CLOSE,
363                         F_Kill,
364                         ParseWmFuncNoArg},
365     {"f.lower",         F_SUBCONTEXT_IB_IICON|F_SUBCONTEXT_IB_WICON,
366                         CRS_ANY,
367                         0,
368                         F_Lower,
369                         ParseWmFuncMaybeStrArg},
370     {"f.marquee_selection",     
371                         F_CONTEXT_WINDOW|F_CONTEXT_ICON|F_SUBCONTEXT_IB_IICON,
372                         CRS_ANY,
373                         0,
374                         F_Marquee_Selection,
375                         ParseWmFuncNoArg},
376     {"f.maximize",      F_CONTEXT_ROOT|F_CONTEXT_MAXIMIZE|
377                                        F_SUBCONTEXT_IB_WICON,
378                         CRS_ANY,
379                         MWM_FUNC_MAXIMIZE,
380                         F_Maximize,
381                         ParseWmFuncNoArg},
382     {"f.menu",          0,
383                         CRS_ANY,
384                         0,
385                         F_Menu,
386                         ParseWmFuncStrArg},
387     {"f.minimize",      F_CONTEXT_ICON|F_CONTEXT_ROOT|F_SUBCONTEXT_IB_IICON,
388                         CRS_ANY,
389                         MWM_FUNC_MINIMIZE,
390                         F_Minimize,
391                         ParseWmFuncNoArg},
392     {"f.move",          F_CONTEXT_ROOT,
393                         CRS_ANY,
394                         MWM_FUNC_MOVE,
395                         F_Move,
396                         ParseWmFuncNoArg},
397     {"f.next_cmap",     0,
398                         CRS_ANY,
399                         0,
400                         F_Next_Cmap,
401                         ParseWmFuncNoArg},
402     {"f.next_key",      0,
403                         CRS_ANY,
404                         0,
405                         F_Next_Key,
406                         ParseWmFuncGrpArg},
407     {"f.next_workspace",        0,
408                         CRS_ANY,
409                         0,
410                         F_Next_Workspace,
411                         ParseWmFuncNoArg},
412     {"f.nop",           F_CONTEXT_ROOT|F_CONTEXT_ICON|F_CONTEXT_WINDOW|
413                             F_SUBCONTEXT_IB_WICON | F_SUBCONTEXT_IB_IICON,
414                         CRS_ANY,
415                         0,
416                         F_Nop,
417                         ParseWmFuncNoArg},
418     {"f.normalize",     F_CONTEXT_ROOT|F_CONTEXT_NORMAL|F_SUBCONTEXT_IB_WICON,
419                         CRS_ANY,
420                         0,
421                         F_Normalize,
422                         ParseWmFuncNoArg},
423     {"f.normalize_and_raise",
424                         F_CONTEXT_ROOT|F_CONTEXT_NORMAL,
425                         CRS_ANY,
426                         0,
427                         F_Normalize_And_Raise,
428                         ParseWmFuncMaybeStrArg},
429     {"f.occupy_all", F_CONTEXT_ICONBOX|F_CONTEXT_ROOT,
430                         CRS_ANY,
431                         DtWM_FUNC_OCCUPY_WS,
432                         F_AddToAllWorkspaces,
433                         ParseWmFuncNoArg},
434     {"f.pack_icons",    0,
435                         CRS_ANY,
436                         0,
437                         F_Pack_Icons,
438                         ParseWmFuncNoArg},
439     {"f.pass_keys",     0,
440                         CRS_ANY,
441                         0,
442                         F_Pass_Key,
443                         ParseWmFuncNoArg},
444     {"f.post_wmenu",    0,
445                         CRS_BUTTON|CRS_KEY,
446                         0,
447                         F_Post_SMenu,
448                         ParseWmFuncNoArg},
449     {"f.prev_cmap",     0,
450                         CRS_ANY,
451                         0,
452                         F_Prev_Cmap,
453                         ParseWmFuncNoArg},
454     {"f.prev_key",      0,
455                         CRS_ANY,
456                         0,
457                         F_Prev_Key,
458                         ParseWmFuncGrpArg},
459     {"f.prev_workspace",        0,
460                         CRS_ANY,
461                         0,
462                         F_Prev_Workspace,
463                         ParseWmFuncNoArg},
464     {"f.quit_mwm",      F_CONTEXT_ICON|F_CONTEXT_WINDOW,
465                         CRS_ANY,
466                         0,
467                         F_Quit_Mwm,
468                         ParseWmFuncNoArg},
469     {"f.raise",         F_SUBCONTEXT_IB_IICON|F_SUBCONTEXT_IB_WICON,
470                         CRS_ANY,
471                         0,
472                         F_Raise,
473                         ParseWmFuncMaybeStrArg},
474     {"f.raise_lower",   F_CONTEXT_ROOT |
475                                 F_SUBCONTEXT_IB_IICON|F_SUBCONTEXT_IB_WICON,
476                         CRS_ANY,
477                         0,
478                         F_Raise_Lower,
479                         ParseWmFuncNoArg},
480     {"f.refresh",       0,
481                         CRS_ANY,
482                         0,
483                         F_Refresh,
484                         ParseWmFuncNoArg},
485     {"f.refresh_win",   F_CONTEXT_ICON|F_CONTEXT_ROOT,
486                         CRS_ANY,
487                         0,
488                         F_Refresh_Win,
489                         ParseWmFuncNoArg},
490     {"f.remove",        F_CONTEXT_ROOT,
491                         CRS_ANY,
492                         DtWM_FUNC_OCCUPY_WS,
493                         F_Remove,
494                         ParseWmFuncNoArg},
495     {"f.resize",        F_CONTEXT_ICON|F_CONTEXT_ROOT|
496                                  F_SUBCONTEXT_IB_IICON|F_SUBCONTEXT_IB_WICON,
497                         CRS_ANY,
498                         MWM_FUNC_RESIZE,
499                         F_Resize,
500                         ParseWmFuncNoArg},
501     {"f.restart",       F_CONTEXT_ICON|F_CONTEXT_WINDOW,
502                         CRS_ANY,
503                         0,
504                         F_Restart,
505                         ParseWmFuncStrArg},
506     {"f.restore",       F_CONTEXT_ROOT|F_CONTEXT_NORMAL|F_SUBCONTEXT_IB_WICON,
507                         CRS_ANY,
508                         0,
509                         F_Restore,
510                         ParseWmFuncNoArg},
511     {"f.restore_and_raise",
512                         F_CONTEXT_ROOT|F_CONTEXT_NORMAL,
513                         CRS_ANY,
514                         0,
515                         F_Restore_And_Raise,
516                         ParseWmFuncNoArg},
517     {"f.screen",        0,
518                         CRS_ANY,
519                         0,
520                         F_Screen,
521                         ParseWmFuncStrArg},
522     {"f.send_msg",      F_CONTEXT_ROOT,
523                         CRS_ANY,
524                         0,
525                         F_Send_Msg,
526                         ParseWmFuncNbrArg},
527     {"f.separator",     0,
528                         CRS_MENU,
529                         0,
530                         F_Separator,
531                         ParseWmFuncNoArg},
532     {"f.set_behavior",  0,
533                         CRS_ANY,
534                         0,
535                         F_Set_Behavior,
536                         ParseWmFuncNoArg},
537     {"f.set_context",   0,
538                         CRS_ANY,
539                         0,
540                         F_Set_Context,
541                         ParseWmFuncNbrArg},
542     {"f.title",         0,
543                         CRS_MENU,
544                         0,
545                         F_Title,
546                         ParseWmFuncNoArg},
547     {"f.toggle_frontpanel", 0,
548                         CRS_ANY,
549                         0,
550                         F_Toggle_Front_Panel,
551                         ParseWmFuncNoArg},
552
553     {"f.version",       0,
554                         CRS_ANY,
555                         0,
556                         F_Version,
557                         ParseWmFuncNoArg},
558
559 #ifdef OLD
560     {"f.workspace_presence",F_CONTEXT_ICON|F_CONTEXT_ROOT|F_CONTEXT_ICONBOX|
561                             F_SUBCONTEXT_IB_IICON|F_SUBCONTEXT_IB_WICON,
562 #endif /* OLD */
563     {"f.workspace_presence", F_CONTEXT_ROOT|F_CONTEXT_ICONBOX|
564                         F_SUBCONTEXT_IB_WICON,
565                         CRS_ANY,
566                         DtWM_FUNC_OCCUPY_WS,
567                         F_Workspace_Presence,
568                         ParseWmFuncNoArg},
569 #if defined(DEBUG)
570     {"f.zz_debug",      0,
571                         CRS_ANY,
572                         0,
573                         F_ZZ_Debug,
574                         ParseWmFuncStrArg},
575 #endif /* DEBUG */
576 };
577
578 /*
579  * NOTE: New functions MUST be added in ALPHABETICAL order.  A binary search
580  *       is used to find the correct function name.
581  */
582
583 #define WMFUNCTIONTABLESIZE (sizeof(functionTable)/sizeof(functionTable[0]))
584
585 /*
586  * Be sure to update these define, whenever adding/deleting a function.
587  */
588 int F_ACTION_INDEX;
589 int F_EXEC_INDEX;
590 int F_NOP_INDEX;
591
592 /******************************<->*************************************
593  *
594  *  void GetFunctionTableValues (int *execIndex, int *nopIndex, 
595  *                               int *actionIndex)
596  *
597  *  Description:
598  *  -----------
599  *  This routine dynamically computes the size of the functionTable[],
600  *  and the indices for key functions, such as f.exec, f.action, and
601  *  f.nop
602  *
603  *  Inputs:
604  *  ------
605  *  We are setting the values of F_EXEC_INDEX, F_ACTION_INDEX, 
606  *  and F_NOP_INDEX on a global level.  The addresses
607  *  for same are passed in.
608  * 
609  *  Outputs:
610  *  -------
611  *
612  *  Comments:
613  *  --------
614  *  This routine calls smaller routines for efficiency sake.
615  *
616  ******************************<->***********************************/
617 void
618 GetFunctionTableValues (int *execIndex, int *nopIndex,
619                     int *actionIndex)
620 {
621
622         GetExecIndex (WMFUNCTIONTABLESIZE, execIndex);
623
624         GetActionIndex (WMFUNCTIONTABLESIZE, actionIndex);
625
626         GetNopIndex (WMFUNCTIONTABLESIZE, nopIndex);
627
628 } /* END OF FUNCTION GetFunctionTableValues */
629
630
631 \f
632 /******************************<->*************************************
633  *
634
635  *
636  *  Description:
637  *  -----------
638
639  *
640  *  Inputs:
641  *  ------
642
643  *  Outputs:
644  *  -------
645  *
646  *  Comments:
647  *  --------
648  *
649  ******************************<->***********************************/
650
651 void
652 GetExecIndex (int tableSize, int *execIndex)
653 {
654         int i;
655
656         for (i = 0; i < (tableSize); i++)
657         {
658                 if (!(strcmp ("f.exec", functionTable[i].funcName)))
659                 {
660                         *execIndex = i;
661                         return;
662                 }
663                 else
664                 {
665                         *execIndex = 0;
666                 }
667         }
668 } /* END OF FUNCTION GetExecIndex */
669
670 \f
671 /******************************<->*************************************
672  *
673
674  *
675  *  Description:
676  *  -----------
677
678  *
679  *  Inputs:
680  *  ------
681
682  *  Outputs:
683  *  -------
684  *
685  *  Comments:
686  *  --------
687  *
688  ******************************<->***********************************/
689
690
691 void
692 GetActionIndex (int tableSize, int *actionIndex)
693 {
694         int i;
695
696         for (i = 0; i < (tableSize); i++)
697         {
698                 if (!(strcmp ("f.action", functionTable[i].funcName)))
699                 {
700                         *actionIndex = i;
701                         return;
702                 }
703                 else
704                 {
705                         *actionIndex = 0;
706                 }
707         }
708 } /* END OF FUNCTION GetActionIndex */
709
710
711 \f
712 /******************************<->*************************************
713  *
714
715  * 
716  *  Description:
717  *  -----------
718
719  *
720  *  Inputs:
721  *  ------
722
723  *  Outputs:
724  *  -------
725  *
726  *  Comments:
727  *  --------
728  *
729  ******************************<->***********************************/
730 void
731 GetNopIndex (int tableSize, int *nopIndex)
732 {
733         int i;
734
735         for (i = 0; i < (tableSize); i++)
736         {
737                 if (!(strcmp ("f.nop", functionTable[i].funcName)))
738                 {
739                         *nopIndex = i;
740                         return;
741                 }
742                 else
743                 {
744                         *nopIndex = 0;
745                 }
746         }
747 } /* END OF FUNCTION GetNopIndex */
748
749
750
751 \f
752 /*************************************<->*************************************
753  *
754  *  void
755  *  WmDtGetHelpArgs ()
756  *
757  *
758  *  Description:
759  *  -----------
760  *  Get Help Args
761
762  *
763  *  Inputs:
764  *  ------
765
766  * 
767  *  Outputs:
768  *  -------
769  *
770  *
771  *  Comments:
772  *  --------
773  *  None.
774  * 
775  *************************************<->***********************************/
776 void
777 WmDtGetHelpArgs(char *args, 
778                  unsigned char* volume, 
779                  unsigned char* topic, 
780                  int *argsCount)
781 {
782     unsigned char *string;
783     unsigned char *lineP;
784
785     cfileP = NULL;
786     linec = 0;
787     parseP = (unsigned char*) args;
788
789     if(GetNextLine () != NULL)
790     {
791         *argsCount = 0;
792         lineP = line;
793         if ((string = GetSmartSMString (&lineP)) != NULL)
794         {
795             *argsCount = *argsCount + 1;
796             strcpy ((char*)topic, (char*)string);
797         }
798
799         if ((string = GetSmartSMString (&lineP)) != NULL)
800         {
801             *argsCount = *argsCount + 1;
802             strcpy ((char*)volume, (char *)string);
803         }
804     }
805
806 } /* END OF FUNCTION WmDtGetHelpArgs */
807
808
809
810
811 \f
812 /******************************<->*************************************
813  *
814  *  void
815  *  ParseDtSessionHints (pSD, property)
816  *
817  *
818  *  Description:
819  *  -----------
820  *  This function parses a DtSessionHints string and returns a list of 
821  *  DtSessionItems array.  The string should have the syntax:
822  *
823
824  *
825  *
826  *  Inputs:
827  *  ------
828  *  line = (global) line buffer
829  *  pSD->rootWindow = default root window of display
830  *
831  * 
832  *  Outputs:
833  *  -------
834  *  Return = 
835  *
836  *
837  *  Comments:
838  *  --------
839  *  None.
840  * 
841  *************************************<->***********************************/
842
843 void 
844 ParseDtSessionHints (WmScreenData *pSD, unsigned char *property)
845 {
846
847     cfileP = NULL;
848     linec = 0;
849     parseP = property;
850
851     ParseSessionItems (pSD);
852
853 } /* END OF FUNCTION ParseDtSessionHints */
854
855 \f
856 /*************************************<->*************************************
857  *
858  *  FindDtSessionMatch(commandArgc, commandArgv, pCD, pSD, pWorkSpaceList,
859  *                      clientMachine)
860  *
861  *  Description:
862  *  -----------
863  *  Try to find a match for this client in the session hints.
864  *  Set up client-session data.
865  *
866  *
867  *  Inputs:
868  *  ------
869  *  commandArgc    -  argument count
870  *  commandArgv    -  WM_COMMAND argument vector
871  *  pCD            -  pointer to client data
872  *  pSD            -  pointer to screen data
873  *  pWorkspaceList -  pointer to a list of workspaces (to be returned)
874  *  clientMachine  -  string for -host option in session hints
875  *
876  *  Outputs:
877  *  -------
878  *  *pCD            -  client data (may be modified)
879  *  FindDtSessionMatch - returns True if a match for this client
880  *                        was found in the session hints.
881  *  *pWorkspaceList - list of workspaces this client should be put 
882  *                    into.  (needs to be freed)
883  *
884  *
885  *  Comments:
886  *  --------
887  *  Various pieces of client state (in pCD) are reset when a match
888  *     is found.
889  *
890  *  The caller must free *pWorkspaceList when done.
891  * 
892  *************************************<->***********************************/
893 Boolean FindDtSessionMatch(int commandArgc, char **commandArgv,
894                             ClientData *pCD, WmScreenData *pSD,
895                             char **pWorkSpaceList, char *clientMachine)
896
897 {
898     int count;
899     int relCount;
900     int argNum;
901     SessionGeom *sessionGeom;
902
903
904     for (count = 0; count < pSD->totalSessionItems; count++)
905     {
906         if (!pSD->pDtSessionItems[count].processed &&
907             pSD->pDtSessionItems[count].commandArgc == commandArgc)
908         {
909             if ((clientMachine) &&
910                 (pSD->pDtSessionItems[count].clientMachine) &&
911                 (strcmp(clientMachine, 
912                         pSD->pDtSessionItems[count].clientMachine)))
913             {
914                 /*
915                  * This item has clientMachine string but the 
916                  * clientMachine does not match.
917                  */
918                 continue;
919             }
920             for (argNum = 0; argNum < commandArgc ; argNum++)
921             {
922                 if(strcmp(commandArgv[argNum],
923                           pSD->pDtSessionItems[count].commandArgv[argNum]))
924
925                 {
926                     /*
927                      * One mismatch and we quit looking at this item.
928                      * Decrement argNum so a mismatch on the last item
929                      * will not look like a match below when comparing
930                      * argNum == commandArgc
931                      */
932                     argNum--;
933                     break;
934                 }
935             }           
936             if (argNum == commandArgc)
937             {
938                 /*
939                  * Made it through all strings so this is a match
940                  */
941                 
942                 pSD->pDtSessionItems[count].processed = True;
943                 pSD->remainingSessionItems --;
944                 pCD->clientFlags |= SM_LAUNCHED;
945                 
946                 /*
947                  * Free strings malloc'd for commandArgv for this item 
948                  */
949                 
950                 for (relCount = 0; relCount < commandArgc; relCount++)
951                 {
952                     XtFree(pSD->pDtSessionItems[count].commandArgv[relCount]);
953                 }
954                 XtFree((char *)pSD->pDtSessionItems[count].commandArgv);
955
956                 if(pSD->pDtSessionItems[count].clientState)
957                 {
958                     pCD->clientState =
959                         pSD->pDtSessionItems[count].clientState;
960                     pCD->clientFlags |= SM_CLIENT_STATE;
961                 }
962                 
963                 if(pSD->pDtSessionItems[count].sessionGeom)
964                 {
965                     sessionGeom = pSD->pDtSessionItems[count].sessionGeom;
966                     if (sessionGeom->flags & XValue)
967                     {
968                         pCD->clientX = sessionGeom->clientX;
969                         pCD->clientFlags |= SM_X;
970                     }
971                     if (sessionGeom->flags & YValue)
972                     {
973                         pCD->clientY = sessionGeom->clientY;
974                         pCD->clientFlags |= SM_Y;
975                     }
976                     if (sessionGeom->flags & WidthValue)
977                     {
978                         pCD->clientWidth = sessionGeom->clientWidth;
979                         pCD->clientFlags |= SM_WIDTH;
980                     }
981                     if (sessionGeom->flags & HeightValue)
982                     {
983                         pCD->clientHeight = sessionGeom->clientHeight;
984                         pCD->clientFlags |= SM_HEIGHT;
985                     }
986
987                     /*
988                      * Free SessionGeom malloc'd space for this item 
989                      */
990                     
991                     XtFree((char *)pSD->pDtSessionItems[count].sessionGeom); 
992                 }
993
994                 if(pSD->pDtSessionItems[count].clientMachine)
995                 {
996                     /*
997                      * Free clientMachine malloc'd space for this item 
998                      */
999                     
1000                     XtFree((char *)
1001                            pSD->pDtSessionItems[count].clientMachine); 
1002                     pSD->pDtSessionItems[count].clientMachine = NULL;
1003                 }
1004                 
1005                 
1006                 if(pSD->pDtSessionItems[count].workspaces)
1007                 {
1008                     /*
1009                      * The caller is responsible for freeing this
1010                      * data.
1011                      */
1012                     *pWorkSpaceList = pSD->pDtSessionItems[count].workspaces;
1013                 }
1014
1015
1016                 if(pSD->remainingSessionItems == 0)
1017                 {
1018                     /*
1019                      * Free the whole pSD->pDtSessionHints structure 
1020                      */
1021                     XtFree((char *)pSD->pDtSessionItems);
1022                 }
1023                 
1024                 return (True);
1025             }
1026
1027         } /* not processed and argc's are the same */
1028
1029     } /* for */
1030     
1031     return (False);
1032     
1033 } /* END OF FUNCTION FindDtSessionMatch */
1034
1035
1036
1037
1038 \f
1039 /*************************************<->*************************************
1040  *
1041  *  void
1042  *  ParseSessionItems (pSD)
1043  *
1044  *
1045  *  Description:
1046  *  -----------
1047  *  Parse session items
1048
1049  *
1050  *  Inputs:
1051  *  ------
1052  *  pSD    = pointer to screen data
1053  *  cfileP = (global) file pointer to  NULL
1054  *  line   = (global) line buffer
1055  *  linec  = (global) line count
1056  *  parseP = (global) parse string pointer if cfileP == NULL
1057  *  pSD->rootWindow = default root window of display
1058
1059  * 
1060  *  Outputs:
1061  *  -------
1062  *  linec  = (global) line count incremented
1063  *  parseP = (global) parse string pointer if cfileP == NULL
1064  *
1065  *
1066  *  Comments:
1067  *  --------
1068  *  None.
1069  * 
1070  *************************************<->***********************************/
1071
1072 void
1073 ParseSessionItems (WmScreenData *pSD)
1074 {
1075     unsigned char *string;
1076     unsigned char *lineP;
1077     int count;
1078
1079     
1080     /*
1081      * Parse property string
1082      */
1083
1084     
1085
1086     if(GetNextLine () != NULL)
1087     {
1088         pSD->totalSessionItems = atoi((char *)line);
1089         pSD->remainingSessionItems = pSD->totalSessionItems;
1090     }
1091     
1092
1093     if((pSD->totalSessionItems < 1) ||
1094        !GetSessionHintsInfo(pSD, pSD->totalSessionItems))
1095     {
1096         /*
1097          * No items or couldn't allocate space
1098          */
1099         return;
1100     }
1101     
1102     count = 0;
1103
1104     while ((count < pSD->totalSessionItems) && (GetNextLine () != NULL))
1105     {
1106
1107         lineP = line;
1108         while ((string = GetSmartSMString (&lineP)) != NULL)
1109         {
1110             if (!strcmp((char *)string, "-geometry"))
1111             {
1112                 /*
1113                  * Parse geometry if it is present
1114                  */
1115                 string = GetSmartSMString(&lineP);
1116                 ParseSessionGeometry (pSD, count, string);
1117             }
1118             
1119             else if (!strcmp((char *)string, "-state"))
1120             {
1121                 /*
1122                  * Parse the state if it is present
1123                  */
1124                 string = GetSmartSMString(&lineP);
1125                 ParseSessionClientState (pSD, count, string);
1126             }
1127
1128             else if (!strcmp((char *)string, "-workspaces"))        
1129             {
1130                 /*
1131                  * Parse the workspaces string if it is present
1132                  */
1133                 string = GetSmartSMString(&lineP);
1134                 ParseSessionWorkspaces (pSD, count, string);
1135             }       
1136
1137             else if (!strcmp((char *)string, "-cmd"))       
1138             {
1139                 /*
1140                  * Parse the command string if it is present
1141                  */
1142                 string = GetSmartSMString(&lineP);
1143                 ParseSessionCommand (pSD, count, &string);
1144             }
1145             
1146             else if (!strcmp((char *)string, "-host"))      
1147             {
1148                 /*
1149                  * Parse the host string if it is present
1150                  */
1151                 string = GetSmartSMString(&lineP);
1152                 ParseSessionHost (pSD, count, string);
1153             }
1154             
1155         } /* while GetSmartSMString */
1156         
1157         count++;
1158
1159     } /* while GetNextLine */
1160     
1161
1162
1163 } /* END OF FUNCTION ParseSessionItems */
1164
1165
1166 \f
1167 /*************************************<->*************************************
1168  *
1169  *  ParseSessionClientState (pSD, count, string);
1170  *
1171  *  Description:
1172  *  -----------
1173  *  Inputs:
1174  *  ------
1175  *  Outputs:
1176  *  -------
1177  *  Comments:
1178  *  --------
1179  *
1180  *************************************<->***********************************/
1181 void ParseSessionClientState (WmScreenData *pSD, int count,
1182                               unsigned char *string)
1183
1184
1185
1186 {
1187
1188     if(!strcmp((char *)string, "NormalState"))
1189     {
1190         pSD->pDtSessionItems[count].clientState = NORMAL_STATE;
1191     }
1192     else if(!strcmp((char *)string, "IconicState"))
1193     {
1194         pSD->pDtSessionItems[count].clientState = MINIMIZED_STATE;
1195     }
1196     
1197
1198 } /* END OF FUNCTION ParseSessionClientState */
1199
1200 \f
1201 /*************************************<->*************************************
1202  *
1203  *  ParseSessionGeometry (pSD, count, string)
1204  *
1205  *  Description:
1206  *  -----------
1207  *  Inputs:
1208  *  ------
1209  *  Outputs:
1210  *  -------
1211  *  Comments:
1212  *  --------
1213  *
1214  *************************************<->***********************************/
1215 void ParseSessionGeometry (WmScreenData *pSD, int count,
1216                            unsigned char *string)
1217
1218 {
1219     SessionGeom *pTmpSessionGeom;
1220     int mask /* = 0 */;
1221     int X, Y, width, height;
1222     X = Y = width = height = 0;
1223     
1224     /*
1225      *  XParseGeometry
1226      */
1227
1228     mask = XParseGeometry((char *)string, &X, &Y, (unsigned int *)&width, 
1229                           (unsigned int *)&height);
1230     if (mask)
1231     {
1232         /*
1233          * Allocate space for the geometry structure
1234          */
1235
1236         if ((pTmpSessionGeom = 
1237              (SessionGeom *)XtMalloc (sizeof (SessionGeom))) == NULL)
1238         {
1239             Warning (((char *)GETMESSAGE(60, 1, "Insufficient memory for session geometry item")));
1240             return;
1241
1242         }
1243
1244         pTmpSessionGeom->flags = mask;
1245         pTmpSessionGeom->clientX = X;
1246         pTmpSessionGeom->clientY = Y;
1247         pTmpSessionGeom->clientWidth = width;
1248         pTmpSessionGeom->clientHeight = height;
1249
1250         pSD->pDtSessionItems[count].sessionGeom = pTmpSessionGeom;
1251     }
1252     
1253 } /* END OF FUNCTION  ParseSessionGeometry */
1254
1255 \f
1256 /*************************************<->*************************************
1257  *
1258  * void
1259  * ParseSessionWorkspaces (pSD, count, string)
1260  *
1261  *  Description:
1262  *  -----------
1263  *  Inputs:
1264  *  ------
1265  *  Outputs:
1266  *  -------
1267  *  Comments:
1268  *  --------
1269  *
1270  *************************************<->***********************************/
1271 void ParseSessionWorkspaces (WmScreenData *pSD,  int count,
1272                              unsigned char *string)
1273
1274 {
1275
1276     /*
1277      * Allocate space for the workspaces string
1278      */
1279
1280     if ((pSD->pDtSessionItems[count].workspaces =
1281          (String)XtMalloc ((unsigned int) (strlen((char *)string) + 1))) == NULL)
1282     {
1283         Warning (((char *)GETMESSAGE(60, 2, "Insufficient memory for workspaces list in session item")));
1284         return;
1285
1286     }
1287
1288     strcpy(pSD->pDtSessionItems[count].workspaces, (char *)string);
1289     
1290 } /* END OF FUNCTION ParseSessionWorkspaces */
1291
1292
1293 \f
1294 /*************************************<->*************************************
1295  *
1296  * void
1297  * ParseSessionCommand (pSD, count, string)
1298  *
1299  *  Description:
1300  *  -----------
1301  *  Inputs:
1302  *  ------
1303  *  Outputs:
1304  *  -------
1305  *  Comments:
1306  *  --------
1307  *
1308  *************************************<->***********************************/
1309 void ParseSessionCommand (WmScreenData *pSD,  int count,
1310                           unsigned char **commandString)
1311 {
1312 #define ARG_AMT 100
1313     int xindex;
1314     unsigned char **argv;
1315     int  argc = 0;
1316     int  iSizeArgv;
1317     
1318     unsigned char *string;
1319     
1320     argv = (unsigned char **) XtMalloc (ARG_AMT * sizeof(char *));
1321     iSizeArgv = ARG_AMT;
1322     
1323     while ((string = GetSmartSMString (commandString)) != NULL)
1324     {
1325         /*
1326          * Get pointers to strings in command line and count them
1327          */
1328         argv[argc] = string;
1329         argc ++;
1330
1331         if (argc >= iSizeArgv)
1332         {
1333             iSizeArgv += ARG_AMT;
1334             argv = (unsigned char **) 
1335                    XtRealloc ((char *)argv, (iSizeArgv * sizeof(char *)));
1336         }
1337     }
1338     if ((pSD->pDtSessionItems[count].commandArgv =
1339          (char **)XtMalloc ((argc) * sizeof(char * ))) == NULL)
1340     {
1341         /*
1342          * Allocate space for saved argv
1343          */
1344         
1345         Warning (((char *)GETMESSAGE(60, 3, "Insufficient memory for commandArgv array")));
1346     }
1347     else
1348     {
1349         pSD->pDtSessionItems[count].commandArgc = argc;
1350         for (xindex = 0; xindex < argc ; xindex++)
1351         {
1352             if ((pSD->pDtSessionItems[count].commandArgv[xindex] =
1353                  (String) XtMalloc
1354                  ((unsigned int) (strlen((char *)argv[xindex]) + 1))) == NULL)
1355             {
1356                 /*
1357                  * Allocate space for the next command segment.
1358                  */
1359                 Warning (((char *)GETMESSAGE(60, 4, "Insufficient memory for commandArgv item")));
1360             }
1361             else
1362             {
1363                 strcpy(pSD->pDtSessionItems[count].commandArgv[xindex],
1364                        (char *)argv[xindex]);
1365             }
1366         }
1367     }
1368
1369     XtFree ((char *) argv);
1370     
1371 } /* END OF FUNCTION ParseSessionCommand */
1372
1373
1374 \f
1375 /*************************************<->*************************************
1376  *
1377  * void
1378  * ParseSessionHost (pSD, count, string)
1379  *
1380  *  Description:
1381  *  -----------
1382  *  Inputs:
1383  *  ------
1384  *  Outputs:
1385  *  -------
1386  *  Comments:
1387  *  --------
1388  *
1389  *************************************<->***********************************/
1390 void ParseSessionHost (WmScreenData *pSD,  int count,
1391                              unsigned char *string)
1392
1393 {
1394
1395     /*
1396      * Allocate space for the workspaces string
1397      */
1398
1399     if ((pSD->pDtSessionItems[count].clientMachine =
1400          (String)XtMalloc ((unsigned int) (strlen((char *)string) + 1))) == 
1401         NULL)
1402     {
1403         Warning (((char *)GETMESSAGE(60, 38, 
1404                 "Insufficient memory for host name in session item")));
1405         return;
1406     }
1407
1408     strcpy(pSD->pDtSessionItems[count].clientMachine, (char *)string);
1409     
1410 } /* END OF FUNCTION ParseSessionHost */
1411
1412
1413 \f
1414 /*************************************<->*************************************
1415  *
1416  *  GetSessionHintsInfo (pSD, numItems)
1417  *
1418  *  Description:
1419  *  -----------
1420  *  Inputs:
1421  *  ------
1422  *  Outputs:
1423  *  -------
1424  *  Comments:
1425  *  --------
1426  *
1427  *************************************<->***********************************/
1428 Boolean GetSessionHintsInfo (WmScreenData *pSD, long numItems)
1429
1430 {
1431    
1432     if ((pSD->pDtSessionItems =
1433          (DtSessionItem *)XtMalloc (numItems * sizeof (DtSessionItem)))
1434         == NULL)
1435     {
1436         Warning (((char *)GETMESSAGE(60, 5, "Insufficient memory for Dt Session Hints")));
1437         return(False);
1438     }
1439     
1440     memset ((char *)pSD->pDtSessionItems, 0,
1441             numItems * sizeof (DtSessionItem));
1442
1443     return(True);
1444     
1445     
1446 } /* END OF FUNCTION  GetSessionHintsInfo  */ 
1447
1448
1449
1450 \f
1451 /*************************************<->*************************************
1452  *
1453  *  PeekAhead (currentChar, currentLev)
1454  *
1455  *
1456  *  Description:
1457  *  -----------
1458  *  Returns a new level value if this is a new nesting level of quoted string
1459  *  Otherwise it returns a zero
1460  *
1461  *
1462  *  Inputs:
1463  *  ------
1464  *  currentChar = current position in the string
1465  *  currentLev = current level of nesting
1466  *
1467  * 
1468  *  Outputs:
1469  *  -------
1470  *  Returns either a new level of nesting or zero if the character is copied in
1471  *
1472  *
1473  *  Comments:
1474  *  --------
1475  * 
1476  *************************************<->***********************************/
1477 unsigned int PeekAhead(unsigned char *currentChar,
1478                        unsigned int currentLev)
1479
1480
1481 {
1482     Boolean             done = False;
1483     unsigned int        tmpLev = 1;
1484     unsigned int        chlen;
1485
1486     while (((chlen = mblen ((char *)currentChar, MB_CUR_MAX)) > 0) &&
1487            (chlen == 1) && ((*currentChar == '"') || (*currentChar == '\\'))
1488            && (done == False))
1489     {
1490         currentChar++;
1491
1492         if(((chlen = mblen ((char *)currentChar, MB_CUR_MAX)) > 0) && (chlen == 1) &&
1493            ((*currentChar == '"') || (*currentChar == '\\')))
1494         {
1495             tmpLev++;
1496             if(*currentChar == '"')
1497             {
1498                 done = True;
1499             }
1500             else
1501             {
1502                 currentChar++;
1503             }
1504         }
1505     }
1506
1507     /*
1508      * Figure out if this is truly a new level of nesting - else ignore it
1509      * This section probably could do some error checking and return -1
1510          * If so, change type of routine from unsigned int to int
1511      */
1512     if(done == True)
1513     {
1514         return(tmpLev);
1515     }
1516     else
1517     {
1518         return(0);
1519     }
1520 } /* END OF FUNCTION PeekAhead */
1521
1522
1523 /*************************************<->*************************************
1524  *
1525  *  SyncModifierStrings (fileName)
1526  *
1527  *  Description:
1528  *  -----------
1529  *  This function updates modifierStrings table so that Mwm uses the correct
1530  *  modifier to keysym mapping.  Specifically, fix up the Alt and Meta bindings.
1531  *
1532  *  Inputs:
1533  *  ------
1534  *  fileName 
1535  *
1536  *  Outputs:
1537  *  -------
1538  *  fileName
1539  *
1540  *  Comments:
1541  *  --------
1542  * 
1543  *************************************<->***********************************/
1544 void SyncModifierStrings(void)
1545 {
1546     XModifierKeymap *map;
1547     int i, j, k = 0;
1548
1549     map = XGetModifierMapping (DISPLAY);
1550
1551     for (i = 0; i < 8; i++)
1552     {
1553         for (j = 0; j < map->max_keypermod; j++)
1554         {
1555             if (map->modifiermap[k])
1556             {
1557                 KeySym ks = XKeycodeToKeysym(DISPLAY, map->modifiermap[k], 0);
1558                 char *nm = XKeysymToString(ks);
1559
1560                 /* Compare, ignoring the trailing '_L' or '_R' in keysym */
1561                 if (nm && !strncmp("Alt", nm, 3))
1562                 {
1563                     modifierStrings[ALT_INDEX].mask = (1<<i);
1564                 }
1565                 else if (nm && !strncmp("Meta", nm, 4))
1566                 {
1567                     modifierStrings[META_INDEX].mask = (1<<i);
1568                 }
1569             }
1570             k++;
1571         }
1572     }
1573
1574     XFreeModifiermap(map);
1575 }
1576
1577
1578 \f
1579 /*************************************<->*************************************
1580  *
1581  *  ProcessWmFile ()
1582  *
1583  *
1584  *  Description:
1585  *  -----------
1586  *  This function reads the mwm resource description file and processes the
1587  *  resources that are described.
1588  *
1589  *
1590  *  Inputs:
1591  *  ------
1592  *  wmGD.bitmapDirectory = bitmapDirectory resource value
1593  *  pSD->buttonBindings = buttonBindings resource value
1594  *  wmGD.configFile = configuration file resource value
1595  *  pSD->keyBindings = keyBindings resource value
1596  *  wmGD.rootWindow = default root window of display
1597  *  HOME = environment variable for home directory
1598  *  functionTable = window manager function parse table
1599  *
1600  * 
1601  *  Outputs:
1602  *  -------
1603  *  wmGD.buttonSpecs = list of button binding specifications
1604  *  wmGD.keySpecs = list of key binding specification
1605  *  wmGD.menuSpecs = list of menu specifications
1606  * *wmGD.acceleratorMenuSpecs = initialized array of (MenuSpec *)
1607  *  wmGD.acceleratorMenuCount = 0
1608  *
1609  *
1610  *  Comments:
1611  *  --------
1612  * If there are more than MAXLINE characters on a line the excess characters
1613  * are truncated.
1614  * 
1615  *************************************<->***********************************/
1616 #define MENU_SPEC       "menu"
1617 #define BUTTON_SPEC     "buttons"
1618 #define KEY_SPEC        "keys"
1619 #define FRONT_PANEL_SPEC DTWM_FP_PANEL_OLD
1620 #define DROP_EFFECTS_SPEC   DTWM_FP_DROP_EFFECTS
1621 #define PANEL_SPEC      DTWM_FP_PANEL
1622 #define BOX_SPEC        DTWM_FP_BOX
1623 #define CONTROL_SPEC    DTWM_FP_CONTROL
1624 #define INCLUDE_SPEC    DTWM_FP_INCLUDE
1625 #define ANIMATION_SPEC  DTWM_FP_ANIMATION
1626 #define SWITCH_SPEC     DTWM_FP_SWITCH
1627
1628 void ProcessWmFile (WmScreenData *pSD, Boolean bNested)
1629
1630 {
1631     unsigned char *lineP;
1632     unsigned char *string;
1633     unsigned int   n;
1634     MenuSpec      *menuSpec;
1635
1636     if (!bNested)
1637     {
1638
1639     /*
1640      * Initialize global data values that are set based on data in
1641      * the mwm resource description file.
1642      */
1643
1644     pSD->buttonSpecs = NULL;
1645     pSD->keySpecs = NULL;
1646     pSD->menuSpecs = NULL;
1647
1648     /**** hhhhhhhhhhhh   ******/
1649     GetFunctionTableValues (&F_EXEC_INDEX, &F_NOP_INDEX, &F_ACTION_INDEX);
1650     /*
1651      * Find and parse the default system menu string, if it exists.
1652      */
1653
1654     cfileP = NULL;
1655     linec = 0;
1656     if (((parseP = (unsigned char *) builtinSystemMenu) != NULL) && 
1657          (GetNextLine () != NULL))
1658     {
1659         lineP = line;
1660         ParseMenuSet (pSD, lineP);
1661     }
1662
1663     linec = 0;
1664     if (((parseP = (unsigned char *) builtinRootMenu) != NULL) && 
1665          (GetNextLine () != NULL))
1666     {
1667         lineP = line;
1668         ParseMenuSet (pSD, lineP);
1669     }
1670     if (wmGD.useFrontPanel &&  !wmGD.dtSD &&
1671         (XDefaultScreen (wmGD.display) == pSD->screen))
1672     {
1673         wmGD.dtSD = pSD;  /* only one per display */
1674     }
1675
1676     /*
1677      * Find and associate a stream with the window manager resource 
1678      *   description file.
1679      */
1680
1681     if ((cfileP = FopenConfigFile ()) == NULL)
1682     {
1683         if (!wmGD.useStandardBehavior)
1684           Warning (((char *)GETMESSAGE(60, 6, "Cannot open configuration file")));
1685         return;
1686     }
1687
1688     }  /* end if (!bNested) */
1689     /*
1690      * Parse the information in the configuration file.
1691      * If there are more than MAXLINE characters on a line the excess are
1692      *   truncated.
1693      */
1694
1695     linec = 0;
1696     while ((GetNextLine () != NULL))  /* not EOF nor read error */
1697     {
1698         lineP = line;
1699         if ((*line == '!') || (string = GetString (&lineP)) == NULL)
1700         /* empty or comment line */
1701         {
1702             continue;
1703         }
1704
1705         ToLower ((char *)string);
1706         if (!strcmp ((char *)string, MENU_SPEC))
1707         {
1708             ParseMenuSet (pSD, lineP);
1709         }
1710         else if (!strcmp ((char *) string, BUTTON_SPEC))
1711         {
1712             ParseButtonSet (pSD, lineP);
1713         }
1714         else if (!strcmp ((char *) string, KEY_SPEC))
1715         {
1716             ParseKeySet (pSD, lineP);
1717         }
1718         else if (!strcmp ((char *)string, INCLUDE_SPEC))
1719         {
1720             ParseIncludeSet (pSD, lineP);
1721         }
1722     }
1723
1724     fclose (cfileP);
1725
1726     /*
1727      * Create and initialize the pSD->acceleratorMenuSpecs array.
1728      * This assumes we create pointers to MenuSpecs within ProcessWmFile().
1729      * Set pSD->acceleratorMenuCount to 0.
1730      */
1731
1732     /* count the number of menu specifications */
1733     n = 0;
1734     menuSpec = pSD->menuSpecs;
1735     while (menuSpec)
1736     {
1737         n++;
1738         menuSpec = menuSpec->nextMenuSpec;
1739     }
1740
1741     /* allocate the array and initialize to zeros */
1742     pSD->acceleratorMenuSpecs = NULL;
1743     if (n)
1744     {
1745         pSD->acceleratorMenuSpecs = 
1746             (MenuSpec **) XtCalloc (n, sizeof (MenuSpec *));
1747         if (pSD->acceleratorMenuSpecs == NULL)
1748         {
1749             Warning (((char *)GETMESSAGE(60, 7, "Insufficient memory for menu accelerators")));
1750         }
1751     }
1752     pSD->acceleratorMenuCount = 0;
1753 } /* END OF FUNCTION ProcessWmFile */
1754
1755 /**** This function stolen from Xt/Intrinsic.c ****/
1756 /* The implementation of this routine is operating system dependent */
1757
1758 static char *ExtractLocaleName(String lang)
1759 {
1760
1761 #ifdef hpux      /* hpux-specific parsing of the locale string */
1762 #define MAXLOCALE       64      /* buffer size of locale name */
1763
1764     char           *start;
1765     char           *end;
1766     int             len;
1767     static char     buf[MAXLOCALE];
1768
1769     /*  If lang has a substring ":<category>;", extract <category>
1770      *  from the first such occurrence as the locale name.
1771      */
1772
1773     start = lang;
1774     if (start = strchr (lang, ':')) {
1775         start++;
1776         if (end = strchr (start, ';')) {
1777             len = end - start;
1778             strncpy(buf, start, len);
1779             *(buf + len) = '\0';
1780             lang = buf;
1781       }
1782     }
1783 #endif  /* hpux */
1784
1785     return lang;
1786 }
1787
1788 #define RC_CONFIG_SUBDIR                "/config/"
1789 #define RC_DEFAULT_CONFIG_SUBDIR        "/config/C"
1790
1791 /*************************************<->*************************************
1792  *
1793  *  FopenConfigFile ()
1794  *
1795  *
1796  *  Description:
1797  *  -----------
1798  *  This function searches for, opens, and associates a stream with the mwm 
1799  *  resource description file,
1800  *
1801  *
1802  *  Inputs:
1803  *  ------
1804  *  wmGD.configFile = configuration file resource value.
1805  *  HOME = environment variable for home directory
1806  *
1807  *
1808  *  Outputs:
1809  *  -------
1810  *  Return = If successful, a pointer to the FILE structure associated with 
1811  *           the configuration file.  Otherwise, NULL.
1812  *
1813  *
1814  *  Comments:
1815  *  --------
1816  *  None.
1817  * 
1818  *************************************<->***********************************/
1819 FILE *FopenConfigFile (void)
1820 {
1821
1822     char    *LANG, *LANGp;
1823     FILE    *fileP;
1824
1825     char *homeDir = XmeGetHomeDirName();
1826     Boolean stackPushed;
1827
1828     /*
1829      * Get the LANG environment variable
1830      * make copy since another call to getenv will blast the value.
1831      */
1832     LANGp = setlocale(LC_CTYPE, NULL);
1833
1834     /*
1835      * setlocale not guaranteed to return $LANG -- extract
1836      * something usable.
1837      */
1838     LANGp = ExtractLocaleName (LANGp);
1839
1840     if ((LANGp == NULL) || (strlen(LANGp) == 0))
1841       {
1842          LANG = NULL;
1843       }
1844     else
1845       {
1846          if ((LANG = (char *) XtMalloc(strlen(LANGp) +1)) == NULL)
1847            {
1848               PWarning (((char *)GETMESSAGE(60, 41, "Insufficient memory to get LANG environment variable.")));
1849               return(NULL);
1850            }
1851
1852          strcpy(LANG, LANGp);
1853       }
1854
1855
1856     /*
1857      * To get a name for the file first look at the value of the configFile
1858      * resource.  Interpret "~/.." as relative to the user's home directory.
1859      * Use the LANG variable if set and .mwmrc is in $HOME/$LANG/.mwmrc  
1860      */
1861
1862     if (pConfigStackTop && pConfigStackTop->tempName)
1863     {
1864         fileP = fopen (pConfigStackTop->tempName, "r");
1865         return (fileP);
1866     }
1867     stackPushed = (pConfigStackTop && (pConfigStackTop != pConfigStack));
1868     fileP = NULL;
1869     cfileName[0] = '\0';
1870
1871     if ((wmGD.configFile != NULL) && (wmGD.configFile[0] != '\0'))
1872     /* pointer to nonNULL string */
1873     {
1874         if ((wmGD.configFile[0] == '~') && (wmGD.configFile[1] == '/'))
1875         /* handle "~/..." */
1876         {
1877             strcpy (cfileName, homeDir);
1878             if (LANG != NULL)
1879             {
1880                 strncat(cfileName, "/", MAXWMPATH-strlen(cfileName));
1881                 strncat(cfileName, LANG, MAXWMPATH-strlen(cfileName));
1882             }
1883             strncat(cfileName, &(wmGD.configFile[1]), MAXWMPATH-strlen(cfileName));
1884             if ((fileP = fopen (cfileName, "r")) != NULL)
1885             {
1886                 if (LANG != NULL) { 
1887                     XtFree(LANG); 
1888                     LANG = NULL; 
1889                 }
1890             }
1891             else
1892             {
1893                 /* 
1894                  * Just try $HOME/.mwmrc
1895                  */
1896                 strcpy (cfileName, homeDir);
1897                 strncat(cfileName, &(wmGD.configFile[1]), 
1898                         MAXWMPATH-strlen(cfileName));
1899                 if ((fileP = fopen (cfileName, "r")) != NULL)
1900                 {
1901                   if (LANG != NULL) {
1902                       XtFree(LANG);
1903                       LANG = NULL;
1904                   }
1905                 }
1906             }
1907
1908             
1909         }
1910         else
1911         /* relative to current directory or absolute */
1912         {
1913             char *pch;
1914
1915             pch = (char *) GetNetworkFileName (wmGD.configFile);
1916
1917             if ((fileP = fopen (pch, "r")) != NULL)
1918             {
1919                 strncpy (cfileName, pch, MAXWMPATH);
1920             }
1921             XtFree (pch);
1922   
1923           if ((fileP == NULL) && !stackPushed)
1924           {
1925             if ((fileP = fopen (wmGD.configFile, "r")) != NULL)
1926               {
1927                 if (LANG != NULL) {
1928                     XtFree(LANG);
1929                     LANG = NULL;
1930                 }
1931                 return(fileP);
1932               }
1933           }
1934           else if ((fileP == NULL) && stackPushed)
1935           {
1936                 strcpy (cfileName, wmGD.configFile);
1937           }
1938         }
1939     }
1940
1941   if ((fileP == NULL) && !stackPushed)
1942   {
1943
1944     /*
1945      * The configFile resource didn't do it for us.
1946      * First try HOME_MWMRC, then try SYS_MWMRC .
1947      */
1948
1949 #define HOME_MWMRC "/.mwmrc"
1950 #define SLASH_MWMRC "/system.mwmrc"
1951
1952     strcpy (cfileName, homeDir);
1953
1954     if (MwmBehavior)
1955     {
1956         /*
1957          *
1958          *  Looking for $HOME/$LANG/.mwmrc
1959          *  --or--if $LANG is NULL
1960          *  Looking for $HOME/.mwmrc
1961          *
1962          */
1963         if (LANG != NULL)
1964         {
1965             strncat(cfileName, "/", MAXWMPATH-strlen(cfileName));
1966             strncat(cfileName, LANG, MAXWMPATH-strlen(cfileName));
1967         }
1968         strncat(cfileName, HOME_MWMRC, MAXWMPATH - strlen(cfileName));
1969     }
1970     else
1971     {
1972         /*
1973          *
1974          *  Looking for $HOME/.dt/$LANG/dtwmrc
1975          *
1976          *  --or--if $LANG is NULL--
1977          *
1978          *  Looking for $HOME/.dt/dtwmrc
1979          *
1980          */
1981         strncat(cfileName, "/.dt/", MAXWMPATH-strlen(cfileName));
1982
1983         if (LANG != NULL)
1984         {
1985             strncat(cfileName, LANG, MAXWMPATH-strlen(cfileName));
1986         }
1987         strncat(cfileName, LANG_DT_WMRC, MAXWMPATH - strlen(cfileName));
1988     }
1989     if ((fileP = fopen (cfileName, "r")) != NULL)
1990     {
1991         if (LANG != NULL) {
1992             XtFree(LANG);
1993             LANG = NULL;
1994         }
1995     }
1996     else
1997     {
1998         /* 
1999          * Just try $HOME/.mwmrc
2000          */
2001         strcpy (cfileName, homeDir);
2002         if (MwmBehavior)
2003         {
2004             /* 
2005              * Just try $HOME/.mwmrc
2006              */
2007             strncat(cfileName, HOME_MWMRC, MAXWMPATH - strlen(cfileName));
2008         }
2009         else
2010         {
2011             /* 
2012              * Just try $HOME/.dt/dtwmrc
2013              */
2014             strncat(cfileName, HOME_DT_WMRC, MAXWMPATH - strlen(cfileName));
2015         }
2016         if ((fileP = fopen (cfileName, "r")) != NULL)
2017         {
2018           if (LANG != NULL) {
2019               XtFree(LANG);
2020               LANG = NULL;
2021           }
2022         }
2023     }
2024   }
2025
2026 #define DTLIBDIR  CDE_INSTALLATION_TOP
2027 #define DTADMINDIR  CDE_CONFIGURATION_TOP
2028 #define SLASH_DT_WMRC "/sys.dtwmrc"
2029
2030   if ((fileP == NULL) && !stackPushed)
2031   {
2032     /* 
2033      * No home-based config file. Try the admin directory.
2034      */
2035     strcpy(cfileName, DTADMINDIR);
2036     strncat(cfileName, RC_CONFIG_SUBDIR, MAXWMPATH-strlen(cfileName));
2037     strncat(cfileName, LANG, MAXWMPATH-strlen(cfileName));
2038     strncat(cfileName, SLASH_DT_WMRC, MAXWMPATH - strlen(cfileName));
2039
2040     if (((fileP = fopen (cfileName, "r")) == NULL) && LANG && *LANG)
2041     {
2042         /* Try it with no LANG */
2043         strcpy(cfileName, DTADMINDIR);
2044         strncat(cfileName, RC_CONFIG_SUBDIR, MAXWMPATH-strlen(cfileName));
2045         strncat(cfileName, SLASH_DT_WMRC, MAXWMPATH - strlen(cfileName));
2046     }
2047
2048     if ((fileP = fopen (cfileName, "r")) != NULL)
2049     {
2050       XtFree(LANG);
2051       LANG = NULL;
2052     }
2053   }
2054
2055   if ((fileP == NULL) && !stackPushed)
2056   {
2057
2058 #ifndef LIBDIR
2059 #define LIBDIR "/usr/lib/X11"
2060 #endif
2061     if (LANG != NULL)
2062     {
2063         if (MwmBehavior)
2064         {
2065             strcpy(cfileName, LIBDIR);
2066             strncat(cfileName, "/", MAXWMPATH-strlen(cfileName));
2067             strncat(cfileName, LANG, MAXWMPATH-strlen(cfileName));
2068             strncat(cfileName, SLASH_MWMRC, MAXWMPATH - strlen(cfileName));
2069         }
2070         else
2071         {
2072             strcpy(cfileName, DTLIBDIR);
2073             strncat(cfileName, RC_CONFIG_SUBDIR, MAXWMPATH-strlen(cfileName));
2074             strncat(cfileName, LANG, MAXWMPATH-strlen(cfileName));
2075             strncat(cfileName, SLASH_DT_WMRC, MAXWMPATH - strlen(cfileName));
2076         }
2077         if ((fileP = fopen (cfileName, "r")) != NULL)
2078         {
2079           XtFree(LANG);
2080           LANG = NULL;
2081         }
2082     }
2083
2084     if ((fileP == NULL) && !stackPushed)
2085     {
2086     if (MwmBehavior)
2087     {
2088         strcpy(cfileName, LIBDIR);
2089         strncat(cfileName, SLASH_MWMRC, MAXWMPATH - strlen(cfileName));
2090         fileP = fopen (cfileName, "r");
2091     }
2092     else
2093     {
2094         strcpy(cfileName, DTLIBDIR);
2095         strncat(cfileName, RC_DEFAULT_CONFIG_SUBDIR, 
2096                                         MAXWMPATH - strlen(cfileName));
2097         strncat(cfileName, SLASH_DT_WMRC, MAXWMPATH - strlen(cfileName));
2098         fileP = fopen (cfileName, "r");
2099     }
2100     }
2101   }
2102
2103     if (!fileP)
2104     {
2105         char *pch;
2106
2107         /*
2108          * handle "<host>:<path>" form of file name
2109          */
2110         pch = (char *) GetNetworkFileName (cfileName);
2111         if ((fileP = fopen (cfileName, "r")) != NULL)
2112         {
2113             strncpy (cfileName, pch, MAXWMPATH);
2114             XtFree (pch);
2115         }
2116
2117         /*
2118          * Either not "<host>:<path>" form or there was a
2119          * problem up above. This is the last attempt to 
2120          * open something.
2121          */
2122         if (!fileP)
2123         {
2124             fileP = fopen (cfileName, "r");
2125         }
2126     }
2127
2128     if (!pConfigStack)
2129     {
2130         ConfigStackInit (cfileName);
2131     }
2132
2133     if (wmGD.cppCommand && *wmGD.cppCommand)
2134     {
2135         /*
2136          *  Run the file through the C-preprocessor
2137          */
2138         PreprocessConfigFile ();
2139         if (pConfigStackTop && pConfigStackTop->cppName)
2140         {
2141             if(fileP) {
2142                 fclose(fileP);
2143             }
2144             /* open the result */
2145             fileP = fopen (pConfigStackTop->cppName, "r");
2146         }
2147     }
2148
2149     if (LANG != NULL) 
2150     {
2151         XtFree(LANG);
2152         LANG = NULL;
2153     }
2154     return (fileP);
2155
2156 } /* END OF FUNCTION FopenConfigFile */
2157
2158 \f
2159 /*************************************<->*************************************
2160  *
2161  *  SaveMenuAccelerators (pSD, newMenuSpec)
2162  *
2163  *
2164  *  Description:
2165  *  -----------
2166  *  This function saves the MenuSpec pointer in pSD->acceleratorMenuSpecs.
2167  *
2168  *
2169  *  Inputs:
2170  *  ------
2171  *  newMenuSpec = pointer to MenuSpec to be saved.
2172  *  pSD->acceleratorMenuSpecs = 
2173  *  pSD->acceleratorMenuCount = 
2174  *
2175  *
2176  *  Outputs:
2177  *  -------
2178  *  pSD->acceleratorMenuSpecs = possibly updated
2179  *  pSD->acceleratorMenuCount = possibly updated
2180  *
2181  *
2182  *  Comments:
2183  *  --------
2184  *  We assume only MenuSpecs created within ProcessWmFile() are to be saved.
2185  *  Otherwise, we may cause override the limits of pSD->acceleratorMenuSpecs.
2186  * 
2187  *************************************<->***********************************/
2188
2189 void SaveMenuAccelerators (WmScreenData *pSD, MenuSpec *newMenuSpec)
2190 {
2191     MenuSpec  **pMenuSpec;
2192
2193     pMenuSpec = pSD->acceleratorMenuSpecs;
2194
2195     if (pMenuSpec == NULL) 
2196         return;
2197
2198     while ((*pMenuSpec != NULL) && (*pMenuSpec != newMenuSpec))
2199     {
2200         pMenuSpec++;
2201     }
2202
2203     if (*pMenuSpec == NULL)
2204     {
2205         *pMenuSpec = newMenuSpec;
2206         pSD->acceleratorMenuCount++;
2207     }
2208
2209 } /* END OF FUNCTION SaveMenuAccelerators */
2210
2211 \f
2212 /*************************************<->*************************************
2213  *
2214  *  ParseMenuSet (pSD, lineP)
2215  *
2216  *
2217  *  Description:
2218  *  -----------
2219  *  Menu pane specification found.  Parse the following syntax:
2220  *
2221  *          v
2222  *     Menu menu_pane_name
2223  *     {
2224  *       label  [mnemonic]  [accelerator]  function
2225  *       label  [mnemonic]  [accelerator]  function
2226  *                 ...
2227  *       label  [mnemonic]  [accelerator]  function
2228  *     }
2229  *
2230  *
2231  *  Inputs:
2232  *  ------
2233  *  cfileP = (global) file pointer to fopened configuration file or NULL
2234  *  lineP = pointer to menu name in line buffer
2235  *  line   = (global) line buffer
2236  *  linec  = (global) line count
2237  *  parseP = (global) parse string pointer if cfileP == NULL
2238  *  pSD->rootWindow = default root window of display
2239  *  wmGD.bitmapDirectory = bitmapDirectory resource value
2240  *  HOME = environment variable for home directory
2241  *
2242  * 
2243  *  Outputs:
2244  *  -------
2245  *  linec  = (global) line count incremented
2246  *  parseP = (global) parse string pointer if cfileP == NULL
2247  *  pSD->menuSpecs = list of menu specifications
2248  *
2249  *
2250  *  Comments:
2251  *  --------
2252  *  Skips unnamed menu specifications.
2253  *  This means custom menu specifications can be distinguished by NULL name.
2254  * 
2255  *************************************<->***********************************/
2256
2257 static void ParseMenuSet (WmScreenData *pSD, unsigned char *lineP)
2258 {
2259     unsigned char     *string;
2260     MenuSpec *menuSpec;
2261     
2262     /*
2263      * If menu name is NULL then skip this pane specification.
2264      */
2265
2266     if ((string = GetString (&lineP)) == NULL)
2267     {
2268         return;
2269     }
2270
2271     /*
2272      * Allocate space for the menu specification structure.
2273      */
2274
2275     if ((menuSpec = (MenuSpec *)XtMalloc (sizeof (MenuSpec))) == NULL)
2276     {
2277         PWarning (((char *)GETMESSAGE(60, 9, "Insufficient memory for menu")));
2278         return;
2279     }
2280     menuSpec->currentContext = 0;
2281     menuSpec->menuWidget = NULL;
2282     menuSpec->whichButton = SELECT_BUTTON;  /* Button1 selection default */
2283     menuSpec->menuItems = NULL;
2284     menuSpec->accelContext = 0;
2285     menuSpec->accelKeySpecs = NULL;
2286     menuSpec->nextMenuSpec = NULL;
2287
2288     /*
2289      * Allocate and fill space for the menu name.
2290      */
2291
2292     if ((menuSpec->name = 
2293          (String)XtMalloc ((unsigned int) (strlen ((char *)string) + 1))) 
2294          == NULL)
2295     {
2296         PWarning (((char *)GETMESSAGE(60, 10, "Insufficient memory for menu")));
2297         XtFree ((char *)menuSpec);
2298         return;
2299     }
2300     strcpy (menuSpec->name, (char *)string);
2301
2302     /* 
2303      * Add the empty structure to the head of the menu specification list.
2304      */
2305
2306     menuSpec->nextMenuSpec = pSD->menuSpecs;
2307     pSD->menuSpecs = menuSpec;
2308
2309     /*
2310      * Require leading '{' on the next line.
2311      */
2312
2313     while ((GetNextLine () != NULL))  /* not EOF nor read error */
2314     {
2315         lineP = line;
2316         ScanWhitespace(&lineP);
2317
2318         if ((lineP == NULL) || (*line == '!') || (*lineP == '\0') || (*lineP == '#'))
2319         /* ignore empty or comment line */
2320         {
2321             continue;
2322         }
2323
2324         if (*lineP == '{')
2325         /* found '{' */
2326         {
2327             break;
2328         }
2329
2330         /* not a '{' */
2331         PWarning (((char *)GETMESSAGE(60, 11, "Expected '{' after menu name")));
2332         return;
2333     }
2334
2335     /*
2336      * Found leading "{" or EOF.
2337      * Parse menu item specifications until "}" or EOF found.
2338      */
2339
2340     menuSpec->menuItems = PARSE_MENU_ITEMS (pSD, menuSpec);
2341
2342 } /* END OF FUNCTION ParseMenuSet */
2343
2344 \f
2345 /*************************************<->*************************************
2346  *
2347  *  MenuItem *
2348  *  ParseMwmMenuStr (pSD, menuStr)
2349  *
2350  *
2351  *  Description:
2352  *  -----------
2353  *  This function parses a WMW_MENU string and returns a list of 
2354  *  MenuItems structures.  The string should have the syntax:
2355  *
2356  *       label  [mnemonic]  [accelerator]  function
2357  *       label  [mnemonic]  [accelerator]  function
2358  *                 ...
2359  *       label  [mnemonic]  [accelerator]  function
2360  *
2361  *
2362  *  Inputs:
2363  *  ------
2364  *  line = (global) line buffer
2365  *  pSD->rootWindow = default root window of display
2366  *  wmGD.bitmapDirectory = bitmapDirectory resource value
2367  *  HOME = environment variable for home directory
2368  *  functionTable = window manager function parse table
2369  *
2370  * 
2371  *  Outputs:
2372  *  -------
2373  *  Return = list of MenuItem structures or NULL
2374  *
2375  *
2376  *  Comments:
2377  *  --------
2378  *  None.
2379  * 
2380  *************************************<->***********************************/
2381
2382 MenuItem *ParseMwmMenuStr (WmScreenData *pSD, unsigned char *menuStr)
2383 {
2384
2385     cfileP = NULL;
2386     linec = 0;
2387     parseP = menuStr;
2388
2389     return (PARSE_MENU_ITEMS (pSD, NULL));
2390
2391 } /* END OF FUNCTION ParseMwmMenuStr */
2392
2393 \f
2394 /*************************************<->*************************************
2395  *
2396  *  static MenuItem *
2397  *  ParseMenuItems (pSD, menuSpec)
2398  *
2399  *
2400  *  Description:
2401  *  -----------
2402  *  Parse menu item specifications:
2403  *
2404  *       label  [mnemonic]  [accelerator]  function
2405  *       label  [mnemonic]  [accelerator]  function
2406  *                 ...
2407  *       label  [mnemonic]  [accelerator]  function
2408  *     [}]
2409  *
2410  *
2411  *  Inputs:
2412  *  ------
2413  *  pSD    = pointer to screen data
2414  *  cfileP = (global) file pointer to fopened configuration file or NULL
2415  *  line   = (global) line buffer
2416  *  linec  = (global) line count
2417  *  parseP = (global) parse string pointer if cfileP == NULL
2418  *  pSD->rootWindow = default root window of display
2419  *  wmGD.bitmapDirectory = bitmapDirectory resource value
2420  *  HOME = environment variable for home directory
2421  *
2422  * 
2423  *  Outputs:
2424  *  -------
2425  *  linec  = (global) line count incremented
2426  *  parseP = (global) parse string pointer if cfileP == NULL
2427  *  Return = list of MenuItem structures or NULL
2428  *
2429  *
2430  *  Comments:
2431  *  --------
2432  *  None.
2433  * 
2434  *************************************<->***********************************/
2435
2436 static MenuItem *ParseMenuItems (WmScreenData *pSD)
2437 {
2438     unsigned char *string;
2439     unsigned char *lineP;
2440     MenuItem      *firstMenuItem;
2441     MenuItem      *lastMenuItem;
2442     MenuItem      *menuItem;
2443     int   ix;
2444     
2445     /*
2446      * Parse "label [mnemonic] [accelerator] function" or
2447      *       "<client command>[.<client command>]*"
2448      * lines until "}" or EOF found.
2449      */
2450
2451     firstMenuItem = lastMenuItem = NULL;
2452     while ((GetNextLine () != NULL))
2453     {
2454         lineP = line;
2455         if ((*line == '!') || (*line == '#') || (string = GetString (&lineP)) == NULL)
2456         /* ignore empty or comment lines */
2457         {
2458             continue;
2459         }
2460         if (*string == '}')  /* finished with menu set. */
2461         {
2462             break;
2463         }
2464
2465         /*
2466          * Allocate space for the menu item structure. 
2467          */
2468
2469         if ((menuItem = (MenuItem *)XtMalloc (sizeof (MenuItem))) == NULL)
2470         {
2471             PWarning (((char *)GETMESSAGE(60, 12, "Insufficient memory for menu item")));
2472             continue;
2473         }
2474         menuItem->nextMenuItem = NULL;
2475         menuItem->wmFunction = (WmFunction)NULL;
2476         menuItem->wmFuncArgs = NULL;
2477
2478         {
2479             /*
2480              * Parse the menu item label.
2481              */
2482             if (!ParseWmLabel (pSD, menuItem, string))
2483             {
2484                 XtFree ((char *)menuItem);
2485                 continue;
2486             }
2487         }
2488
2489         /*
2490          * Parse any menu function mnemonic.
2491          */
2492
2493         ParseWmMnemonic (&lineP, menuItem);
2494
2495         /*
2496          * Parse any menu function accelerator.
2497          */
2498
2499         if (!ParseWmAccelerator (&lineP, menuItem))
2500         {
2501             XtFree ((char *)menuItem);
2502             continue;
2503         }
2504         /*
2505          * Parse the menu function name if this is not a client
2506          * command. If it is a client command, then the wmFunction
2507          * field should already be set, as well as the ix variable,
2508          * but we do want to search for a menu item name that occupies
2509          * the same place as the function does for normal menu items.
2510          */
2511         ix = ParseWmFunction (&lineP, CRS_MENU, &menuItem->wmFunction);
2512
2513         /*
2514          * Determine context sensitivity and applicability mask.
2515          */
2516
2517         menuItem->greyedContext = functionTable[ix].greyedContext;
2518         menuItem->mgtMask = functionTable[ix].mgtMask;
2519         if ((menuItem->wmFunction == F_Toggle_Front_Panel) &&
2520             ((wmGD.useFrontPanel == False) ||
2521              (wmGD.dtSD != pSD)))
2522         {
2523             /*
2524              * disallow this function if there's no front
2525              * panel on this screen.
2526              */
2527             menuItem->greyedContext |= (F_CONTEXT_ALL           | 
2528                                        F_SUBCONTEXT_IB_WICON    | 
2529                                        F_SUBCONTEXT_IB_IICON);
2530         }
2531
2532         /* 
2533          * Apply the function argument parser.
2534          */
2535         if (!(*(functionTable [ix].parseProc)) 
2536                    (&lineP, menuItem->wmFunction, &menuItem->wmFuncArgs))
2537         {
2538             FreeMenuItem (menuItem);
2539             continue;  /* skip this menu item */
2540         }
2541
2542         /*
2543          * Add this item to the menu specification.
2544          */
2545
2546         if (lastMenuItem != NULL)  /* not first */
2547         {
2548             lastMenuItem->nextMenuItem = menuItem;
2549         }
2550         else
2551         {
2552             firstMenuItem = menuItem;
2553         }
2554         lastMenuItem = menuItem;
2555     }
2556
2557     return (firstMenuItem);
2558
2559 } /* END OF FUNCTION ParseMenuItems */
2560
2561
2562 /*************************************<->*************************************
2563  *
2564  *  ParseWmLabel (pSD, menuItem, string)
2565  *
2566  *
2567  *  Description:
2568  *  -----------
2569  *  Parse a menu label string.
2570  *
2571  *
2572  *  Inputs:
2573  *  ------
2574  *  pSD      = pointer to screen data
2575  *  menuItem = pointer to MenuItem structure
2576  *  string   = label string
2577  *
2578  * 
2579  *  Outputs:
2580  *  -------
2581  *  menuItem->label
2582  *  menuItem->labelType
2583  *  menuItem->labelBitmapCache
2584  *  Return   = boolean, FALSE iff insufficient memory
2585  *
2586  *
2587  *  Comments:
2588  *  --------
2589  * We have two label types:  XmSTRING and XmPIXMAP
2590  * We allocate and fill the label field with string and set the type to 
2591  * XmSTRING.  If string = "@<bitmap_file>", and <bitmap_file> contains a
2592  * with which to build a label image we save the bitmap in the MenuItem 
2593  * structure and set the type to XmPIXMAP.
2594  * 
2595  *************************************<->***********************************/
2596
2597 static Boolean ParseWmLabel (WmScreenData *pSD, MenuItem *menuItem, 
2598                              unsigned char *string)
2599 {
2600
2601     /*
2602      * Allocate the label field and copy string.
2603      */
2604
2605     if ((menuItem->label = (String)
2606         XtMalloc ((unsigned int)(strlen ((char *)string) + 1))) == NULL)
2607     {
2608         PWarning (((char *)GETMESSAGE(60, 13, "Insufficient memory for menu item")));
2609         return (FALSE);
2610     }
2611
2612     strcpy (menuItem->label, (char *)string);
2613     menuItem->labelType = XmSTRING;
2614
2615     if (*string == '@')
2616     /*
2617      * Here:  string  = "@<bitmap file>"
2618      * Try to find the label bitmap in the bitmap cache or read the label 
2619      * bitmap file.
2620      */
2621     {
2622         string++;  /* skip "@" */
2623         if ((menuItem->labelBitmapIndex = GetBitmapIndex (pSD, 
2624                                            (char *)string, True)) >= 0)
2625         {
2626             menuItem->labelType = XmPIXMAP;
2627         }
2628     }
2629     return (TRUE);
2630
2631 } /* END OF FUNCTION ParseWmLabel */
2632
2633
2634 \f
2635 /*************************************<->*************************************
2636  *
2637  *  ParseWmMnemonic (linePP, menuItem)
2638  *
2639  *
2640  *  Description:
2641  *  -----------
2642  *  Parse an optional menu function mnemonic.
2643  *
2644  *
2645  *  Inputs:
2646  *  ------
2647  *  linePP   = pointer to current line buffer pointer.
2648  *  menuItem = pointer to MenuItem structure
2649  *
2650  * 
2651  *  Outputs:
2652  *  -------
2653  *  linePP   = pointer to revised line buffer pointer.
2654  *  menuItem->mnemonic = valid mnemonic character or NULL.
2655  *
2656  *
2657  *  Comments:
2658  *  --------
2659  *  None.
2660  * 
2661  *************************************<->***********************************/
2662
2663 static void ParseWmMnemonic (unsigned char **linePP, MenuItem *menuItem)
2664 {
2665     unsigned char *lineP = *linePP;
2666     unsigned char *mnemonic;
2667
2668     /*
2669      * Skip leading white space.
2670      */
2671     ScanWhitespace (&lineP);
2672     menuItem->mnemonic = (KeySym)NULL;
2673
2674     if (*lineP == '_')
2675     /* 
2676      * We have a mnemonic specification. 
2677      * Get the next string (we only use the first character).
2678      * If no string exists, the labelType is not XmSTRING, or does not contain 
2679      * the first character, then skip the string and return.
2680      * Otherwise, accept the first character as a mnemonic.
2681      */
2682     {
2683         KeySym ks;
2684         lineP++;
2685         mnemonic = GetString(&lineP);
2686
2687         if (menuItem->labelType == XmSTRING &&
2688             mnemonic != NULL &&
2689             (ks = XStringToKeysym((char *)mnemonic)) != NoSymbol &&
2690             strchr(menuItem->label, (char)(ks & 0xff)) != NULL)
2691         {
2692             menuItem->mnemonic = ks;
2693         }
2694         else
2695         {
2696             PWarning (((char *)GETMESSAGE(60, 14, "Invalid mnemonic specification")));
2697         }
2698     }
2699
2700     *linePP = lineP;  /* consume any string */
2701
2702 } /* END OF FUNCTION ParseWmMnemonic */
2703
2704 \f
2705 /*************************************<->*************************************
2706  *
2707  *  ParseWmAccelerator (linePP, menuItem)
2708  *
2709  *
2710  *  Description:
2711  *  -----------
2712  *  Parse an optional menu function accelerator.
2713  *
2714  *
2715  *  Inputs:
2716  *  ------
2717  *  linePP   = pointer to current line buffer pointer.
2718  *  menuItem = pointer to MenuItem structure
2719  *
2720  * 
2721  *  Outputs:
2722  *  -------
2723  *  linePP   = pointer to revised line buffer pointer.
2724  *  menuItem->accelText = pointer to an accelerator string or NULL.
2725  *
2726  *
2727  *  Comments:
2728  *  --------
2729  *  None.
2730  * 
2731  *************************************<->***********************************/
2732
2733 static Boolean ParseWmAccelerator (unsigned char **linePP, MenuItem *menuItem)
2734 {
2735     unsigned char *lineP;
2736     String        string;
2737     unsigned int  eventType;
2738     unsigned int  state;
2739     KeyCode       keycode;
2740     Boolean       status;
2741
2742     menuItem->accelState = 0;
2743     menuItem->accelKeyCode = 0;
2744     menuItem->accelText = NULL;
2745     status = TRUE;
2746
2747     /*
2748      * If linePP contains NULL, then abort.
2749      */
2750     if (*linePP == (unsigned char *) NULL) return(FALSE);
2751
2752     /*
2753      * Skip leading white space.
2754      */
2755     ScanWhitespace (linePP);
2756     lineP = *linePP;
2757
2758     /*
2759      * If the second character is not ".", and an accelerator specification 
2760      * exists, then process and save the specification string.
2761      */
2762
2763     if ((*lineP != '\0') &&     /* something follows */
2764         (*lineP != '!')  &&     /* skip if we have the ! WmFunction */
2765         (*lineP != 'f')  &&
2766         (*(lineP+1) != '.'))    /* skip if we have f.xxx WmFunction */
2767     {
2768         if (ParseKeyEvent(&lineP, &eventType, &keycode, &state))
2769         {
2770             if ((string = (String) XtMalloc 
2771                  ((unsigned int) (lineP - *linePP + 1))) == NULL)
2772             {
2773                 PWarning (((char *)GETMESSAGE(60, 15, "Insufficient memory for accelerator specification")));
2774                 status = FALSE;
2775             }
2776             else
2777             /*
2778              * Save the accelerator state and keycode.
2779              * Process and save the accelerator text.
2780              */
2781             {
2782                 ProcessAccelText (*linePP, lineP, (unsigned char *) string);
2783                 menuItem->accelState = state;
2784                 menuItem->accelKeyCode = keycode;
2785                 menuItem->accelText = string;
2786             }
2787         }
2788         else
2789         {
2790             PWarning (((char *)GETMESSAGE(60, 16, "Invalid accelerator specification")));
2791             status = FALSE;
2792         }
2793
2794         *linePP = lineP;  /* consume the specification */
2795     }
2796
2797     return (status);
2798
2799 } /* END OF FUNCTION ParseWmAccelerator */
2800
2801 /*************************************<->*************************************
2802  *
2803  *  int
2804  *  ParseWmFunction (linePP, res_spec, pWmFunction)
2805  *
2806  *
2807  *  Description:
2808  *  -----------
2809  *  Parse a button, key, or menu function name and return its function table
2810  *  index.
2811  *
2812  *
2813  *  Inputs:
2814  *  ------
2815  *  linePP   = pointer to current line buffer pointer.
2816  *  res_spec = resource specification type (key, button, or menu).
2817  *  pWmFunction = pointer to window manager function destination.
2818  *  functionTable = window manager function parse table
2819  *
2820  * 
2821  *  Outputs:
2822  *  -------
2823  *  linePP   = pointer to revised line buffer pointer.
2824  *  pWmFunction = pointer to parsed window manager function.
2825  *  Return = function table index of parsed function.
2826  *
2827  *
2828  *  Comments:
2829  *  --------
2830  *  Uses F_Nop if the function name or resource type is invalid.
2831  * 
2832  *************************************<->***********************************/
2833
2834 int ParseWmFunction (unsigned char **linePP, unsigned int res_spec, 
2835                             WmFunction *pWmFunction)
2836 {
2837     unsigned char *lineP = *linePP;
2838     unsigned char *string;
2839     int  low, mid, high, cmp;
2840
2841     /*
2842      * Skip leading white space.
2843      */
2844     ScanWhitespace (&lineP);
2845
2846     /* 
2847      * Have function string (may be NULL or a comment).
2848      * Handle the special case of '!' 
2849      */
2850
2851     if (*lineP == '!')
2852     {
2853         *linePP = ++lineP;
2854         *pWmFunction = F_Exec;
2855         return (F_EXEC_INDEX);
2856     }
2857
2858     /*
2859      * Identify the function corresponding to the specified name.
2860      * Try binary search of the window manager function parse table.
2861      * Assume f.nop if the function and resource type cannot be matched.
2862      * This handles NULL and comment strings, bad function names, and functions
2863      *   in inappropriate resource sets.
2864      */
2865     string = GetString (&lineP);
2866     *linePP = lineP;
2867
2868     if (string != NULL)
2869     {
2870         ToLower ((char *)string);
2871         low = 0;
2872         high = WMFUNCTIONTABLESIZE - 1;
2873
2874         while (low <= high)
2875         {
2876             mid = (low + high)/2;
2877             cmp = strcmp (functionTable[mid].funcName, (char *)string);
2878
2879             if (!cmp)
2880             /*
2881              * Function name match 
2882              * Require proper resource type for the function.
2883              */
2884             {
2885                 if (res_spec & functionTable[mid].resource)
2886                 {
2887                     *pWmFunction = functionTable[mid].wmFunction;
2888                     return (mid);
2889                 }
2890
2891                 /* invalid resource:  use F_Nop */
2892                 break;
2893             }
2894
2895             /*
2896              * Function name mismatch 
2897              */
2898             if (cmp > 0)
2899             {
2900                 high = mid - 1;
2901             }
2902             else
2903             {
2904                 low = mid + 1;
2905             }
2906         }
2907     }
2908
2909     /* 
2910      * Not found:  assume f.nop
2911      */
2912     *pWmFunction = F_Nop;
2913     return (F_NOP_INDEX);
2914
2915 } /* END OF FUNCTION ParseWmFunction */
2916
2917 \f
2918 /*************************************<->*************************************
2919  *
2920  *  ParseWmFuncMaybeStrArg (linePP, wmFunction, pArgs)
2921  *
2922  *
2923  *  Description:
2924  *  -----------
2925  *  Parses a window manager function with a null or string argument.
2926  *
2927  *
2928  *  Inputs:
2929  *  ------
2930  *  linePP   = pointer to current line buffer pointer.
2931  *  wmFunction = function (not used).
2932  *  pArgs = pointer to argument destination.
2933  *
2934  * 
2935  *  Outputs:
2936  *  -------
2937  *  linePP   = pointer to revised line buffer pointer.
2938  *  pArgs    = pointer to parsed argument string.
2939  *  Return   = FALSE iff insufficient memory
2940  *
2941  *
2942  *  Comments:
2943  *  --------
2944  *  Only used to parse arguments for F_Lower, F_Raise, and F_Raise_Lower.
2945  *  If it is used for any other function, be sure to change FreeMenuItem ()
2946  *  accordingly.
2947  * 
2948  *************************************<->***********************************/
2949
2950 Boolean ParseWmFuncMaybeStrArg (unsigned char **linePP, 
2951                                        WmFunction wmFunction, String *pArgs)
2952 {
2953     unsigned char *string = *linePP;
2954     unsigned int  len;
2955
2956     ScanWhitespace (&string);
2957 /*
2958     if (*lineP == '-')
2959     {
2960         *linePP = ++lineP;
2961         return (ParseWmFuncStrArg (linePP, wmFunction, pArgs));
2962     }
2963 */
2964 #if 0
2965     else if (*lineP == '"' && *(lineP+1) == '-')
2966     {
2967         /* kill off '-' */
2968         strcpy ((char *) (lineP+1), (char *) (lineP+2));
2969         return (ParseWmFuncStrArg (linePP, wmFunction, pArgs));
2970     }
2971 #endif
2972     if ((len = strlen ((char *)string)) != 0)
2973     {
2974         if ((*pArgs = (String)XtMalloc (len + 1)) == NULL)
2975         {
2976             PWarning (((char *)GETMESSAGE(60, 17, "Insufficient memory")));
2977             return (FALSE);
2978         }
2979         strcpy (*pArgs, (char *)string);
2980         return (TRUE);
2981     }
2982     else
2983     /* Do ParseWmFuncNoArg () */
2984     {
2985         *pArgs = NULL;
2986         return (TRUE);
2987     }
2988
2989 } /* END OF FUNCTION ParseWmFuncMaybeStrArg */
2990
2991 \f
2992 /*************************************<->*************************************
2993  *
2994  *  ParseWmFuncNoArg (linePP, wmFunction, pArgs)
2995  *
2996  *
2997  *  Description:
2998  *  -----------
2999  *  Parses a window manager function null argument.
3000  *
3001  *
3002  *  Inputs:
3003  *  ------
3004  *  linePP   = pointer to current line buffer pointer.
3005  *  wmFunction = function (not used).
3006  *  pArgs = pointer to argument destination.
3007  *
3008  * 
3009  *  Outputs:
3010  *  -------
3011  *  linePP   = unchanged
3012  *  pArgs    = NULL
3013  *  Return   = TRUE
3014  *
3015  *
3016  *  Comments:
3017  *  --------
3018  *  None.
3019  * 
3020  *************************************<->***********************************/
3021
3022 static Boolean ParseWmFuncNoArg (unsigned char **linePP, WmFunction wmFunction,
3023                                  String *pArgs)
3024 {
3025
3026     *pArgs = NULL;
3027     return (TRUE);
3028
3029 } /* END OF FUNCTION ParseWmFuncNoArg */
3030
3031 \f
3032 /*************************************<->*************************************
3033  *
3034  *  ParseWmFuncStrArg (linePP, wmFunction, pArgs)
3035  *
3036  *
3037  *  Description:
3038  *  -----------
3039  *  Parses a window manager function string argument.
3040  *
3041  *
3042  *  Inputs:
3043  *  ------
3044  *  linePP   = pointer to current line buffer pointer.
3045  *  wmFunction = function for which the argument string is intended.
3046  *  pArgs = pointer to argument string destination.
3047  *
3048  * 
3049  *  Outputs:
3050  *  -------
3051  *  linePP   = pointer to revised line buffer pointer.
3052  *  pArgs    = pointer to parsed argument string.
3053  *  Return   = FALSE iff insufficient memory
3054  *
3055  *
3056  *  Comments:
3057  *  --------
3058  *  Insures that an argument for F_Exec() ends in '&' .
3059  *  Only used to parse arguments for F_Exec, F_Menu, F_Lower, F_Raise, 
3060  *  F_Raise_Lower, and F_Screen.  If it is used for any other function, be
3061  *  sure to change FreeMenuItem () accordingly.
3062  * 
3063  *************************************<->***********************************/
3064
3065 Boolean ParseWmFuncStrArg (unsigned char **linePP, 
3066                                   WmFunction wmFunction, String *pArgs)
3067 {
3068     unsigned char *string;
3069     unsigned int  len;
3070     char *p;
3071     wchar_t last;
3072     char delim;
3073     wchar_t wdelim;
3074     int lastlen;
3075
3076     if ((string = GetString (linePP)) != NULL)
3077     /* nonNULL string argument */
3078     {
3079         len = strlen ((char *)string);
3080         if ((*pArgs = (String)XtMalloc (len + 2)) == NULL)
3081         {
3082             PWarning (((char *)GETMESSAGE(60, 17, "Insufficient memory")));
3083             return (FALSE);
3084         }
3085         strcpy (*pArgs, (char *)string);
3086
3087         /*
3088          *  Insure that an argument for F_Exec ends in '&' .
3089          */
3090
3091         if (wmFunction == F_Exec)
3092         {
3093             lastlen = 0;
3094             p = *pArgs;
3095             while (*p &&
3096                    ((len = mblen(p, MB_CUR_MAX)) > 0))
3097             {
3098                 mbtowc(&last, p, MB_CUR_MAX);
3099                 lastlen = len;
3100                 p += len;
3101             }
3102             delim = '&';
3103             mbtowc(&wdelim, &delim, MB_CUR_MAX);
3104             if (lastlen == 1 && last != wdelim)
3105             {
3106                 *p++ = '&';
3107                 *p   = '\0';
3108             }
3109         }
3110     }
3111     else
3112     /* NULL string argument */
3113     {
3114         *pArgs = NULL;
3115     }
3116
3117     return (TRUE);
3118
3119 } /* END OF FUNCTION ParseWmFuncStrArg */
3120
3121 \f
3122 /*************************************<->*************************************
3123  *
3124  *  FreeMenuItem (menuItem)
3125  *
3126  *
3127  *  Description:
3128  *  -----------
3129  *  This procedure destroys a MenuItem structure.
3130  *
3131  *
3132  *  Inputs:
3133  *  ------
3134  *  menuItem = to be destroyed.
3135  *
3136  * 
3137  *  Outputs:
3138  *  -------
3139  *  None.
3140  *
3141  *
3142  *  Comments:
3143  *  --------
3144  *  Assumes that ParseWmFuncStrArg () has parsed a menu item's function
3145  *  argument only for F_Exec, F_Menu, F_Lower, F_Raise, F_Raise_Lower, and
3146  *  F_Screen. If it is used for other functions, be sure to include them here!
3147  * 
3148  *************************************<->***********************************/
3149
3150 void FreeMenuItem (MenuItem *menuItem)
3151 {
3152     if (menuItem->label != NULL)
3153     {
3154         XtFree ((char *)menuItem->label);
3155     }
3156
3157     if (menuItem->accelText != NULL)
3158     {
3159         XtFree ((char *)menuItem->accelText);
3160     }
3161
3162     /*
3163      * If menuItem->wmFuncArgs is nonNULL, we assume that it is a string that
3164      * was malloc'ed in ParseWmFuncStrArg () and we free it now.
3165      */
3166     if ((menuItem->wmFuncArgs != NULL) &&
3167         ((menuItem->wmFunction == F_Exec)  || 
3168          (menuItem->wmFunction == F_Menu)  || 
3169          (menuItem->wmFunction == F_Lower) || 
3170          (menuItem->wmFunction == F_Raise) || 
3171          (menuItem->wmFunction == F_Raise_Lower) ||
3172          (menuItem->wmFunction == F_Screen)))
3173     {
3174         XtFree ((char *)menuItem->wmFuncArgs);
3175     }
3176
3177     XtFree ((char *)menuItem);
3178
3179 } /* END OF FUNCTION FreeMenuItem */
3180
3181
3182 \f
3183 /*************************************<->*************************************
3184  *
3185  *  ParseWmFuncGrpArg (linePP, wmFunction, pGroup)
3186  *
3187  *
3188  *  Description:
3189  *  -----------
3190  *  Parses a window manager function group argument.
3191  *
3192  *
3193  *  Inputs:
3194  *  ------
3195  *  linePP   = pointer to current line buffer pointer.
3196  *  wmFunction = function for which the group argument is intended.
3197  *  pGroup = pointer to group argument destination.
3198  *
3199  * 
3200  *  Outputs:
3201  *  -------
3202  *  linePP   = pointer to revised line buffer pointer.
3203  *  pGroup    = pointer to parsed group argument.
3204  *  Return   = FALSE iff invalid group argument.
3205  *
3206  *
3207  *  Comments:
3208  *  --------
3209  *  The only valid nonNULL arguments are "icon", "window", and "transient".
3210  * 
3211  *************************************<->***********************************/
3212
3213 static Boolean ParseWmFuncGrpArg (unsigned char **linePP, 
3214                                   WmFunction wmFunction, GroupArg *pGroup)
3215 {
3216     unsigned char  *lineP = *linePP;
3217     unsigned char  *startP;
3218     unsigned char   grpStr[MAX_GROUP_STRLEN+1];
3219     int    len;
3220
3221
3222     /*
3223      * Parse groups while each is followed by "|".
3224      */
3225
3226     *pGroup = 0;
3227     while (1)
3228     {
3229         /* 
3230          * Skip whitespace and find next group string.
3231          */
3232
3233         ScanWhitespace (&lineP);
3234         startP = lineP;
3235         ScanAlphanumeric (&lineP);
3236         if (startP == lineP)
3237         /* Group missing => use default or complain */
3238         {
3239             if (*pGroup)
3240             {
3241                 PWarning (((char *)GETMESSAGE(60, 18, "Missing group specification")));
3242                 return (FALSE);
3243             }
3244             else
3245             {
3246                 *pGroup = F_GROUP_DEFAULT;
3247                 break;
3248             }
3249         }
3250
3251         /*
3252          * Found a group string; compare it with valid groups.
3253          */
3254
3255         len = min (lineP - startP, MAX_GROUP_STRLEN);
3256         (void) strncpy ((char *)grpStr, (char *)startP, len);
3257         grpStr[len] = '\0';
3258         ToLower ((char *)grpStr);
3259
3260         if (!strcmp ("icon", (char *)grpStr))
3261         {
3262             *pGroup |= F_GROUP_ICON;
3263         }
3264         else if (!strcmp ("window", (char *)grpStr))
3265         {
3266             *pGroup |= F_GROUP_WINDOW;
3267         }
3268         else if (!strcmp ("transient", (char *)grpStr))
3269         {
3270             *pGroup |= F_GROUP_TRANSIENT;
3271         }
3272         else 
3273         /* Unknown group name */
3274         {
3275             PWarning (((char *)GETMESSAGE(60, 19, "Invalid group specification")));
3276             return (FALSE);
3277         }
3278
3279         /*
3280          *  Continue processing until the line is exhausted.
3281          *  Skip any '|' .
3282          */
3283
3284         ScanWhitespace (&lineP);
3285
3286         if (lineP == NULL || *lineP == '\0')
3287         {
3288             break; 
3289         }
3290         else if (*lineP == '|')
3291         {
3292             lineP++;
3293         }
3294     }
3295
3296     *linePP = lineP;
3297     return (TRUE);
3298
3299 } /* END OF FUNCTION ParseWmFuncGrpArg */
3300
3301
3302 \f
3303 /*************************************<->*************************************
3304  *
3305  *  ParseWmFuncNbrArg (linePP, wmFunction, pNumber)
3306  *
3307  *
3308  *  Description:
3309  *  -----------
3310  *  Parses a window manager function number argument.
3311  *
3312  *
3313  *  Inputs:
3314  *  ------
3315  *  linePP   = pointer to current line buffer pointer.
3316  *  wmFunction = function 
3317  *  pNumber = pointer to number argument destination.
3318  *
3319  * 
3320  *  Outputs:
3321  *  -------
3322  *  linePP   = pointer to revised line buffer pointer.
3323  *  pNumber  = pointer to parsed number argument.
3324  *  Return   = FALSE iff invalid number argument.
3325  *
3326  *
3327  *  Comments:
3328  *  --------
3329  *  None.
3330  * 
3331  *************************************<->***********************************/
3332
3333 static Boolean ParseWmFuncNbrArg (unsigned char **linePP, 
3334                                   WmFunction wmFunction, 
3335                                   unsigned long *pNumber)
3336 {
3337     int  val;
3338
3339     val = StrToNum (GetString (linePP));
3340     if (val == -1)
3341     {
3342         PWarning (((char *)GETMESSAGE(60, 20, "Invalid number specification")));
3343         *pNumber = 0;
3344         return (FALSE);
3345     }
3346
3347     *pNumber = val;
3348     return (TRUE);
3349
3350 } /* END OF FUNCTION ParseWmFuncNbrArg */
3351
3352 /*************************************<->*************************************
3353  *
3354  *  ParseButtonStr ()
3355  *
3356  *
3357  *  Description:
3358  *  -----------
3359  *  This function parses a button set specification string:
3360  *
3361  *     bindings_name
3362  *     {
3363  *       button   context   function
3364  *       button   context   function
3365  *                 ...
3366  *       button   context   function
3367  *     }
3368  *
3369  *
3370  *
3371  *  Inputs:
3372  *  ------
3373  *  pSD->buttonBindings = buttonBindings resource value
3374  *  functionTable = window manager function parse table
3375  *
3376  * 
3377  *  Outputs:
3378  *  -------
3379  *  pSD->buttonSpecs = list of button binding specifications.
3380  *
3381  *
3382  *  Comments:
3383  *  --------
3384  *  The button set specification name must match pSD->buttonBindings.
3385  * 
3386  *************************************<->***********************************/
3387
3388 void ParseButtonStr (WmScreenData *pSD, unsigned char *buttonStr)
3389 {
3390     unsigned char *lineP;
3391
3392     cfileP = NULL;
3393     linec = 0;
3394     if (((parseP = buttonStr) != NULL) && (GetNextLine () != NULL))
3395     {
3396         lineP = line;
3397         ParseButtonSet (pSD, lineP);
3398     }
3399
3400 } /* END OF FUNCTION ParseButtonStr */
3401
3402 \f
3403 /*************************************<->*************************************
3404  *
3405  *  ParseButtonSet (pSD, lineP)
3406  *
3407  *
3408  *  Description:
3409  *  -----------
3410  *  Button set specification found.  Parse the following syntax:
3411  *
3412  *             v
3413  *     Buttons bindings_name
3414  *     {
3415  *       button   context   function
3416  *       button   context   function
3417  *                 ...
3418  *       button   context   function
3419  *     }
3420  *
3421  *
3422  *  Inputs:
3423  *  ------
3424  *  cfileP = (global) file pointer to fopened configuration file or NULL
3425  *  line =  (global) line buffer
3426  *  lineP = pointer to current character in line buffer
3427  *  pSD->buttonBindings = buttonBindings resource value
3428  *
3429  * 
3430  *  Outputs:
3431  *  -------
3432  *  lineP = pointer to current character in line buffer
3433  *  pSD->buttonSpecs = list of button binding specifications.
3434  *
3435  *
3436  *  Comments:
3437  *  --------
3438  *  Skips unnamed button binding set and sets with names that don't match
3439  *  the buttonBindings resource.
3440  *  Skips bad button binding specifications.
3441  * 
3442  *************************************<->***********************************/
3443
3444 static void ParseButtonSet (WmScreenData *pSD, unsigned char *lineP)
3445 {
3446     unsigned char *string;
3447     ButtonSpec    *buttonSpec;
3448     ButtonSpec    *lastButtonSpec;
3449     int            ix;
3450     
3451     /*
3452      * Parse the button set bindings from the configuration file.
3453      * If either the button set name or buttonBindings resource is NULL or 
3454      *   they don't match, then skip this button specification.
3455      */
3456
3457     if (((string = GetString (&lineP)) == NULL) ||
3458         (pSD->buttonBindings == NULL) ||
3459         strcmp ((char *)string, pSD->buttonBindings))
3460     {
3461         return;
3462     }
3463
3464     /* 
3465      * Require leading '{' on the next line.
3466      */
3467     while ((GetNextLine () != NULL))  /* not EOF nor read error */
3468     {
3469         lineP = line;
3470         ScanWhitespace(&lineP);
3471
3472         if ((lineP == NULL) || (*line == '!') || (*lineP == '\0') || (*lineP == '#'))
3473         /* ignore empty or comment line */
3474         {
3475             continue;
3476         }
3477
3478         if (*lineP == '{')
3479         /* found '{' */
3480         {
3481             break;
3482         }
3483
3484         /* not a '{' */
3485         PWarning (((char *)GETMESSAGE(60, 21, "Expected '{' after button set name")));
3486         return;
3487     }
3488
3489     /*
3490      * Found leading "{" or EOF.
3491      * Prepare to accumulate button bindings by finding the end of
3492      *   the button specification list.
3493      * lastButtonSpec will be NULL only if no prior bindings exist.
3494      */
3495
3496     lastButtonSpec = pSD->buttonSpecs;
3497     if (lastButtonSpec != NULL)
3498     {
3499         while (lastButtonSpec->nextButtonSpec != NULL)
3500         {
3501             lastButtonSpec = (lastButtonSpec->nextButtonSpec);
3502         }
3503     }
3504
3505     /*
3506      * Parse "button context function"  until "}" or EOF found.
3507      * Skips bad button binding specifications.
3508      */
3509
3510     while ((GetNextLine () != NULL))  /* not EOF nor read error */
3511     {
3512         lineP = line;
3513         ScanWhitespace(&lineP);
3514         if ((lineP == NULL) || (*line == '!') || (*lineP == '\0') || (*lineP == '#'))
3515         /* ignore empty or comment lines */
3516         {
3517             continue;
3518         }
3519         if (*lineP == '}')  /* finished with button set */
3520         {
3521             break;
3522         }
3523
3524         /*
3525          * Allocate space for the button binding specification. 
3526          */
3527         if ((buttonSpec = (ButtonSpec *)XtMalloc (sizeof (ButtonSpec))) == NULL)
3528         {
3529             PWarning (((char *)GETMESSAGE(60, 22, "Insufficient memory for button specification")));
3530             continue;
3531         }
3532         buttonSpec->wmFunction = (WmFunction)NULL;
3533         buttonSpec->wmFuncArgs = NULL;
3534         buttonSpec->nextButtonSpec = NULL;
3535
3536         /*
3537          * Parse the button specification "button".
3538          */
3539         lineP = line;
3540         if (!ParseBtnEvent(&lineP,
3541                            &buttonSpec->eventType,
3542                            &buttonSpec->button,
3543                            &buttonSpec->state,
3544                            &buttonSpec->click))
3545         {
3546             PWarning (((char *)GETMESSAGE(60, 23, "Invalid button specification")));
3547             XtFree ((char *)buttonSpec);
3548             continue;  /* skip this button specification */
3549         }
3550
3551
3552         /*
3553          * Parse the button context.
3554          */
3555         if (!ParseContext(&lineP, &buttonSpec->context, 
3556                           &buttonSpec->subContext))
3557         {
3558             PWarning (((char *)GETMESSAGE(60, 24, "Invalid button context")));
3559             XtFree ((char *)buttonSpec);
3560             continue;  /* skip this button specification */
3561         }
3562
3563         /*
3564          * Parse the button function and any arguments.
3565          */
3566
3567         ix = ParseWmFunction (&lineP, CRS_BUTTON, &buttonSpec->wmFunction);
3568
3569         /*
3570          * remove any subContexts that don't apply to this function
3571          */
3572
3573         if ((functionTable[ix].greyedContext & F_SUBCONTEXT_IB_IICON) &&
3574             (buttonSpec->subContext & F_SUBCONTEXT_IB_IICON))
3575         {
3576             buttonSpec->subContext &= ~F_SUBCONTEXT_IB_IICON;
3577         }
3578
3579         if ((functionTable[ix].greyedContext & F_SUBCONTEXT_IB_WICON) &&
3580             (buttonSpec->subContext & F_SUBCONTEXT_IB_WICON))
3581         {
3582             buttonSpec->subContext &= ~F_SUBCONTEXT_IB_WICON;
3583         }
3584
3585         /*
3586          * Map Button3 menus to BMenu virtual button
3587          */
3588         if (buttonSpec->button == Button3 && 
3589            (buttonSpec->wmFunction == F_Menu ||
3590             buttonSpec->wmFunction == F_Post_SMenu)) {
3591
3592             buttonSpec->button = wmGD.bMenuButton;
3593         }
3594
3595         /* 
3596          * Apply the function argument parser.
3597          */
3598         if (!(*(functionTable [ix].parseProc)) 
3599                    (&lineP, buttonSpec->wmFunction, &buttonSpec->wmFuncArgs))
3600         {
3601             XtFree ((char *)buttonSpec);
3602             continue;  /* skip this button specification */
3603         }
3604
3605         /* 
3606          * Add the button specification to the button specification list.
3607          */
3608         if (lastButtonSpec != NULL)
3609         /* a prior specification exists */
3610         {
3611             lastButtonSpec->nextButtonSpec = buttonSpec;
3612         }
3613         else
3614         {
3615             pSD->buttonSpecs = buttonSpec;
3616         }
3617         lastButtonSpec = buttonSpec;
3618     }
3619
3620 } /* END OF FUNCTION ParseButtonSet */
3621
3622
3623 \f
3624 /*************************************<->*************************************
3625  *
3626  *  ParseContext (linePP, context, subContext)
3627  *
3628  *
3629  *  Description:
3630  *  -----------
3631  *  Parses a general context string.
3632  *
3633  *
3634  *  Inputs:
3635  *  ------
3636  *  linePP =  pointer to current line buffer pointer.
3637  * 
3638  *  Outputs:
3639  *  -------
3640  *  linePP =  pointer to revised line buffer pointer.
3641  *  context =    context field value
3642  *  subContext = subContext field value
3643  *  Return = (Boolean) true iff valid context string
3644  *
3645  *
3646  *  Comments:
3647  *  --------
3648  *  None.
3649  * 
3650  *************************************<->***********************************/
3651
3652 static Boolean ParseContext (unsigned char **linePP, Context *context, 
3653                              Context *subContext)
3654 {
3655     unsigned char   *lineP = *linePP;
3656     unsigned char   *startP;
3657     unsigned char    ctxStr[MAX_CONTEXT_STRLEN+1];
3658     int     len;
3659
3660     /*
3661      * Parse contexts while each is followed by "|".
3662      */
3663
3664     *context = 0;
3665     *subContext = 0;
3666     while (1) {
3667
3668         /* 
3669          * Skip whitespace and find next context string.
3670          */
3671         ScanWhitespace (&lineP);
3672         startP = lineP;
3673         ScanAlphanumeric (&lineP);
3674         if (startP == lineP)
3675         /* ERROR: Context missing */
3676         {
3677            return (FALSE);
3678         }
3679
3680         /*
3681          * Found nonNULL string; compare it with valid contexts.
3682          */
3683
3684         len = min(lineP - startP, MAX_CONTEXT_STRLEN);
3685         (void) strncpy ((char *)ctxStr, (char *)startP, len);
3686         ctxStr[len] = '\0';
3687         ToLower ((char *)ctxStr);
3688
3689         if (!strcmp ("root", (char *)ctxStr))
3690         {
3691             *context |= F_CONTEXT_ROOT;
3692             *subContext |= F_SUBCONTEXT_R_ALL;
3693         }
3694         else if (!strcmp ("icon", (char *)ctxStr))
3695         {
3696             *context |= (F_CONTEXT_ICON        |
3697                          F_CONTEXT_ICONBOX     |
3698                          F_SUBCONTEXT_IB_IICON | 
3699                          F_SUBCONTEXT_IB_WICON );
3700             *subContext |= (F_SUBCONTEXT_I_ALL    | 
3701                             F_SUBCONTEXT_IB_IICON | 
3702                             F_SUBCONTEXT_IB_WICON );
3703         }
3704         else if (!strcmp ("window", (char *)ctxStr))
3705         {
3706             *context |= F_CONTEXT_WINDOW;
3707             *subContext |= F_SUBCONTEXT_W_ALL;
3708         }
3709         else if (!strcmp ("frame", (char *)ctxStr))
3710         {
3711             *context |= F_CONTEXT_WINDOW;
3712             *subContext |= F_SUBCONTEXT_W_FRAME;
3713         }
3714         else if (!strcmp ("title", (char *)ctxStr))
3715         {
3716             *context |= F_CONTEXT_WINDOW;
3717             *subContext |= F_SUBCONTEXT_W_TITLE;
3718         }
3719         else if (!strcmp ("border", (char *)ctxStr))
3720         {
3721             *context |= F_CONTEXT_WINDOW;
3722             *subContext |= F_SUBCONTEXT_W_BORDER;
3723         }
3724         else if (!strcmp ("app", (char *)ctxStr))
3725         {
3726             *context |= F_CONTEXT_WINDOW;
3727             *subContext |= F_SUBCONTEXT_W_APP;
3728         }
3729         else if (!strcmp ("ifkey", (char *)ctxStr))
3730         {
3731             *context |= F_CONTEXT_IFKEY;
3732         }
3733         else 
3734         /* Unknown context name */
3735         {
3736            return (FALSE);
3737         }
3738
3739         /* continue only if followed by '|' */
3740         ScanWhitespace (&lineP);
3741         if (*lineP != '|')
3742         {
3743             break; 
3744         }
3745         lineP++;
3746     }
3747
3748     *linePP = lineP;
3749     return (TRUE);
3750
3751 } /* END OF FUNCTION ParseContext */
3752
3753 \f
3754 /*************************************<->*************************************
3755  *
3756  *  ParseKeyStr ()
3757  *
3758  *
3759  *  Description:
3760  *  -----------
3761  *  This function parses a key set specification string:
3762  *
3763  *     bindings_name
3764  *     {
3765  *        key   context   function
3766  *        key   context   function
3767  *                     ...
3768  *        key   context   function
3769  *     }
3770  *
3771  *
3772  *  Inputs:
3773  *  ------
3774  *  pSD->keyBindings = keyBindings resource value
3775  *  functionTable = window manager function parse table
3776  *
3777  * 
3778  *  Outputs:
3779  *  -------
3780  *  pSD->keySpecs = list of key binding specification
3781  *
3782  *
3783  *  Comments:
3784  *  --------
3785  *  The key set specification name must match pSD->keyBindings.
3786  * 
3787  *************************************<->***********************************/
3788
3789 void
3790 ParseKeyStr (WmScreenData *pSD, unsigned char *keyStr)
3791 {
3792     unsigned char *lineP;
3793
3794     cfileP = NULL;
3795     linec = 0;
3796     if (((parseP = keyStr) != NULL) && (GetNextLine () != NULL))
3797     {
3798         lineP = line;
3799         ParseKeySet (pSD, lineP);
3800     }
3801
3802 } /* END OF FUNCTION ParseKeyStr */
3803
3804
3805 \f
3806 /*************************************<->*************************************
3807  *
3808  *  ParseKeySet (pSD, lineP)
3809  *
3810  *
3811  *  Description:
3812  *  -----------
3813  *  Key set specification found.  Parse the following syntax:
3814  *
3815  *          v
3816  *     Keys bindings_name
3817  *     {
3818  *        key   context   function
3819  *        key   context   function
3820  *                     ...
3821  *        key   context   function
3822  *     }
3823  *
3824  *
3825  *  Inputs:
3826  *  ------
3827  *  cfileP = (global) file pointer to fopened configuration file or NULL
3828  *  line =  (global) line buffer
3829  *  lineP = pointer to current character in line buffer
3830  *  pSD->keyBindings = keyBindings resource value
3831  *
3832  * 
3833  *  Outputs:
3834  *  -------
3835  *  lineP = pointer to current character in line buffer
3836  *  pSD->keySpecs = list of key binding specifications.
3837  *
3838  *
3839  *  Comments:
3840  *  --------
3841  *  Skips unnamed key binding set and sets with names that don't match the 
3842  *  keyBindings resource.
3843  *  Skips bad key binding specifications.
3844  * 
3845  *************************************<->***********************************/
3846
3847 static void ParseKeySet (WmScreenData *pSD, unsigned char *lineP)
3848 {
3849     unsigned char         *string;
3850     KeySpec      *keySpec;
3851     KeySpec      *lastKeySpec;
3852     unsigned int  eventType;
3853     int           ix;
3854     Boolean     bBadKey;
3855     
3856     /*
3857      * Parse the key set bindings from the configuration file.
3858      * If either the key set name or keyBindings resource is NULL or they
3859      *   don't match then skip this key specification.
3860      */
3861
3862     if (((string = GetString (&lineP)) == NULL) ||
3863         (pSD->keyBindings == NULL) ||
3864         strcmp ((char *)string, pSD->keyBindings))
3865     {
3866         return;
3867     }
3868
3869     /*
3870      * Require leading '{' on next line.
3871      */
3872     while ((GetNextLine () != NULL))  /* not EOF nor read error */
3873     {
3874         lineP = line;
3875         ScanWhitespace(&lineP);
3876
3877         if ((lineP == NULL) || (*line == '!') || (*lineP == '\0') || (*lineP == '#'))
3878         /* ignore empty or comment line */
3879         {
3880             continue;
3881         }
3882
3883         if (*lineP == '{')
3884         /* found '{' */
3885         {
3886             break;
3887         }
3888
3889         /* not a '{' */
3890         PWarning (((char *)GETMESSAGE(60, 25, "Expected '{' after key set name")));
3891         return;
3892     }
3893
3894     /*
3895      * Found leading "{" or EOF.
3896      * Prepare to accumulate key bindings by finding the end of
3897      *   the key specification list.
3898      * lastKeySpec will be NULL only if no prior bindings exist.
3899      */
3900
3901     lastKeySpec = pSD->keySpecs;
3902     if (lastKeySpec != NULL)
3903     {
3904         while (lastKeySpec->nextKeySpec != NULL)
3905         {
3906             lastKeySpec = (lastKeySpec->nextKeySpec);
3907         }
3908     }
3909
3910     /*
3911      * Parse "key context function"  until "}" or EOF found.
3912      * Skip bad key bindings.
3913      */
3914
3915     while ((GetNextLine () != NULL))  /* not EOF nor read error */
3916     {
3917         lineP = line;
3918         ScanWhitespace (&lineP);
3919         if ((lineP == NULL) || (*line == '!') || (*lineP == '\0') || (*lineP == '#'))
3920         /* ignore empty or comment lines */
3921         {
3922             continue;
3923         }
3924         if (*lineP == '}')  /* finished with key set */
3925         {
3926             break;
3927         }
3928
3929         /*
3930          * Allocate space for the key specification.
3931          */
3932         if ((keySpec = (KeySpec *)XtMalloc (sizeof (KeySpec))) == NULL)
3933         {
3934             PWarning (((char *)GETMESSAGE(60, 26, "Insufficient memory for key specification")));
3935             continue;
3936         }
3937
3938         keySpec->wmFunction = (WmFunction)NULL;
3939         keySpec->wmFuncArgs = NULL;
3940         keySpec->nextKeySpec = NULL;
3941
3942         /*
3943          * Parse the key specification.
3944          */
3945         bBadKey = False;
3946         if (!ParseKeyEvent(&lineP,
3947                            &eventType,
3948                            &keySpec->keycode,
3949                            &keySpec->state))
3950         {
3951             bBadKey = True;
3952         }
3953
3954         /*
3955          * Parse the key context.
3956          *   Here lineP points to the candidate context string.
3957          */
3958
3959         if (!ParseContext(&lineP, &keySpec->context, 
3960                           &keySpec->subContext))
3961         {
3962             if (bBadKey)
3963                 PWarning (((char *)GETMESSAGE(60, 27, "Invalid key specification")));
3964             PWarning (((char *)GETMESSAGE(60, 28, "Invalid key context")));
3965             XtFree ((char *)keySpec);
3966             continue;  /* skip this key specification */
3967         }
3968         if (bBadKey)
3969         {
3970             /*
3971              * Don't print an error message if this is a "hardware
3972              * available" binding.
3973              */
3974             if (!(keySpec->context & F_CONTEXT_IFKEY))
3975                 PWarning (((char *)GETMESSAGE(60, 27, "Invalid key specification")));
3976             XtFree ((char *)keySpec);
3977             continue;  /* skip this key specification */
3978         }
3979
3980         /*
3981          * This flag is only used for parsing, clear it so the
3982          * rest of the program doesn't see it.
3983          */
3984         keySpec->context &= ~F_CONTEXT_IFKEY;
3985
3986
3987         /*
3988          * Parse the key function and any arguments.
3989          */
3990
3991         ix = ParseWmFunction (&lineP, CRS_KEY, &keySpec->wmFunction);
3992         
3993         /*
3994          * remove any subContexts that don't apply to this function
3995          */
3996         if ((functionTable[ix].greyedContext & F_SUBCONTEXT_IB_IICON) &&
3997             (keySpec->subContext & F_SUBCONTEXT_IB_IICON))
3998         {
3999             keySpec->subContext &= ~F_SUBCONTEXT_IB_IICON;
4000         }
4001
4002         if ((functionTable[ix].greyedContext & F_SUBCONTEXT_IB_WICON) &&
4003             (keySpec->subContext & F_SUBCONTEXT_IB_WICON))
4004         {
4005             keySpec->subContext &= ~F_SUBCONTEXT_IB_WICON;
4006         }
4007
4008         /* 
4009          * Apply the function argument parser.
4010          */
4011         if (!(*(functionTable [ix].parseProc)) 
4012                    (&lineP, keySpec->wmFunction, &keySpec->wmFuncArgs))
4013         {
4014             XtFree ((char *)keySpec);
4015             continue;  /* skip this key specification */
4016         }
4017
4018         /* 
4019          * Add the key specification to the key specification list. 
4020          */
4021         if (lastKeySpec != NULL)
4022         /* a prior specification exists */
4023         {
4024             lastKeySpec->nextKeySpec = keySpec;
4025         }
4026         else
4027         {
4028             pSD->keySpecs = keySpec;
4029         }
4030         lastKeySpec = keySpec;
4031     }
4032
4033 } /* END OF FUNCTION ParseKeySet */
4034
4035
4036 /*************************************<->*************************************
4037  *
4038  *  ParseBtnEvent (linePP, eventType, button, state, fClick)
4039  *
4040  *
4041  *  Description:
4042  *  -----------
4043  *  Parse a button event specification.
4044  *
4045  *
4046  *  Inputs:
4047  *  ------
4048  *  linePP =          pointer to current line buffer pointer
4049  *  buttonEvents =    (global) button event parse table
4050  *  modifierStrings = (global) modifier string/mask table
4051  *
4052  * 
4053  *  Outputs:
4054  *  -------
4055  *  linePP =    pointer to revised line buffer pointer.
4056  *  eventType = type of event
4057  *  button =    parsed button number
4058  *  state =     composite modifier mask
4059  *  fClick =    is click?
4060  *
4061  *  Return = (Boolean) true iff valid button event specification
4062  * 
4063  *
4064  *  Comments:
4065  *  --------
4066  *  None.
4067  * 
4068  *************************************<->***********************************/
4069
4070 Boolean ParseBtnEvent (unsigned char  **linePP,
4071                        unsigned int *eventType,
4072                        unsigned int *button,
4073                        unsigned int *state,
4074                        Boolean      *fClick)
4075 {
4076     if (!ParseEvent (linePP, buttonEvents, eventType, button, state, fClick))
4077     {
4078        return (FALSE);
4079     }
4080
4081     /* 
4082      * The following is a fix for an X11 deficiency in regards to 
4083      * modifiers in grabs.
4084      */
4085     if (*eventType == ButtonRelease)
4086     {
4087         /* the button that is going up will always be in the modifiers... */
4088         *state |= buttonModifierMasks[*button];
4089     }
4090
4091     return (TRUE);
4092
4093 } /* END OF FUNCTION ParseBtnEvent */
4094
4095
4096 \f
4097 /*************************************<->*************************************
4098  *
4099  *  ParseKeyEvent (linePP, eventType, keyCode, state)
4100  *
4101  *
4102  *  Description:
4103  *  -----------
4104  *  Parse a key event specification.
4105  *
4106  *
4107  *  Inputs:
4108  *  ------
4109  *  linePP =          pointer to current line buffer pointer
4110  *  keyEvents =       (global) key event parse table
4111  *  modifierStrings = (global) modifier string/mask table
4112  *
4113  * 
4114  *  Outputs:
4115  *  -------
4116  *  linePP =    pointer to revised line buffer pointer.
4117  *  eventType = type of event
4118  *  keyCode =   parsed KeyCode
4119  *  state =     composite modifier mask
4120  *
4121  *  Return = (Boolean) true iff valid key event specification
4122  * 
4123  *
4124  *  Comments:
4125  *  --------
4126  *  None.
4127  * 
4128  *************************************<->***********************************/
4129
4130 Boolean ParseKeyEvent (unsigned char **linePP, unsigned int *eventType,
4131                        KeyCode *keyCode,  unsigned int *state)
4132
4133
4134 {
4135     Boolean      fClick;
4136     unsigned int keySym = 0;
4137
4138     if (!ParseEvent (linePP, keyEvents, eventType, &keySym, state, &fClick))
4139     {
4140        return (FALSE);
4141     }
4142
4143     /* 
4144      * Here keySym is a KeySym.  Convert it to a KeyCode.
4145      * KeyCode will be set to 0 if keySym is not defined for any KeyCode
4146      *  (e.g. 0x001).
4147      */
4148
4149     *keyCode = XKeysymToKeycode(DISPLAY, (KeySym) keySym);
4150
4151     if (*keyCode == 0)
4152     {
4153         if (keySym == XK_F9)
4154         {
4155             keySym = XK_KP_F1;
4156         }
4157         else if (keySym == XK_F10)
4158         {
4159             keySym = XK_KP_F2;
4160         }
4161         else if (keySym == XK_F11)
4162         {
4163             keySym = XK_KP_F3;
4164         }
4165         else if (keySym == XK_F12)
4166         {
4167             keySym = XK_KP_F4;
4168         }
4169         *keyCode = XKeysymToKeycode(DISPLAY, (KeySym) keySym);
4170     }
4171
4172     return (*keyCode != 0);
4173
4174 } /* END OF FUNCTION ParseKeyEvent */
4175
4176 \f
4177 /*************************************<->*************************************
4178  *
4179  *  ParseEvent (linePP, table, eventType, detail, state, fClick)
4180  *
4181  *
4182  *  Description:
4183  *  -----------
4184  *  Parse an event specification.
4185  *
4186  *
4187  *  Inputs:
4188  *  ------
4189  *  linePP =          pointer to current line buffer pointer.
4190  *  table =           event parse table
4191  *  modifierStrings = (global) modifier string/mask table
4192  *
4193  * 
4194  *  Outputs:
4195  *  -------
4196  *  linePP =    pointer to revised line buffer pointer.
4197  *  eventType = type of event
4198  *  detail =    dependent upon parse table detail procedure and closure
4199  *  state =     composite modifier mask
4200  *  fClick =    click flag
4201  *
4202  *  Return = (Boolean) true iff valid event specification
4203  *
4204  *  Comments:
4205  *  --------
4206  *  None.
4207  * 
4208  *************************************<->***********************************/
4209
4210 static Boolean ParseEvent (unsigned char **linePP, EventTableEntry *table,
4211                            unsigned int *eventType, unsigned int *detail,
4212                            unsigned int *state, Boolean *fClick)
4213 {
4214     unsigned char    *lineP = *linePP;
4215     Cardinal ix;
4216     Boolean  status;
4217  
4218     /* Parse the modifiers */
4219     if (!ParseModifiers (&lineP, state) || *lineP != '<')
4220     {
4221        return (FALSE);
4222     }
4223     lineP++;  /* skip '<' */
4224
4225     /* Parse the event type */
4226     if (!ParseEventType (&lineP, table, eventType, &ix) || *lineP != '>') 
4227     {
4228        return (FALSE);
4229     }
4230     lineP++;  /* skip '>' */
4231
4232     /*
4233      *  Compute detail and fClick.
4234      *  Status will be False for a invalid KeySym name.
4235      */
4236     status = (*(table[ix].parseProc))(&lineP, table[ix].closure, detail);
4237     *fClick = table[ix].fClick;
4238
4239     if (status)
4240     {
4241         *linePP = lineP;
4242     }
4243     return (status);
4244
4245 } /* END OF FUNCTION ParseEvent */
4246
4247
4248 \f
4249 /*************************************<->*************************************
4250  *
4251  *  ParseModifiers(linePP, state)
4252  *
4253  *
4254  *  Description:
4255  *  -----------
4256  *  Parses a modifier specification.
4257  *
4258  *
4259  *  Inputs:
4260  *  ------
4261  *  linePP = pointer to current line buffer pointer.
4262  *  modifierStrings = (global) modifier string/mask table
4263  *
4264  * 
4265  *  Outputs:
4266  *  -------
4267  *  linePP =    pointer to revised line buffer pointer.
4268  *  state  = composite modifier mask
4269  *  Return = (Boolean) true iff valid modifier name
4270  *
4271  *
4272  *  Comments:
4273  *  --------
4274  *  If successful, will be followed by NULL or '<'.
4275  * 
4276  *************************************<->***********************************/
4277
4278 static Boolean ParseModifiers(unsigned char **linePP, unsigned int *state)
4279 {
4280     unsigned char         *lineP = *linePP;
4281     unsigned char         *startP;
4282     unsigned char          modStr[MAX_MODIFIER_STRLEN+1];
4283     Boolean       fNot;
4284     unsigned int  maskBit;
4285     int           len;
4286
4287     *state = 0;
4288  
4289     /*
4290      * Parse modifiers until the event specifier is encountered.
4291      */
4292
4293     ScanWhitespace (&lineP);
4294     while ((*lineP != '\0') && (*lineP != '<'))
4295     {
4296         if (*lineP == '~') 
4297         {
4298             fNot = TRUE;
4299             lineP++;
4300         }
4301         else 
4302         {
4303             fNot = FALSE;
4304         }
4305
4306         startP = lineP;
4307         ScanAlphanumeric (&lineP);
4308         if (startP == lineP)
4309         /* ERROR: Modifier or '<' missing */
4310         {
4311             return (FALSE);
4312         }
4313         len = min(lineP - startP, MAX_MODIFIER_STRLEN);
4314         (void) strncpy ((char *)modStr, (char *)startP, len);
4315         modStr[len] = '\0';
4316
4317         if (!LookupModifier (modStr, &maskBit))
4318         /* Unknown modifier name */
4319         {
4320             return (FALSE);
4321         }
4322
4323         if (fNot) 
4324         {
4325             *state &= ~maskBit;
4326         }
4327         else 
4328         {
4329             *state |= maskBit;
4330         }
4331         ScanWhitespace(&lineP);
4332     }
4333
4334     *linePP = lineP;
4335     return (TRUE);  /* must have '<' or NULL following */
4336
4337 } /* END OF FUNCTION ParseModifiers */
4338
4339 \f
4340 /*************************************<->*************************************
4341  *
4342  *  LookupModifier (name, valueP)
4343  *
4344  *
4345  *  Description:
4346  *  -----------
4347  *  Return the modifier mask for the provided modifier name.
4348  *
4349  *
4350  *  Inputs:
4351  *  ------
4352  *  name = modifier string
4353  *  modifierStrings = modifier string/mask table
4354  *
4355  * 
4356  *  Outputs:
4357  *  -------
4358  *  valueP = modifier mask
4359  *  Return = (Boolean) true iff valid modifier name
4360  *
4361  *
4362  *  Comments:
4363  *  --------
4364  *  None.
4365  * 
4366  *************************************<->***********************************/
4367
4368 static Boolean LookupModifier (unsigned char *name, unsigned int *valueP)
4369 {
4370     int i;
4371
4372     if (name != NULL)
4373     {
4374         ToLower ((char *)name);
4375         for (i=0; modifierStrings[i].name != NULL; i++)
4376         {
4377             if (!strcmp (modifierStrings[i].name, (char *)name))
4378             {
4379                 *valueP = modifierStrings[i].mask;
4380                 return (TRUE);
4381             }
4382         }
4383     }
4384
4385     return (FALSE);
4386
4387 } /* END OF FUNCTION LookupModifier */
4388
4389 /*************************************<->*************************************
4390  *
4391  *  ParseEventType(linePP, table, eventType, ix)
4392  *
4393  *
4394  *  Description:
4395  *  -----------
4396  *  Parses the event type string.
4397  *
4398  *
4399  *  Inputs:
4400  *  ------
4401  *  linePP = pointer to current line buffer pointer.
4402  *  table =  event parse table
4403  *
4404  * 
4405  *  Outputs:
4406  *  -------
4407  *  linePP = pointer to revised line buffer pointer.
4408  *  eventType = type of event
4409  *  ix = table index for matched event
4410  *
4411  *  Return = (Boolean) true iff valid event
4412  *
4413  *
4414  *  Comments:
4415  *  --------
4416  *  None.
4417  * 
4418  *************************************<->***********************************/
4419
4420 static Boolean ParseEventType (unsigned char **linePP, EventTableEntry *table,
4421                                unsigned int *eventType, Cardinal *ix)
4422 {
4423     unsigned char *lineP = *linePP;
4424     unsigned char *startP = *linePP;
4425     unsigned char eventTypeStr[MAX_EVENTTYPE_STRLEN+1];
4426     int  len;
4427
4428     /* Parse out the event string */
4429     ScanAlphanumeric (&lineP);
4430
4431     /*
4432      * Attempt to match the parsed event against our supported event set.
4433      */
4434
4435     if (startP != lineP)
4436     {
4437         len = min (lineP - startP, MAX_EVENTTYPE_STRLEN);
4438         (void) strncpy ((char *)eventTypeStr, (char *)startP, len);
4439         eventTypeStr[len] = '\0';
4440         ToLower ((char *)eventTypeStr);
4441
4442         for (len = 0; table[len].event != NULL; len++)
4443             if (!strcmp (table[len].event, (char *)eventTypeStr))
4444             {
4445                *ix = len;
4446                *eventType = table[*ix].eventType;
4447                *linePP = lineP;
4448                return (TRUE); 
4449             }
4450     }
4451
4452     /* Unknown event specified */
4453     return (FALSE);
4454
4455 } /* END OF FUNCTION ParseEventType */
4456
4457 \f
4458 /*************************************<->*************************************
4459  *
4460  *  ParseImmed (linePP, closure, detail)
4461  *
4462  *
4463  *  Description:
4464  *  -----------
4465  *  Button event detail procedure.
4466  *
4467  *
4468  *  Inputs:
4469  *  ------
4470  *  linePP =  not used
4471  *  closure = table entry
4472  *
4473  * 
4474  *  Outputs:
4475  *  -------
4476  *  detail = pointer to closure
4477  *
4478  *  Return = TRUE
4479  *
4480  *
4481  *  Comments:
4482  *  --------
4483  *  None.
4484  * 
4485  *************************************<->***********************************/
4486
4487 static Boolean ParseImmed (unsigned char **linePP, unsigned int closure,
4488                            unsigned int  *detail)
4489 {
4490     *detail = closure;
4491     return (TRUE);
4492
4493 } /* END OF FUNCTION ParseImmed */
4494
4495 \f
4496 /*************************************<->*************************************
4497  *
4498  *  ParseKeySym (linePP, closure, detail)
4499  *
4500  *
4501  *  Description:
4502  *  -----------
4503  *  Key event detail procedure.  Parses a KeySym string.
4504  *
4505  *
4506  *  Inputs:
4507  *  ------
4508  *  linePP = pointer to current line buffer pointer
4509  *
4510  *  closure = not used.
4511  *
4512  * 
4513  *  Outputs:
4514  *  -------
4515  *  linePP = pointer to revised line buffer pointer
4516  *  detail = pointer to parsed KeySym
4517  *
4518  *  Return = (Boolean) true iff valid KeySym string
4519  *
4520  *
4521  *  Comments:
4522  *  --------
4523  *  None.
4524  * 
4525  *************************************<->***********************************/
4526
4527 static Boolean ParseKeySym (unsigned char **linePP, unsigned int closure,
4528                             unsigned int *detail)
4529 {
4530     unsigned char *lineP = *linePP;
4531     unsigned char *startP;
4532     char           keySymName[MAX_KEYSYM_STRLEN+1];
4533     int            len;
4534     int            chlen;
4535
4536     ScanWhitespace (&lineP);
4537     startP = lineP;
4538
4539     while (*lineP &&
4540            ((chlen = mblen ((char *)lineP, MB_CUR_MAX)) > 0) &&
4541            ((chlen > 1) ||
4542            (!isspace (*lineP) && *lineP != ',' && *lineP != ':')))
4543     {
4544         /* Skip next character */
4545         lineP += chlen;
4546     }
4547
4548     len = min (lineP - startP, MAX_KEYSYM_STRLEN);
4549     (void) strncpy (keySymName, (char *)startP, len);
4550     keySymName[len] = '\0';
4551
4552     if ((*detail = XStringToKeysym(keySymName)) == NoSymbol &&
4553          (mblen (keySymName, MB_CUR_MAX) == 1))
4554     {
4555         if (!isdigit (keySymName[0]) ||
4556             ((*detail = StrToNum ((unsigned char *)&keySymName[0])) == -1))
4557         {
4558             *detail = NoSymbol;
4559             return (FALSE);
4560         }
4561     }
4562     *linePP = lineP;
4563     return (TRUE);
4564
4565 } /* END OF FUNCTION ParseKeySym */
4566
4567
4568 \f
4569 /*************************************<->*************************************
4570  *
4571  *  StrToNum(str)
4572  *
4573  *
4574  *  Description:
4575  *  -----------
4576  *  Converts a string to an unsigned hexadecimal, decimal, or octal integer.
4577  *
4578  *
4579  *  Inputs:
4580  *  ------
4581  *  str = character string
4582  *
4583  * 
4584  *  Outputs:
4585  *  -------
4586  *  Return = unsigned integer 
4587  *
4588  *
4589  *  Comments:
4590  *  --------
4591  *  None.
4592  * 
4593  *************************************<->***********************************/
4594
4595 static unsigned int StrToNum(unsigned char *str)
4596 {
4597     unsigned char c;
4598     unsigned int  val = 0;
4599
4600     if (*str == '0')
4601     {
4602         str++;
4603         if (*str == 'x' || *str == 'X')
4604         {
4605             return (StrToHex(++str));
4606         }
4607         return (StrToOct(str));
4608     }
4609
4610     while ((c = *str) != '\0')
4611     {
4612         if ('0' <= c && c <= '9')
4613         {
4614             val = val*10+c-'0';
4615         }
4616         else
4617         {
4618             return (-1);
4619         }
4620         str++;
4621     }
4622
4623     return (val);
4624
4625 } /* END OF FUNCTION StrToNum */
4626
4627
4628 \f
4629 /*************************************<->*************************************
4630  *
4631
4632  *
4633  *
4634  *  Description:
4635  *  -----------
4636  *
4637  *  Inputs:
4638  *  ------
4639  *
4640  * 
4641  *  Outputs:
4642  *  -------
4643  *
4644  *  Comments:
4645  *  --------
4646  *  None.
4647  * 
4648  *************************************<->***********************************/
4649
4650 static unsigned int StrToHex(unsigned char *str)
4651 {
4652     unsigned char c;
4653     unsigned int  val = 0;
4654
4655     while ((c = *str) != '\0')
4656     {
4657         if ('0' <= c && c <= '9')
4658         {
4659             val = val*16+c-'0';
4660         }
4661         else if ('a' <= c && c <= 'f')
4662         {
4663             val = val*16+c-'a'+10;
4664         }
4665         else if ('A' <= c && c <= 'F')
4666         {
4667             val = val*16+c-'A'+10;
4668         }
4669         else
4670         {
4671             return (-1);
4672         }
4673         str++;
4674     }
4675
4676     return (val);
4677
4678 } /* END OF FUNCTION StrToHex */
4679
4680
4681 \f
4682 /*************************************<->*************************************
4683  *
4684
4685  *
4686  *
4687  *  Description:
4688  *  -----------
4689  *
4690  *  Inputs:
4691  *  ------
4692  *
4693  * 
4694  *  Outputs:
4695  *  -------
4696  *
4697  *  Comments:
4698  *  --------
4699  *  None.
4700  * 
4701  *************************************<->***********************************/
4702
4703 static unsigned int StrToOct(unsigned char *str)
4704 {
4705     unsigned char c;
4706     unsigned int  val = 0;
4707
4708     while ((c = *str) != '\0')
4709     {
4710         if ('0' <= c && c <= '7')
4711         {
4712             val = val*8+c-'0';
4713         }
4714         else
4715         {
4716             return (-1);
4717         }
4718         str++;
4719     }
4720
4721     return (val);
4722
4723 } /* END OF FUNCTION StrToOct */
4724
4725
4726 \f
4727 /*************************************<->*************************************
4728  *
4729  *  ScanAlphanumeric (linePP)
4730  *
4731  *
4732  *  Description:
4733  *  -----------
4734  *  Scan string until a non-alphanumeric character is encountered.
4735  *
4736  *
4737  *  Inputs:
4738  *  ------
4739  *  linePP = nonNULL pointer to current line buffer pointer
4740  *
4741  * 
4742  *  Outputs:
4743  *  -------
4744  *  linePP = nonNULL pointer to revised line buffer pointer
4745  *
4746  *
4747  *  Comments:
4748  *  --------
4749  *  Assumes linePP is nonNULL
4750  * 
4751  *************************************<->***********************************/
4752
4753 void ScanAlphanumeric (unsigned char **linePP)
4754 {
4755     int            chlen;
4756
4757     while (*linePP &&
4758            ((chlen = mblen ((char *) *linePP, MB_CUR_MAX)) > 0) &&
4759            ((chlen > 1) || isalnum (**linePP)))
4760     {
4761         (*linePP) += chlen;
4762     }
4763 } /* END OF FUNCTION ScanAlphanumeric */
4764
4765
4766
4767
4768 \f
4769 /*************************************<->*************************************
4770  *
4771  *  PWarning (message)
4772  *
4773  *
4774  *  Description:
4775  *  -----------
4776  *  This function lists a resource description parse message to stderr.
4777  *
4778  *
4779  *  Inputs:
4780  *  ------
4781  *  message = pointer to a message string
4782  *  cfileP  = (global) file pointer to fopened configuration file or NULL
4783  *  linec   = (global) line counter
4784  * 
4785  *************************************<->***********************************/
4786
4787 void
4788 PWarning (char *message)
4789 {
4790
4791     char pch[MAXWMPATH+1];
4792     String sMessage;
4793     char *pchFile;
4794
4795     sMessage = XtNewString ((String) message);
4796     if (cfileP != NULL)
4797     {
4798         if (pConfigStackTop->fileName)
4799         {
4800             pchFile = pConfigStackTop->fileName;
4801         }
4802         else 
4803         {
4804             pchFile = wmGD.configFile;
4805         }
4806
4807         sprintf (pch, pWarningStringFile,
4808                      GETMESSAGE(20,1,"Workspace Manager"), 
4809                      sMessage, linec, pchFile);
4810     }
4811     else
4812     {
4813         sprintf (pch, pWarningStringLine,
4814                      GETMESSAGE(20,1,"Workspace Manager"), 
4815                      sMessage, linec);
4816     }
4817     _DtSimpleError (wmGD.mwmName, DtIgnore, NULL, pch, NULL);
4818     XtFree (sMessage);
4819
4820
4821 } /* END OF FUNCTION PWarning */
4822
4823 /*
4824  * Key substitution table entry
4825  */
4826
4827 typedef struct _keySubs
4828 {
4829     unsigned char *     pchFrom;
4830     int                 lenFrom;
4831     unsigned char *     pchTo;
4832 } KeySub;
4833
4834 \f
4835 /*************************************<->*************************************
4836  *
4837  *  InitKeySubs (ppKeySubs, pNumKeySubs)
4838  *
4839  *
4840  *  Description:
4841  *  -----------
4842  *  Initialize key label substitutions used in acclerator text
4843  *
4844  *
4845  *  Inputs:
4846  *  ------
4847  *  ppKeySubs   = ptr to key substitution table ptr
4848  *  pNumKeySubs = ptr to number of key substitutions
4849  *
4850  * 
4851  *  Outputs:
4852  *  -------
4853  *  *ppKeySubs          = ptr to key substitution table 
4854  *  *pNumKeySubs        = number of substitutions found
4855  *
4856  *  Comments:
4857  *  --------
4858  *  *ppKeySubs is allocated with XtMalloc in a complicated way.
4859  *  If this ever needs to be freed, a function to free it needs to
4860  *  be written.
4861  * 
4862  *************************************<->***********************************/
4863
4864 static void InitKeySubs (
4865     KeySub      **ppKeySubs,
4866     int         *pNumKeySubs)
4867 {
4868     int numKS;
4869     KeySub *pKS;
4870     KeySub *pKSret;
4871     unsigned char *pch0;
4872     unsigned char *pch1;
4873     int len;
4874     int         chlen;
4875
4876     pch0 = (unsigned char *)GETMESSAGE(60, 40, "");
4877
4878     if ((pch0 == NULL) || (*pch0 == '\0'))
4879     {
4880         *ppKeySubs = NULL;
4881         *pNumKeySubs = 0;
4882         return;
4883     }
4884
4885     pKSret = NULL;
4886     numKS = 0;
4887
4888     while (*pch0 != '\0')
4889     {
4890         ScanWhitespace (&pch0);
4891         if (*pch0 == '\0') break;
4892
4893         /* 
4894          * allocate space for next key sub 
4895          */
4896         if (pKSret == NULL)
4897         {
4898             pKSret = (KeySub *) XtMalloc (1*sizeof(KeySub));
4899         }
4900         else
4901         {
4902             pKSret = (KeySub *) XtRealloc ((char *)pKSret, 
4903                                            (numKS+1)*sizeof(KeySub));
4904         }
4905         pKS = &pKSret[numKS];
4906
4907         /* get "from" string */
4908         pch1 = pch0;
4909         while (*pch1 && ((chlen = mblen ((char *)pch1, MB_CUR_MAX)) > 0))
4910         {
4911             if ((chlen == 1) && (*pch1 == ' '))
4912             {
4913                 break;
4914             }
4915             pch1 += chlen;
4916         }
4917         pKS->lenFrom = pch1 - pch0;
4918         if (pKS->lenFrom < 1) 
4919         {
4920             /*
4921              * no "from" string
4922              */
4923             break;
4924         }
4925         pKS->pchFrom = (unsigned char *) XtMalloc (1+pKS->lenFrom);
4926         memcpy (pKS->pchFrom, pch0, pKS->lenFrom);
4927         pKS->pchFrom[pKS->lenFrom] = '\0';
4928
4929         /* get "to" string */
4930         ScanWhitespace (&pch1);
4931         pch0 = pch1;
4932
4933         while (*pch1 && ((chlen = mblen ((char *)pch1, MB_CUR_MAX)) > 0))
4934         {
4935             if ((chlen == 1) && (*pch1 == ' '))
4936             {
4937                 break;
4938             }
4939             pch1 += chlen;
4940         }
4941
4942         len = pch1 - pch0;
4943         if (len < 1)
4944         {
4945             /*
4946              * Invalid format, "from" string with no "to" string.
4947              */
4948             break;
4949         }
4950         pKS->pchTo = (unsigned char *) XtMalloc (1+len);
4951         memcpy (pKS->pchTo, pch0, len);
4952         pKS->pchTo[len] = '\0';
4953
4954         /* advance cursor */
4955         pch0 = pch1;
4956
4957         /* got one, bump the counter */
4958         numKS++; 
4959     }
4960
4961     *ppKeySubs = pKSret;
4962     *pNumKeySubs = numKS;
4963 }
4964
4965
4966 /*************************************<->*************************************
4967  *
4968  *  ProcessAccelText (startP, endP, destP)
4969  *
4970  *
4971  *  Description:
4972  *  -----------
4973  *  Process accelerator text and copy into string.
4974  *
4975  *
4976  *  Inputs:
4977  *  ------
4978  *  startP = pointer to start of valid accelerator specification
4979  *  endP =   pointer past end of accelerator specification
4980  *  destP =  pointer to destination buffer
4981  *
4982  * 
4983  *  Outputs:
4984  *  -------
4985  *  Destination buffer has processed accelerator text.
4986  *
4987  *  Comments:
4988  *  --------
4989  *  None.
4990  * 
4991  *************************************<->***********************************/
4992
4993 static void ProcessAccelText (unsigned char *startP, unsigned char *endP,
4994                               unsigned char *destP)
4995 {
4996     int   chlen;
4997     static Boolean      bAccelInit = False;
4998     static KeySub       *pKeySub;
4999     static int          numKeySubs;
5000     unsigned char *     pchFirst;
5001     unsigned char *     pchSub;
5002     int                 lenSub;
5003     int                 i;
5004
5005     if (!bAccelInit)
5006     {
5007         InitKeySubs (&pKeySub, &numKeySubs);
5008         bAccelInit = True;
5009     }
5010
5011     /*
5012      * Copy modifiers
5013      */
5014
5015     ScanWhitespace (&startP);
5016
5017     while (*startP != '<')
5018     {
5019         if (*startP == '~') 
5020         {
5021             *destP++ = *startP++;
5022         }
5023         pchFirst = startP;
5024
5025         while (*startP &&
5026                (((chlen = mblen ((char *)startP, MB_CUR_MAX)) > 1)
5027                 || isalnum (*startP)))
5028         {
5029             while (chlen--)
5030             {
5031                 startP++;
5032             }
5033         }
5034
5035         /* find substitution */
5036         pchSub = NULL;
5037         lenSub = 0;
5038
5039         for (i=0; i<numKeySubs; i++)
5040         {
5041             if ((pKeySub[i].lenFrom == startP-pchFirst) &&
5042                 (!strncasecmp ((char *)pKeySub[i].pchFrom, (char *)pchFirst, 
5043                                 pKeySub[i].lenFrom)))
5044             {
5045                 pchSub = pKeySub[i].pchTo;
5046                 lenSub = strlen((char *)pchSub);
5047                 break;
5048             }
5049
5050         }
5051
5052         if ((pchSub != NULL) && (lenSub > 0))
5053         {
5054             memcpy (destP, pchSub, lenSub);
5055             destP += lenSub;
5056         }
5057         else
5058         {
5059             memcpy (destP, pchFirst, startP-pchFirst);
5060             destP += startP-pchFirst;
5061         }
5062         *destP++ = '+';
5063
5064         ScanWhitespace (&startP);
5065     }
5066
5067     /*
5068      * Skip the key event type.
5069      */
5070     startP++;  /* skip '<' */
5071     while (*startP != '>')
5072     {
5073         startP += mblen ((char *)startP, MB_CUR_MAX);
5074     }
5075     startP++;  /* skip '>' */
5076
5077     /*
5078      * Copy the KeySym string.
5079      */
5080
5081     ScanWhitespace (&startP);
5082     while (startP != endP)
5083     {
5084         *destP++ = *startP++;
5085     }
5086     *destP = '\0';
5087
5088 } /* END OF FUNCTION ProcessAccelText */
5089
5090
5091 \f
5092 /*************************************<->*************************************
5093  *
5094  *  ProcessCommandLine (argc, argv)
5095  *
5096  *
5097  *  Description:
5098  *  -----------
5099  *  This function looks for and processes mwm options in the command line
5100  *
5101  *  Inputs:
5102  *  ------
5103  *  argc =   argument count.
5104  *  argv =   argument vector.
5105  *
5106  * 
5107  *  Outputs:
5108  *  -------
5109  *  Changes global data to based on command line options recognized
5110  *
5111  *
5112  *************************************<->***********************************/
5113 #define SCREENS_OPT             "-screens"
5114 #define MULTI_SCREEN_OPT        "-multiscreen"
5115
5116 void ProcessCommandLine (int argc,  char *argv[])
5117 {
5118     unsigned char *string;
5119     int argnum;
5120     unsigned char *lineP;
5121
5122     for (argnum = 1; argnum < argc; argnum++)
5123     {
5124         lineP = (unsigned char *) argv[argnum];
5125         if ((string = GetString (&lineP)) == NULL) 
5126         /* empty or comment line */
5127         {
5128             continue;
5129         }
5130         if (!strcmp((char *)string, MULTI_SCREEN_OPT))
5131         {
5132             wmGD.multiScreen = True;
5133             wmGD.numScreens = ScreenCount (DISPLAY);
5134         }
5135         else if (!strcmp((char *)string, SCREENS_OPT))
5136         {
5137             argnum++;           /* skip to next arg */
5138             ParseScreensArgument (argc, argv, &argnum, lineP);
5139         }
5140     }
5141
5142 } /* END OF FUNCTION ProcessCommandLine */
5143
5144 \f
5145 /*************************************<->*************************************
5146  *
5147  *  ParseScreensArgument (argc, argv, pArgnum, lineP)
5148  *
5149  *
5150  *  Description:
5151  *  -----------
5152  *  This function processes the ``-screens'' command line argument
5153  *
5154  *  Inputs:
5155  *  ------
5156  *  argc =   argument count.
5157  *  argv =   argument vector.
5158  *  pArgnum = pointer to argument number where processing left off
5159  *  lineP  = pointer into argv[*pArgnum] where processing left off
5160  *
5161  * 
5162  *  Outputs:
5163  *  -------
5164  *  Changes global data to based on command line options recognized
5165  *      + wmGD.screenNames
5166  *      + wmGD.numScreens
5167  *  Assumes default screenNames are already in place
5168  *
5169  *************************************<->***********************************/
5170
5171 static void ParseScreensArgument (int argc, char *argv[], int *pArgnum,
5172                                   unsigned char *lineP)
5173 {
5174     unsigned char *string;
5175     int sNum = 0;
5176     int lastLen;
5177     int nameCount = 0;
5178
5179     for (; (*pArgnum < argc) && (sNum < ScreenCount(DISPLAY)); 
5180                                             (*pArgnum)++, sNum++)
5181     {
5182         lineP = (unsigned char *)argv[*pArgnum];
5183         if (*argv[*pArgnum] == '"')
5184         {
5185             /*
5186              * if Quote, use GetString to strip it
5187              */
5188             if ((string = GetString (&lineP)) == NULL) 
5189                 /* empty or comment line */
5190             {
5191                 continue;
5192             }
5193         }
5194         else 
5195         {
5196             string = (unsigned char *)argv[*pArgnum];
5197             if (*string == '-')
5198             {
5199                 /* another option, end of screens names */
5200                 break;
5201             }
5202         }
5203         
5204         if (!(wmGD.screenNames[sNum] = (unsigned char *) 
5205               XtRealloc ((char*)wmGD.screenNames[sNum], 
5206                          1 + strlen((char *)string))))
5207         {
5208             Warning (((char *)GETMESSAGE(60, 31, "Insufficient memory for screen names")));
5209             ExitWM(WM_ERROR_EXIT_VALUE);
5210         }
5211         else 
5212         {
5213             strcpy((char *)wmGD.screenNames[sNum], (char *)string);
5214             nameCount++;
5215         }
5216     }
5217
5218     (*pArgnum)--;
5219
5220     /*
5221      * remaining screens (if any) get first name specified 
5222      */
5223     if (nameCount > 0)
5224     {
5225         lastLen = 1 + strlen((char *)wmGD.screenNames[0]);
5226         for (; sNum < ScreenCount(DISPLAY); sNum++)
5227         {
5228             if (!(wmGD.screenNames[sNum] = (unsigned char *) 
5229                 XtRealloc ((char*)wmGD.screenNames[sNum], lastLen)))
5230             {
5231                 Warning (((char *)GETMESSAGE(60, 32, "Insufficient memory for screen names")));
5232                 ExitWM(WM_ERROR_EXIT_VALUE);
5233             }
5234             else 
5235             {
5236                 strcpy((char *)wmGD.screenNames[sNum], 
5237                        (char *)wmGD.screenNames[0]);
5238             }
5239         }
5240     }
5241
5242 } /* END OF FUNCTION ParseScreensArgument */
5243
5244 \f
5245 /*************************************<->*************************************
5246  *
5247  *  ProcessMotifBindings ()
5248  *
5249  *
5250  *  Description:
5251  *  -----------
5252  *  This function is used retrieve the motif input bindings
5253  *  and put them into a property on the root window.
5254  *
5255  *
5256  *************************************<->***********************************/
5257 void ProcessMotifBindings (void)
5258 {
5259     char           fileName[MAXWMPATH+1];
5260     char          *bindings = NULL;
5261     char          *homeDir = XmeGetHomeDirName();
5262
5263     /*
5264      *  Look in the user's home directory for .motifbind
5265      */
5266
5267     strcpy (fileName, homeDir);
5268     strncat(fileName, "/", MAXWMPATH-strlen(fileName));
5269     strncat(fileName, MOTIF_BINDINGS_FILE, MAXWMPATH-strlen(fileName));
5270
5271     XDeleteProperty (DISPLAY, RootWindow (DISPLAY, 0),
5272                 XInternAtom (DISPLAY, "_MOTIF_BINDINGS", False));
5273     XDeleteProperty (DISPLAY, RootWindow (DISPLAY, 0),
5274                 XInternAtom (DISPLAY, "_MOTIF_DEFAULT_BINDINGS", False));
5275
5276     if (_XmVirtKeysLoadFileBindings (fileName, &bindings) == True) {
5277         XChangeProperty (DISPLAY, RootWindow(DISPLAY, 0),
5278                 XInternAtom (DISPLAY, "_MOTIF_BINDINGS", False),
5279                 XA_STRING, 8, PropModeReplace,
5280                 (unsigned char *)bindings, strlen(bindings));
5281     }
5282     else {
5283         _XmVirtKeysLoadFallbackBindings (DISPLAY, &bindings);
5284     }
5285     XtFree (bindings);
5286 } /* END OF FUNCTION ProcessMotifBindings */
5287
5288
5289 /*************************************<->*************************************
5290  *
5291  *  void
5292  *  ParseWmFunctionArg (linePP, ix, wmFunc, ppArg, sClientName)
5293  *
5294  *
5295  *  Description:
5296  *  -----------
5297  *  Parse the function arguments for a window manager function.
5298  *
5299  *
5300  *  Inputs:
5301  *  ------
5302  *  linePP   = pointer to line buffer pointer (contains string arg).
5303  *  ix = window manager function index (returned by ParseWmFunction)
5304  *  pWmFunction = pointer to window manager function destination.
5305  *  ppArg = ptr to argument pointer.
5306  *  sClientName = string name of client 
5307  *
5308  *
5309  *  Outputs:
5310  *  -------
5311  *  *ppArg = arg to pass to window manager function when invoking.
5312  *  Return = true on succes, false on some kind of parse error
5313  *
5314  *
5315  *  Comments:
5316  *  --------
5317  *  functionTable (window manager function table) is indexed with ix
5318  *  to get parsing info.
5319  *
5320  *  This function may malloc memory for the returned arg.
5321  *
5322  *  The sClientName is needed for starting some hpterm-based push_recall
5323  *  clients. It needs to be passed into the action so the hpterm gets
5324  *  named appropriately.
5325  * 
5326  *************************************<->***********************************/
5327
5328 Boolean
5329 ParseWmFunctionArg (
5330                 unsigned char **linePP,
5331                 int ix, 
5332                 WmFunction wmFunc, 
5333                 void **ppArg,
5334                 String sClientName,
5335                 String sTitle)
5336 {
5337     unsigned char *lineP = *linePP;
5338     Boolean bValidArg = True;
5339     unsigned char *str = NULL;
5340
5341     /*
5342      * If this is (possibly) a string argument, put it 
5343      * in quotes so that it will be parsed properly.
5344      */
5345     if ((functionTable[ix].parseProc == ParseWmFuncStrArg) ||
5346         (functionTable[ix].parseProc == ParseWmFuncMaybeStrArg))
5347     {
5348         if (lineP && *lineP != '"')
5349         {
5350             /*
5351              * not in quotes, repackage it, escaping the appropriate
5352              * characters.
5353              */
5354             str = _DtWmParseMakeQuotedString (lineP);
5355             if (str)
5356             {
5357                 lineP = str;
5358             }
5359         }
5360     }
5361
5362     /* 
5363      * Apply the function argument parser.
5364      */
5365     if ((functionTable[ix].wmFunction != wmFunc) ||
5366         !(*(functionTable [ix].parseProc)) (&lineP, wmFunc, ppArg))
5367     {
5368         bValidArg = False;
5369     }
5370
5371     /*
5372      * Add the exec parms if this is an f.action
5373      */
5374     if ((wmFunc == F_Action) && ppArg && *ppArg)
5375     {
5376         WmActionArg *pAP = (WmActionArg *) *ppArg;
5377         int totLen = 0;
5378
5379         /*
5380          * allocate more than enough string space to copy 
5381          * strings and intervening spaces.
5382          */
5383         if (sClientName && *sClientName)
5384         {
5385             /* length of: "name=" + sClientName + NULL */
5386             totLen += 5 + strlen(sClientName) + 1;
5387         }
5388         if (sTitle && *sTitle)
5389         {
5390             /* length of: "," + "title=" + sTitle + NULL */
5391             totLen += 1 + 6 + strlen(sTitle) + 1;
5392         }
5393
5394         if (totLen > 0)
5395         {
5396             pAP->szExecParms = (String) XtMalloc (totLen);
5397             /* start with empty string */
5398             pAP->szExecParms[0] = '\0'; 
5399
5400             if (sClientName && *sClientName)
5401             {
5402                 strcat (pAP->szExecParms, "name=");
5403                 strcat (pAP->szExecParms, sClientName);
5404             }
5405             if (sTitle && *sTitle)
5406             {
5407                 if (pAP->szExecParms[0] != '\0')
5408                 {
5409                     strcat (pAP->szExecParms, ",");
5410                 }
5411                 strcat (pAP->szExecParms, "title=");
5412                 strcat (pAP->szExecParms, sTitle);
5413             }
5414         }
5415     }
5416
5417     if (str)
5418     {
5419         XtFree ((char *) str);
5420     }
5421
5422     return (bValidArg);
5423
5424 } /* END OF FUNCTION ParseWmFunctionArg */
5425
5426 \f
5427 /*************************************<->*************************************
5428  *
5429  *  SystemCmd (pchCmd)
5430  *
5431  *
5432  *  Description:
5433  *  -----------
5434  *  This function fiddles with our signal handling and calls the
5435  *  system() function to invoke a unix command.
5436  *
5437  *
5438  *  Inputs:
5439  *  ------
5440  *  pchCmd = string with the command we want to exec.
5441  *
5442  *  Outputs:
5443  *  -------
5444  *
5445  *
5446  *  Comments:
5447  *  --------
5448  *  The system() command is touchy about the SIGCLD behavior. Restore
5449  *  the default SIGCLD handler during the time we run system().
5450  * 
5451  *************************************<->***********************************/
5452
5453 void
5454 SystemCmd (char *pchCmd)
5455 {
5456     struct sigaction sa;
5457     struct sigaction osa;
5458
5459     (void) sigemptyset(&sa.sa_mask);
5460     sa.sa_flags = 0;
5461     sa.sa_handler = SIG_DFL;
5462
5463     (void) sigaction (SIGCHLD, &sa, &osa);
5464
5465     system (pchCmd);
5466
5467     (void) sigaction (SIGCHLD, &osa, (struct sigaction *) 0);
5468 }
5469
5470
5471 \f
5472 /*************************************<->*************************************
5473  *
5474  *  DeleteTempConfigFileIfAny ()
5475  *
5476  *
5477  *  Description:
5478  *  -----------
5479  *  This function deletes the temporary config file used to process 
5480  *  old dtwmrc syntax.
5481  *
5482  *
5483  *  Inputs:
5484  *  ------
5485  *
5486  *
5487  *  Outputs:
5488  *  -------
5489  *
5490  *
5491  *  Comments:
5492  *  --------
5493  * 
5494  *************************************<->***********************************/
5495
5496 void
5497 DeleteTempConfigFileIfAny (void)
5498 {
5499     char pchCmd[MAXWMPATH+1];
5500
5501     if (pConfigStackTop->tempName)
5502     {
5503         strcpy (pchCmd, "/bin/rm ");
5504         strcat (pchCmd, pConfigStackTop->tempName);
5505         SystemCmd (pchCmd);
5506         XtFree ((char *) pConfigStackTop->tempName);
5507         pConfigStackTop->tempName = NULL;
5508     }
5509     if (pConfigStackTop->cppName)
5510     {
5511         strcpy (pchCmd, "/bin/rm ");
5512         strcat (pchCmd, pConfigStackTop->cppName);
5513         SystemCmd (pchCmd);
5514         XtFree ((char *) pConfigStackTop->cppName);
5515         pConfigStackTop->cppName = NULL;
5516     }
5517 }
5518
5519 \f
5520 /*************************************<->*************************************
5521  *
5522  *  ParseIncludeSet (pSD, lineP)
5523  *
5524  *
5525  *  Description:
5526  *  -----------
5527  *
5528  *
5529  *  Inputs:
5530  *  ------
5531  *  cfileP = (global) file pointer to fopened configuration file or NULL
5532  *  lineP = pointer to line buffer
5533  *  line   = (global) line buffer
5534  *  linec  = (global) line count
5535  *  parseP = (global) parse string pointer if cfileP == NULL
5536  *  pSD->rootWindow = default root window of display
5537  * 
5538  *  Outputs:
5539  *  -------
5540  *  linec  = (global) line count incremented
5541  *  parseP = (global) parse string pointer if cfileP == NULL
5542  *
5543  *
5544  *  Comments:
5545  *  --------
5546  * 
5547  *************************************<->***********************************/
5548
5549 static void ParseIncludeSet (WmScreenData *pSD, unsigned char *lineP) 
5550 {
5551     unsigned char     *string;
5552     unsigned char     *pchName;
5553
5554     /*
5555      * Require leading '{' on the next line.
5556      */
5557
5558     while ((GetNextLine () != NULL))  /* not EOF nor read error */
5559     {
5560         lineP = line;
5561         ScanWhitespace(&lineP);
5562
5563         if ((lineP == NULL) || (*line == '!') || (*lineP == '\0') || (*lineP == '#'))
5564         /* ignore empty or comment line */
5565         {
5566             continue;
5567         }
5568
5569         if (*lineP == '{')
5570         /* found '{' */
5571         {
5572             break;
5573         }
5574
5575         /* not a '{' */
5576         PWarning (((char *)GETMESSAGE(60, 37, "Expected '{'")));
5577         return;
5578     }
5579
5580     /*
5581      * Found leading "{" or EOF.
5582      * Parse include files until "}" or EOF found.
5583      */
5584     while ((GetNextLine () != NULL))
5585     {
5586         lineP = line;
5587         if ((*line == '!') || (string = GetString (&lineP)) == NULL)
5588         /* ignore empty or comment lines */
5589         {
5590             continue;
5591         }
5592         if (*string == '}')  /* finished with set. */
5593         {
5594             break;
5595         }
5596         pchName = _DtWmParseFilenameExpand (string);
5597         if (pchName && ConfigStackPush (pchName))
5598         {
5599             ProcessWmFile (pSD, True /* nested */);
5600             ConfigStackPop ();
5601             XtFree ((char *) pchName);
5602         }
5603
5604     }
5605
5606 }
5607
5608
5609 \f
5610 /*************************************<->*************************************
5611  *
5612  *  ConfigStackInit (pchFileName)
5613  *
5614  *
5615  *  Description:
5616  *  -----------
5617  *  Initializes the config file processing stack
5618  *
5619  *  Inputs:
5620  *  ------
5621  *  pchFileName = name of new file to start parsing
5622  * 
5623  *  Outputs:
5624  *  -------
5625  *
5626  *
5627  *  Comments:
5628  *  --------
5629  * 
5630  *************************************<->***********************************/
5631
5632 static void ConfigStackInit (char *pchFileName)
5633 {
5634
5635     pConfigStack  = XtNew (ConfigFileStackEntry);
5636
5637     if (pConfigStack)
5638     {
5639         pConfigStackTop = pConfigStack;
5640         pConfigStackTop->fileName = XtNewString (pchFileName);
5641         pConfigStackTop->tempName = NULL;
5642         pConfigStackTop->cppName = NULL;
5643         pConfigStackTop->offset = 0;
5644         pConfigStackTop->pWmPB = wmGD.pWmPB;
5645         pConfigStackTop->wmgdConfigFile = wmGD.configFile;
5646         pConfigStackTop->pIncluder = NULL;
5647
5648     }
5649     else
5650     {
5651         sprintf ((char *)wmGD.tmpBuffer,
5652             (char *)GETMESSAGE(60,36,"Insufficient memory to process included file: %s"), 
5653             pchFileName);
5654         Warning ((char *)wmGD.tmpBuffer);
5655     }
5656 }
5657
5658 \f
5659 /*************************************<->*************************************
5660  *
5661  *  ConfigStackPush (pchFileName)
5662  *
5663  *
5664  *  Description:
5665  *  -----------
5666  *  Open an included config file
5667  *
5668  *  Inputs:
5669  *  ------
5670  *  pchFileName = name of new file to start parsing
5671  *  wmGD.pWmPB = global parse buffer (pickle this)
5672  * 
5673  *  Outputs:
5674  *  -------
5675  *  wmGD.pWmPB = global parse buffer (new one for new file)
5676  *  return = FILE * to open file or NULL
5677  *
5678  *
5679  *  Comments:
5680  *  --------
5681  * 
5682  *************************************<->***********************************/
5683
5684 static FILE *
5685 ConfigStackPush (unsigned char *pchFileName)
5686 {
5687     ConfigFileStackEntry *pEntry;
5688     FILE *pNewFile = NULL;
5689
5690     pEntry = XtNew (ConfigFileStackEntry);
5691     if (pEntry)
5692     {
5693         /* save off state of current config file */
5694         pConfigStackTop->offset = ftell (cfileP);
5695         pConfigStackTop->pWmPB = wmGD.pWmPB;
5696         fclose (cfileP);
5697
5698         /* set up state of new config file */
5699         pEntry->fileName = XtNewString ((char *)pchFileName);
5700         pEntry->tempName = NULL;
5701         pEntry->cppName = NULL;
5702         pEntry->wmgdConfigFile = (String) pEntry->fileName;
5703
5704         /* set globals for new config file */
5705         wmGD.pWmPB = _DtWmParseNewBuf ();
5706         wmGD.configFile = pEntry->wmgdConfigFile;
5707
5708         /* put new entry onto stack */
5709         pEntry->pIncluder = pConfigStackTop;
5710         pConfigStackTop = pEntry;
5711
5712         /* open the file */
5713         pNewFile = cfileP = FopenConfigFile();
5714
5715         if (!pNewFile)
5716         {
5717             /* file open failed! back out */
5718             ConfigStackPop ();
5719         }
5720     }
5721     else
5722     {
5723         sprintf ((char *)wmGD.tmpBuffer,
5724             (char *)GETMESSAGE(60,36,"Insufficient memory to process included file: %s"), 
5725             pchFileName);
5726         Warning ((char *)wmGD.tmpBuffer);
5727     }
5728
5729     return (pNewFile);
5730 }
5731
5732
5733 \f
5734 /*************************************<->*************************************
5735  *
5736  *  ConfigStackPop ()
5737  *
5738  *
5739  *  Description:
5740  *  -----------
5741  *
5742  *
5743  *  Inputs:
5744  *  ------
5745  *  pchFileName = name of new file to start parsing
5746  *  wmGD.pWmPB = global parse buffer (pickle this)
5747  * 
5748  *  Outputs:
5749  *  -------
5750  *  wmGD.pWmPB = global parse buffer (new one for new file)
5751  *
5752  *
5753  *  Comments:
5754  *  --------
5755  *  assumes cfileP is closed already
5756  * 
5757  *************************************<->***********************************/
5758
5759 static void ConfigStackPop (void)
5760 {
5761     ConfigFileStackEntry *pPrev;
5762     char pchCmd[MAXWMPATH+1];
5763
5764     if (pConfigStackTop != pConfigStack)
5765     {
5766         pPrev = pConfigStackTop->pIncluder;
5767
5768         _DtWmParseDestroyBuf (wmGD.pWmPB);
5769         if (pConfigStackTop->tempName)
5770         {
5771             XtFree (pConfigStackTop->tempName);
5772         }
5773         if (pConfigStackTop->cppName)
5774         {
5775             strcpy (pchCmd, "/bin/rm "); 
5776             strcat (pchCmd, pConfigStackTop->cppName); 
5777             SystemCmd (pchCmd);
5778             XtFree ((char *) pConfigStackTop->cppName);
5779             pConfigStackTop->cppName = NULL;
5780         }
5781         if (pConfigStackTop->fileName)
5782         {
5783             XtFree (pConfigStackTop->fileName);
5784         }
5785
5786         wmGD.pWmPB = pPrev->pWmPB;
5787         wmGD.configFile = pPrev->wmgdConfigFile;
5788         if (pPrev->tempName)
5789         {
5790             cfileP = fopen (pPrev->tempName, "r");
5791         }
5792         else if (pPrev->cppName)
5793         {
5794             cfileP = fopen (pPrev->cppName, "r");
5795         }
5796         else
5797         {
5798             cfileP = fopen (pPrev->fileName, "r");
5799         }
5800         if (cfileP) 
5801         {
5802             fseek (cfileP, pPrev->offset, 0);
5803         }
5804         else
5805         {
5806             char msg[MAXWMPATH+1];
5807
5808             sprintf(msg, ((char *)GETMESSAGE(60, 39, 
5809                             "Could not reopen configuration file %s")),
5810                 pPrev->fileName);
5811             Warning (msg);
5812         }
5813
5814         XtFree ((char *)pConfigStackTop);
5815         pConfigStackTop = pPrev;
5816     }
5817 }
5818
5819 \f
5820 /*************************************<->*************************************
5821  *
5822  *  ParseWmFuncActionArg (linePP, wmFunction, pArgs)
5823  *
5824  *
5825  *  Description:
5826  *  -----------
5827  *  Parses a window manager f.action argument
5828  *
5829  *
5830  *  Inputs:
5831  *  ------
5832  *  linePP   = pointer to current line buffer pointer.
5833  *  wmFunction = function for which the argument string is intended.
5834  *  pArgs = pointer to argument destination.
5835  *
5836  * 
5837  *  Outputs:
5838  *  -------
5839  *  linePP   = pointer to revised line buffer pointer.
5840  *  pArgs    = pointer to parsed argument.
5841  *  Return   = FALSE iff insufficient memory
5842  *
5843  *
5844  *  Comments:
5845  *  --------
5846  * 
5847  *************************************<->***********************************/
5848
5849 Boolean ParseWmFuncActionArg (unsigned char **linePP, 
5850                                   WmFunction wmFunction, String *pArgs)
5851 {
5852 #define WM_ACTION_ARG_INCREMENT 5
5853 #define WM_ACTION_ARG_PAD       256
5854     unsigned char *string;
5855     char *pch;
5856     WmActionArg *pAP;
5857     int iArgSz;
5858
5859     pAP = XtNew (WmActionArg);
5860     if (pAP && (string = GetString (linePP)) != NULL)
5861     {
5862         /* Got action name */
5863         pAP->actionName = XtNewString ((char *) string);
5864
5865         /* Get action arguments, if any */
5866         if ((pAP->aap = (DtActionArg *)
5867                 XtMalloc (WM_ACTION_ARG_INCREMENT * sizeof (DtActionArg))))
5868         {
5869             iArgSz = WM_ACTION_ARG_INCREMENT;
5870             pAP->numArgs = 0;
5871
5872             while ((string = GetString (linePP)) != NULL)
5873             {
5874                if ((pAP->aap[pAP->numArgs].u.file.name = (char *)
5875                        XtMalloc(1 + strlen((char *)string))))
5876                {
5877                    pAP->aap[pAP->numArgs].argClass = DtACTION_FILE;
5878
5879                    /* format the action argument */
5880                    pch = pAP->aap[pAP->numArgs].u.file.name;
5881
5882                    /* 
5883                     * Expand environment variable
5884                     */
5885                    if (string[0] == '$')
5886                    {
5887                        string = (unsigned char *) getenv ((char *)&string[1]);
5888                        if (!string)
5889                        {
5890                            break;
5891                        }
5892                        else
5893                        {
5894                            /*
5895                             * Make sure there's room for the new
5896                             * string.
5897                             */
5898                            pch = (char *) 
5899                              XtRealloc (pch, (1+strlen((char *)string)));
5900                            pAP->aap[pAP->numArgs].u.file.name = pch;
5901                        }
5902                    }
5903
5904                    /* !!! No host name processing is done!!! */
5905
5906                    strcpy (pch, (char *)string);
5907
5908                    pAP->numArgs++;
5909                    if (pAP->numArgs == iArgSz)
5910                    {
5911                        /* need to increase our array space */
5912                        iArgSz += WM_ACTION_ARG_INCREMENT;
5913                        pAP->aap = (DtActionArg *) 
5914                            XtRealloc((char *)pAP->aap,
5915                                      (iArgSz * sizeof (DtActionArg)));
5916                        if (!pAP->aap)
5917                        {
5918                            break; /* out of memory */
5919                        }
5920                    }
5921                }
5922                else
5923                {
5924                    break; /* out of memory */
5925                }
5926             }
5927
5928         }
5929         pAP->szExecParms = NULL;
5930         *pArgs = (String) pAP;
5931     }
5932     else
5933     /* NULL string argument */
5934     {
5935         *pArgs = NULL;
5936     }
5937
5938     return (TRUE);
5939
5940 } /* END OF FUNCTION ParseWmFuncActionArg */
5941
5942
5943 /*************************************<->*************************************
5944  *
5945  *  PreprocessConfigFile (pSD)
5946  *
5947  *
5948  *  Description:
5949  *  -----------
5950  *  This function runs the configuration file through the C
5951  *  preprocessor
5952  *
5953  *
5954  *  Inputs:
5955  *  ------
5956  *  pSD = ptr to screen data
5957  *
5958  *  Outputs:
5959  *  -------
5960  *
5961  *
5962  *  Comments:
5963  *  --------
5964  * 
5965  *************************************<->***********************************/
5966
5967 static void
5968 PreprocessConfigFile (void)
5969 {
5970 #define CPP_NAME_SIZE   ((L_tmpnam)+1)
5971     char pchCmd[MAXWMPATH+1];
5972
5973     if (wmGD.cppCommand && *wmGD.cppCommand)
5974     {
5975         /*
5976          * Generate a temp file name.
5977          */
5978         pConfigStackTop->cppName = XtMalloc (CPP_NAME_SIZE * sizeof(char));
5979         if (pConfigStackTop->cppName)
5980         {
5981             (void) tmpnam (pConfigStackTop->cppName);
5982
5983             /*
5984              * Build up the command line.
5985              */
5986             strcpy (pchCmd, wmGD.cppCommand);
5987             strcat (pchCmd, " ");
5988             strcat (pchCmd, pConfigStackTop->fileName);
5989             strcat (pchCmd, " ");
5990             strcat (pchCmd, pConfigStackTop->cppName);
5991
5992             /*
5993              * Run the config file through the converter program
5994              * and send the output to a temp file. 
5995              */
5996             SystemCmd (pchCmd);
5997         }
5998     }
5999 }
6000
6001 \f
6002 /*************************************<->*************************************
6003  *
6004  *  GetNetworkFileName (char *pchFile)
6005  *
6006  *
6007  *  Description:
6008  *  -----------
6009  *  This function returns a local representation for a network
6010  *  file. 
6011  *
6012  *
6013  *  Inputs:
6014  *  ------
6015  *  pchFile     - pointer to file name of form [<host>:]<path>
6016  *
6017  *  Return:
6018  *  -------
6019  *  String      - ptr to allocated string of local file name. If input
6020  *                is not a network file, the a copy of pchFile is returned.
6021  *
6022  *
6023  *  Comments:
6024  *  --------
6025  *  returned file name should be freed with XtFree().
6026  * 
6027  *************************************<->***********************************/
6028
6029 static String
6030 GetNetworkFileName (char *pchFile)
6031 {
6032     char *pch;
6033     char *host_part; 
6034     char *file_part; 
6035     char *netfile;
6036     char *sName = NULL;
6037     char *pchName = NULL;
6038     int count;
6039     char **pchTok;
6040     String sReturn = NULL;
6041     char *homeDir;
6042     int len;
6043
6044
6045     pch = strchr (pchFile, ':');
6046
6047     if (pch)
6048     {
6049         /*
6050          * Expand special chars and find matching file.
6051          */
6052         pchTok = (char **)shellscan (pchFile, &count, 0);
6053
6054         if ((count == 1) || (count == 2))
6055         {
6056             /* one match found */
6057             host_part = pchTok[0];
6058         }
6059         else if (count > 2)
6060         {
6061             /* several matches found, pick one */
6062             host_part = pchTok[1];
6063         }
6064         else
6065         {
6066             host_part = NULL;
6067         }
6068
6069         if (host_part != NULL)
6070         {
6071             pch = strchr (host_part, ':');
6072             if (pch)
6073             {
6074                 /* 
6075                  * copy the string so we don't munge data
6076                  * inside shellscan 
6077                  */
6078                 host_part = sName = XtNewString ((String) host_part);
6079                 pch = strchr (sName, ':');
6080
6081                 /*
6082                  * separate the host and file parts of the
6083                  * file name
6084                  */
6085                 *pch = '\0';
6086                 file_part = pch+1;
6087             }
6088             else
6089             {
6090                 /*
6091                  * The colon went away. Hmm...
6092                  */
6093                 file_part = host_part;
6094                 host_part = NULL;
6095             }
6096
6097             if ((mblen(file_part, MB_CUR_MAX) == 1) && 
6098                 (mblen(file_part+1, MB_CUR_MAX) == 1) &&
6099                 (*file_part == '~') &&
6100                 (*(file_part+1) == '/'))
6101             {
6102                 /*
6103                  * Replace '~' with $HOME
6104                  */
6105                 homeDir = XmeGetHomeDirName();
6106                 len = strlen (host_part) + 1 +
6107                       strlen (homeDir) + strlen (file_part) + 1;
6108                 pch = (char *) XtMalloc (len);
6109                 strcpy (pch, sName);
6110                 host_part = pch;
6111                 pch += strlen (pch) + 1;
6112                 strcpy (pch, homeDir);
6113                 strcat (pch, file_part+1);
6114                 file_part = pch;
6115                 XtFree (sName);
6116                 sName = host_part;
6117             }
6118         }
6119         else
6120         {
6121             /*
6122              * shellscan had a problem with the file name.
6123              * just operate on the name as-is.
6124              * temporarily replace ':' with a NULL 
6125              */
6126             host_part = sName = XtNewString ((String) pchFile);
6127             pch = strchr (sName, ':');
6128             *pch = '\0';
6129             host_part = pchFile;
6130             file_part = pch+1;
6131         }
6132
6133         /* convert to canonical host/file name */
6134         netfile = (char *) 
6135                     tt_host_file_netfile (host_part, file_part);
6136         if (tt_pointer_error (netfile) == TT_OK)
6137         {
6138             /* convert to local file name equivalent */
6139             pchName = tt_netfile_file (netfile);
6140
6141             if (tt_pointer_error (pchName) == TT_OK)
6142             {
6143                 sReturn = XtNewString ((String) pchName);
6144                 tt_free ((char *)pchName);
6145             }
6146             tt_free (netfile);
6147         }
6148
6149         if (sName)
6150         {
6151             XtFree ((char *)sName);
6152         }
6153     }
6154
6155     if (sReturn == NULL)
6156     {
6157         if ((mblen(pchFile, MB_CUR_MAX) == 1) && 
6158             (mblen(pchFile+1, MB_CUR_MAX) == 1) &&
6159             (*pchFile == '~') &&
6160             (*(pchFile+1) == '/'))
6161         {
6162             /*
6163              * Replace '~' with $HOME
6164              */
6165             homeDir = XmeGetHomeDirName();
6166             len = strlen (homeDir) + strlen (pchFile) + 1;
6167             sReturn = (char *) XtMalloc (len);
6168             strcpy (sReturn, homeDir);
6169             strcat (sReturn, pchFile+1);
6170         }
6171         else
6172         {
6173             sReturn = XtNewString ((String) pchFile);
6174         }
6175     }
6176
6177     return (sReturn);
6178 }
6179 /****************************   eof    ***************************/
6180