Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / programs / dtwm / WmParse.c
1 #ifdef VERBOSE_REV_INFO
2 static char rcsid[] = "$XConsortium: WmParse.c /main/5 1996/10/30 11:36:06 drk $";
3 #endif /* VERBOSE_REV_INFO */
4 /******************************<+>*************************************
5  **********************************************************************
6  **
7  **  File:        WmParse.c
8  **
9  **  Project:     HP/Motif Workspace Manager (dtwm)
10  **
11  **  Description:
12  **  -----------
13  **  This file contains the generic parsing routines
14  **
15  **
16  *********************************************************************
17  **
18  ** (c) Copyright 1987, 1988, 1989, 1990, 1991 HEWLETT-PACKARD COMPANY
19  ** ALL RIGHTS RESERVED
20  **
21  **********************************************************************
22  **********************************************************************
23  **
24  **
25  **********************************************************************
26  ******************************<+>*************************************/
27
28 /* ANSI C definitions,  This should be the first thing in WmGlobal.h */
29 #ifdef __STDC__
30 #define Const const
31 #else
32 #define Const /**/
33 #endif
34
35
36 /*
37  * Included Files:
38  */
39 #include <stdio.h>
40 #include <X11/Intrinsic.h>
41 #include "WmGlobal.h"
42 #include "WmParse.h"
43 #include "WmParseP.h"
44 #ifdef MULTIBYTE
45 #include <stdlib.h>
46 #endif /* MULTIBYTE */
47
48 /*
49  * Internal routines
50  */
51
52 static DtWmpParseBuf * _DtWmpIncBuf (
53     DtWmpParseBuf *pWmPB);
54
55
56
57 #ifndef MAXLINE
58 #define MAXLINE         (MAXWMPATH+1)
59 #endif
60 #define MAX_QUOTE_DEPTH 10
61
62 /*
63  *  This flags non-OSF code in those sections that were lifted
64  *  from mwm.
65  */
66 #define PARSE_LIB
67
68
69 /*
70  * Defines used to maintain code similarity between OSF/mwm source
71  * routines and these routines.
72  */
73 #define cfileP                  ((pWmPB)->pFile)
74 #define linec                   ((pWmPB)->lineNumber)
75 #define line                    ((pWmPB)->pchLine)
76 #define parseP                  ((pWmPB)->pchNext)
77 #define ScanWhitespace(s)       (_DtWmParseSkipWhitespaceC(s))
78 #define PeekAhead(s,l)          (_DtWmParsePeekAhead(s,l))
79
80 \f
81 /*************************************<->*************************************
82  *
83  *  _DtWmParseSkipWhitespace(pWmPB)
84  *
85  *
86  *  Description:
87  *  -----------
88  *  Scan the current string, skipping over all white space characters.
89  *
90  *
91  *  Inputs:
92  *  ------
93  *  pWmPB = ptr to parse buffer
94  *
95  * 
96  *  Outputs:
97  *  -------
98  *  pWmPB = parse buffer modified; current line ptr may be moved.
99  *
100  *
101  *  Comments:
102  *  --------
103  *  Assumes there's a current line in the parse buffer
104  * 
105  *************************************<->***********************************/
106
107 void _DtWmParseSkipWhitespace(DtWmpParseBuf *pWmPB)
108 {
109     _DtWmParseSkipWhitespaceC (&(pWmPB->pchNext));
110
111 } /* END OF FUNCTION _DtWmParseSkipWhitespace */
112
113 \f
114 /*************************************<->*************************************
115  *
116  *  _DtWmParseNextToken (pWmPB)
117  *
118  *
119  *  Description:
120  *  -----------
121  *  Returns the next quoted or whitespace-terminated nonquoted string in the
122  *  current line buffer.
123  *
124  *
125  *  Inputs:
126  *  ------
127  *  pWmPB       = ptr to parse buffer
128  *
129  * 
130  *  Outputs:
131  *  -------
132  *  Return      = ptr to null terminated string.
133  *  pWmPB       = current line modified internally.
134  *
135  *
136  *  Comments:
137  *  --------
138  *  May alter the line buffer contents.
139  *  Handles quoted strings and characters, removing trailing whitespace from
140  *  quoted strings.
141  *  Returns NULL string if the line is empty or is a comment.
142  *  Does not use session manager style algorithm for dealing with
143  *  quoted strings.
144  * 
145  *************************************<->***********************************/
146
147 unsigned char *
148 _DtWmParseNextToken (
149     DtWmpParseBuf *pWmPB 
150     )
151 {
152     return (_DtWmParseNextTokenC (&(pWmPB->pchNext), False));
153 }
154
155 \f
156 /*************************************<->*************************************
157  *
158  *  _DtWmParseNextTokenExpand (pWmPB)
159  *
160  *
161  *  Description:
162  *  -----------
163  *  Returns the next quoted or whitespace-terminated nonquoted string in the
164  *  current line buffer. Environment variables found in the are expanded.
165  *  Characters quoted by '\' are passed through unaffected with the 
166  *  quoting '\' removed.
167  *
168  *
169  *  Inputs:
170  *  ------
171  *  pWmPB       = ptr to parse buffer
172  *
173  * 
174  *  Outputs:
175  *  -------
176  *  Return      = ptr to null terminated string.
177  *                Free this string with XtFree().
178  *  pWmPB       = current line modified internally.
179  *
180  *
181  *  Comments:
182  *  --------
183  *  May alter the line buffer contents.
184  *  Handles quoted strings and characters, removing trailing whitespace from
185  *  quoted strings.
186  *  Returns NULL string if the line is empty or is a comment.
187  * 
188  *************************************<->***********************************/
189
190 unsigned char *
191 _DtWmParseNextTokenExpand (
192     DtWmpParseBuf *pWmPB 
193     )
194 {
195     unsigned char *pch;
196     unsigned char *pchReturn = NULL;
197
198     /* isolate the next token */
199     pch = _DtWmParseNextTokenC (&(pWmPB->pchNext), False);
200
201     /* expand environment variables, a copy of the string is returned */
202     pchReturn = _DtWmParseExpandEnvironmentVariables (pch, NULL);
203
204     /* 
205      * If a token was found, but no copy returned, there were no 
206      * environment variables. This routine needs to return a copy, 
207      * so make one now.
208      */
209     if (pch && !pchReturn) 
210         pchReturn = (unsigned char *) XtNewString ((String) pch);
211
212     return (pchReturn);
213 }
214
215 \f
216 /*************************************<->*************************************
217  *
218  *  _DtWmParseBackUp (pWmPB, pchTok)
219  *
220  *
221  *  Description:
222  *  -----------
223  *  Backs up to the previous token (the one before pchTok) 
224  *
225  *
226  *  Inputs:
227  *  ------
228  *  pWmPB       = ptr to parse buffer
229  *  pchTok      = ptr to a token in the parse buffer
230  *
231  * 
232  *  Outputs:
233  *  -------
234  *  Returns     = ptr to prev token
235  *
236  *
237  *  Comments:
238  *  --------
239  *  Operates on the line buffer in the pWmPB structure. Backs up
240  *  the next pointer and writes a space over the interpolated
241  *  NULL (if any).
242  * 
243  *************************************<->***********************************/
244
245 unsigned char *
246 _DtWmParseBackUp (
247     DtWmpParseBuf *pWmPB,
248     unsigned char *pchTok
249     )
250 {
251     if ((pchTok > pWmPB->pchLine) &&
252         (pchTok < (pWmPB->pchLine +  pWmPB->cLineSize)))
253     {
254 #ifdef MULTIBYTE
255         unsigned char *pch;
256         unsigned char *pchLast;
257         int chlen;
258
259         pch = pchLast = pWmPB->pchLine;
260
261         /*
262          * Search from beginning (because of multibyte chars) to
263          * find the token before the string we're interested in.
264          */
265         while ((pch < pchTok))
266         {
267             chlen = mblen ((char *)pch, MB_CUR_MAX);
268             if (*pch == '\0')
269             {
270                 pch++;
271                 if (pch == pchTok)
272                 {
273                     /*
274                      * Found the NULL preceding the string passed in!
275                      * Replace it with a blank and return the previous
276                      * token (pointed to by pchLast).
277                      */
278                     *(pch - 1) = DTWM_CHAR_SPACE;
279                     break;
280                 }
281                 else
282                 {
283                     /* 
284                      * Remember the beginning of this token.
285                      */
286                     pchLast = pch;
287                 }
288             }
289             else if (chlen < 1)
290             {
291                 break;
292             }
293             else
294             {
295                 pch += chlen;
296             }
297         }
298
299         pWmPB->pchNext = pchLast;
300 #else /* MULTIBYTE */
301
302         /* 
303          * Replace preceding NULL with a space.
304          */
305         pchTok--;
306
307         if (*pchTok == '\0')
308         {
309             *pchTok = DTWM_CHAR_SPACE;
310         }
311
312         /* 
313          * Back up to next NULL or beginning of line.
314          */
315         while ((pchTok >= pWmPB->pchLine) && *pchTok)
316         {
317             pchTok--;
318         }
319
320         pWmPB->pchNext = pchTok + 1;
321
322 #endif  /* MULTIBYTE */
323     }
324     return (pWmPB->pchNext);
325 }
326
327
328 \f
329 /*************************************<->*************************************
330  *
331  *  _DtWmParseSkipWhitespaceC(linePP)
332  *
333  *
334  *  Description:
335  *  -----------
336  *  Scan the string, skipping over all white space characters.
337  *
338  *
339  *  Inputs:
340  *  ------
341  *  linePP = nonNULL pointer to current line buffer pointer
342  *
343  * 
344  *  Outputs:
345  *  -------
346  *  linePP = nonNULL pointer to revised line buffer pointer
347  *
348  *
349  *  Comments:
350  *  --------
351  *  Assumes linePP is nonNULL
352  * 
353  *************************************<->***********************************/
354
355 void _DtWmParseSkipWhitespaceC(unsigned char  **linePP)
356 {
357 #ifdef MULTIBYTE
358     while (*linePP && 
359            (mblen ((char *)*linePP, MB_CUR_MAX) == 1) && 
360            isspace (**linePP))
361 #else
362     while (*linePP && isspace (**linePP))
363 #endif
364     {
365         (*linePP)++;
366     }
367
368 } /* END OF FUNCTION _DtWmParseSkipWhitespaceC */
369
370
371 \f
372 /*************************************<->*************************************
373  *
374  *  _DtWmParseNextTokenC (linePP, SmBehavior)
375  *
376  *
377  *  Description:
378  *  -----------
379  *  Returns the next quoted or whitespace-terminated nonquoted string in the
380  *  line buffer.
381  *  Additional functionality added to GetString in that anything in a
382  *  quoted string is considered sacred and nothing will be stripped from
383  *  the middle of a quoted string.
384  *
385  *
386  *  Inputs:
387  *  ------
388  *  linePP =  pointer to current line buffer pointer.
389  *  SmBehavior = flag that enables parsing session manager hints  if True.
390  *               If False, this behaves as the normal OSF mwm GetString
391  *               routine.
392  *
393  * 
394  *  Outputs:
395  *  -------
396  *  linePP =  pointer to revised line buffer pointer.
397  *  Return =  string 
398  *
399  *
400  *  Comments:
401  *  --------
402  *  May alter the line buffer contents.
403  *  Handles quoted strings and characters, removing trailing whitespace from
404  *  quoted strings.
405  *  Returns NULL string if the line is empty or is a comment.
406  *  Code stolen from dtmwm.
407  * 
408  *************************************<->***********************************/
409
410 unsigned char *
411 _DtWmParseNextTokenC (
412     unsigned char **linePP, 
413     Boolean SmBehavior 
414     )
415 {
416 /***********************************************************************
417  *
418  * The following code is duplicated from WmResParse.c (GetStringC)
419  * GetStringC is the HP DT version of GetString. 
420  *
421  * It works here through the magic of #defines.
422  *
423  ***********************************************************************/
424     unsigned char *lineP = *linePP;
425     unsigned char *endP;
426     unsigned char *curP;
427     unsigned char *lnwsP;
428     unsigned int  level = 0, checkLev, i, quoteLevel[MAX_QUOTE_DEPTH];
429 #ifdef MULTIBYTE
430     int            chlen;
431
432     /* get rid of leading white space */
433     ScanWhitespace (&lineP);
434
435     /*
436      * Return NULL if line is empty, whitespace, or begins with a comment.
437      */
438     if (
439         *lineP == '\0' ||
440         ((chlen = mblen ((char *)lineP, MB_CUR_MAX)) < 1) ||
441         ((chlen == 1) && ((*lineP == '!') || 
442                           ((!SmBehavior) && (*lineP == '#'))))
443        )
444     {
445         *linePP = lineP;
446         return (NULL);
447     }
448
449     if ((chlen == 1) && (*lineP == '"'))
450     /* Quoted string */
451     {
452         quoteLevel[level] = 1;  
453         /*
454          * Start beyond double quote and find the end of the quoted string.
455          * '\' quotes the next character.
456          * Otherwise,  matching double quote or NULL terminates the string.
457          *
458          * We use lnwsP to point to the last non-whitespace character in the
459          * quoted string.  When we have found the end of the quoted string,
460          * increment lnwsP and if lnwsP < endP, write NULL into *lnwsP.
461          * This removes any trailing whitespace without overwriting the 
462          * matching quote, needed later.  If the quoted string was all 
463          * whitespace, then this will write a NULL at the beginning of the 
464          * string that will be returned -- OK.
465          */
466         lnwsP = lineP++;                /* lnwsP points to first '"' */
467         curP = endP = lineP;            /* other pointers point beyond */
468
469         while ((*endP = *curP) &&
470                ((chlen = mblen ((char *)curP, MB_CUR_MAX)) > 0) &&
471                ((chlen > 1) || (*curP != '"')))
472         /* Haven't found matching quote yet.
473          * First byte of next character has been copied to endP.
474          */
475         {
476             curP++;
477             if ((chlen == 1) && (*endP == '\\') && 
478                 ((chlen = mblen ((char *)curP, MB_CUR_MAX)) > 0))
479             /* character quote:
480              * copy first byte of quoted nonNULL character down.
481              * point curP to next byte
482              */
483             {
484                 if (SmBehavior)
485                 {
486                     /*
487                      * Check to see if this is a quoted quote - if it is
488                      * strip off a level - if not - it's sacred leave it alone
489                      */
490                     checkLev = PeekAhead((curP - 1), quoteLevel[level]);
491                     if(checkLev > 0)
492                     {
493                         if(quoteLevel[level] >= checkLev)
494                         {
495                             if (level > 0) level--;
496                         }
497                         else if (level < MAX_QUOTE_DEPTH)
498                         {
499                             level++;
500                             quoteLevel[level] = checkLev;
501                         }
502                         
503                         for(i = 0;i < (checkLev - 2);i++)
504                         {
505                             *endP++ = *curP++;curP++;
506                         }
507                         *endP = *curP++;
508                     }
509                 }
510                 else 
511                 {
512                 *endP = *curP++;
513                 }
514             }
515
516             if (chlen == 1)
517             /* Singlebyte character:  character copy finished. */
518             {
519                 if (isspace (*endP))
520                 /* whitespace character:  leave lnwsP unchanged. */
521                 {
522                     endP++;
523                 }
524                 else
525                 /* non-whitespace character:  point lnwsP to it. */
526                 {
527                     lnwsP = endP++;
528                 }
529             }
530             else if (chlen > 1)
531             /* Multibyte (nonwhitespace) character:  point lnwsP to it.
532              * Finish character byte copy.
533              */
534             {
535                 lnwsP = endP++;
536                 while (--chlen)
537                 {
538                     *endP++ = *curP++;
539                     lnwsP++;
540                 }
541             }
542         }
543 #else /* MULTIBYTE */
544
545     /* get rid of leading white space */
546     ScanWhitespace (&lineP);
547
548     /* Return NULL if line is empty, whitespace, or begins with a comment. */
549     if ((lineP == NULL || *lineP == '\0') ||
550         (!SmBehavior && (*lineP == '#')))
551     {
552         *linePP = lineP;
553         return (NULL);
554     }
555
556     if (*lineP == '"')
557     /* Quoted string */
558     {
559         quoteLevel[level] = 1;  
560         /*
561          * Start beyond double quote and find the end of the quoted string.
562          * '\' quotes the next character.
563          * Otherwise,  matching double quote or NULL terminates the string.
564          *
565          * We use lnwsP to point to the last non-whitespace character in the
566          * quoted string.  When we have found the end of the quoted string,
567          * increment lnwsP and if lnwsP < endP, write NULL into *lnwsP.
568          * This removes any trailing whitespace without overwriting the 
569          * matching quote, needed later.  If the quoted string was all 
570          * whitespace, then this will write a NULL at the beginning of the 
571          * string that will be returned -- OK.
572          */
573         lnwsP = lineP++;                /* lnwsP points to first '"' */
574         curP = endP = lineP;            /* other pointers point beyond */
575
576         while ((*endP = *curP) && (*endP != '"'))
577         /* haven't found matching quote yet */
578         {
579             /* point curP to next character */
580             curP++;
581             if ((*endP == '\\') && (*curP != NULL))
582             /* shift quoted nonNULL character down and curP ahead */
583             {
584                 if (SmBehavior)
585                 {
586                     /*
587                      * Check to see if this is a quoted quote - if it is
588                      * strip off a level - if not - it's sacred leave it alone
589                      */
590                     checkLev = PeekAhead((curP - 1), quoteLevel[level]);
591                     if(checkLev > 0)
592                     {
593                         if(quoteLevel[level] >= checkLev)
594                         {
595                             if (level > 0) level--;
596                         }
597                         else if (level < MAX_QUOTE_DEPTH)
598                         {
599                             level++;
600                             quoteLevel[level] = checkLev;
601                         }
602                         
603                         for(i = 0;i < (checkLev - 2);i++)
604                         {
605                             *endP++ = *curP++;curP++;
606                         }
607                         *endP = *curP++;
608                     }
609                 }
610                 else 
611                 {
612                 *endP = *curP++;
613                 }
614             }
615             if (isspace (*endP))
616             /* whitespace character:  leave lnwsP unchanged. */
617             {
618                 endP++;
619             }
620             else
621             /* non-whitespace character:  point lnwsP to it. */
622             {
623                 lnwsP = endP++;
624             }
625         }
626 #endif /* MULTIBYTE */
627
628         /*
629          *  Found matching quote or NULL.  
630          *  NULL out any trailing whitespace.
631          */
632
633         lnwsP++;
634         if (lnwsP < endP)
635         {
636             *lnwsP = '\0';
637         }
638     }
639
640     else
641     /* Unquoted string */
642     {
643         /* 
644          * Find the end of the nonquoted string.
645          * '\' quotes the next character.
646          * Otherwise,  whitespace, NULL, or '#' terminates the string.
647          */
648         curP = endP = lineP;
649
650 #ifdef MULTIBYTE
651         while ((*endP = *curP) &&
652                ((chlen = mblen ((char *)curP, MB_CUR_MAX)) > 0) &&
653                ((chlen > 1) || (!isspace (*curP) && 
654                                 (SmBehavior || (*curP != '#')))))
655         /* Haven't found whitespace or '#' yet.
656          * First byte of next character has been copied to endP.
657          */
658         {
659             curP++;
660             if ((chlen == 1) && (*endP == '\\') && 
661                 ((chlen = mblen ((char *)curP, MB_CUR_MAX)) > 0))
662             /* character quote:
663              * copy first byte of quoted nonNULL character down.
664              * point curP to next byte
665              */
666             {
667                 *endP = *curP++;
668             }
669             endP++;
670             if (chlen > 1)
671             /* Multibyte character:  finish character copy. */
672             {
673                 while (--chlen)
674                 {
675                     *endP++ = *curP++;
676                 }
677             }
678         }
679 #else /* MULTIBYTE */
680         while ((*endP = *curP) && !isspace (*endP) && 
681                                         (SmBehavior || (*endP != '#')))
682         {
683             /* point curP to next character */
684             curP++;
685             if ((*endP == '\\') && (*curP != '\0'))
686             /* shift quoted nonNULL character down and curP ahead */
687             {
688                 *endP = *curP++;
689             }
690             endP++;
691         }
692 #endif /* MULTIBYTE */
693     }
694
695     /*
696      * Three cases for *endP:
697      *   '#' --> write NULL over # and point to NULL
698      *   whitespace or
699      *     matching quote -> write NULL over char and point beyond
700      *   NULL -> point to NULL 
701      */
702
703     if (!SmBehavior && (*endP == '#'))
704     {
705         *endP = '\0';       /* write NULL over '#' */
706         *linePP = endP;     /* point to NULL */
707     }
708     else if (*endP != '\0')
709     {
710         *endP = '\0';       /* write NULL over terminator */
711         *linePP = ++curP;   /* point beyond terminator */
712     }
713     else
714     {
715         *linePP = endP;
716     }
717     return ((unsigned char *)lineP);
718
719 } /* END OF FUNCTION _DtWmParseNextTokenC */
720
721
722 \f
723 /*************************************<->*************************************
724  *
725  *  (DtWmParseBuf *) _DtWmParseNewBuf (void)
726  *
727  *
728  *  Description:
729  *  -----------
730  *  Allocates a new parse record for parsing. 
731  *
732  *  Inputs:
733  *  ------
734  *  none 
735  *
736  * 
737  *  Outputs:
738  *  -------
739  *  Return      = ptr to parse buffer record, NULL if memory allocation
740  *                error.
741  *
742  *
743  *  Comments:
744  *  --------
745  *  Call this first before using the other DtWmp routines that require
746  *  a parse buffer. Treat this as an opaque type; use the provided 
747  *  routines to create, access, and destroy this structure.
748  * 
749  *************************************<->***********************************/
750
751 DtWmpParseBuf * 
752 _DtWmParseNewBuf ( void )
753
754 {
755     DtWmpParseBuf *pWmPB;
756
757     pWmPB = (DtWmpParseBuf *) XtMalloc (sizeof (DtWmpParseBuf));
758     if (pWmPB)
759     {
760         pWmPB->pchLine = (unsigned char *) XtMalloc (MAXLINE+1);
761         if (!pWmPB->pchLine)
762         {
763             XtFree ((char *)pWmPB);
764             pWmPB = NULL;
765         }
766     }
767
768     if (pWmPB)
769     {
770         pWmPB->lineNumber = 0;
771         pWmPB->pchNext = pWmPB->pchLine;
772         pWmPB->cLineSize = MAXLINE+1;
773         pWmPB->pFile = NULL;
774         *(pWmPB->pchLine) = '\0';
775     }
776
777     return (pWmPB);
778
779 } /* END OF FUNCTION _DtWmParseNewBuf */
780
781 \f
782 /*************************************<->*************************************
783  *
784  *  (DtWmParseBuf *) _DtWmpIncBuf (pWmPB)
785  *
786  *
787  *  Description:
788  *  -----------
789  *  Increases the size of the line buffer in the parse buffer
790  *
791  *  Inputs:
792  *  ------
793  *  pWmPB       = pointer to a parse buffer
794  *
795  * 
796  *  Outputs:
797  *  -------
798  *  Return      = ptr to parse buffer record, NULL if memory allocation
799  *                error.
800  *
801  *
802  *  Comments:
803  *  --------
804  * 
805  *************************************<->***********************************/
806
807 static DtWmpParseBuf * 
808 _DtWmpIncBuf (
809     DtWmpParseBuf *pWmPB)
810
811 {
812     if (pWmPB)
813     {
814         int ix;
815
816         if (pWmPB->pFile)
817         {
818             /* save index into old string */
819             ix = pWmPB->pchNext - pWmPB->pchLine;
820         }
821
822         pWmPB->pchLine = (unsigned char *) 
823             XtRealloc ((char *)pWmPB->pchLine, (pWmPB->cLineSize + MAXLINE));
824         if (pWmPB->pchLine)
825         {
826             pWmPB->cLineSize += MAXLINE;
827
828             if (pWmPB->pFile)
829             {
830                 /* restore index into new string */
831                 pWmPB->pchNext = pWmPB->pchLine + ix;
832             }
833         }
834     }
835
836     return (pWmPB);
837
838 } /* END OF FUNCTION _DtWmpIncBuf */
839
840 \f
841 /*************************************<->*************************************
842  *
843  *  _DtWmParseDestroyBuf (pWmPB)
844  *
845  *
846  *  Description:
847  *  -----------
848  *  Destroys a parse buffer record, freeing any allocated memory.
849  *
850  *
851  *  Inputs:
852  *  ------
853  *  pWmPB       = ptr to previously allocated parse buffer
854  *
855  * 
856  *  Outputs:
857  *  -------
858  *  none
859  *
860  *
861  *  Comments:
862  *  --------
863  *  Destroys parse buffers allocated by _DtWmParseNewBuf.
864  * 
865  *************************************<->***********************************/
866
867 void 
868 _DtWmParseDestroyBuf (
869     DtWmpParseBuf *pWmPB
870     )
871
872 {
873     if (pWmPB)
874     {
875         if (pWmPB->pchLine)
876         {
877             XtFree ((char *) pWmPB->pchLine);
878         }
879         XtFree ((char *) pWmPB);
880     }
881
882 } /* END OF FUNCTION _DtWmParseDestroyBuf */
883
884 \f
885 /*************************************<->*************************************
886  *
887  *  (unsigned char *) _DtWmParseSetLine (pWmPB, pch)
888  *
889  *
890  *  Description:
891  *  -----------
892  *  Sets a line into the parse buffer structure. This is used in cases
893  *  where parsing of an embedded string, usually a default, is done
894  *  instead of parsing out of a file.
895  *
896  *
897  *  Inputs:
898  *  ------
899  *  pWmPB       = previously allocated parse buffer 
900  *  pch         = ptr to unsigned char string (zero terminated)
901  * 
902  *  Outputs:
903  *  -------
904  *  Return      = pch
905  *
906  *
907  *  Comments:
908  *  --------
909  *  This resets any previous setting of the file pointer. EOF wil be 
910  *  returned when the string pointed to by pch is exhausted.
911  *
912  *  Resets line number count.
913  * 
914  *************************************<->***********************************/
915
916 void 
917 _DtWmParseSetLine (
918     DtWmpParseBuf  *pWmPB,
919     unsigned char  *pch
920     )
921
922 {
923     if (pWmPB)
924     {
925         pWmPB->pchLine = pch;
926         pWmPB->pchNext = pWmPB->pchLine;
927         pWmPB->pFile = NULL;
928         pWmPB->lineNumber = 0;
929     }
930
931 } /* END OF FUNCTION _DtWmParseSetLine */
932
933 \f
934 /*************************************<->*************************************
935  *
936  *  (FILE *) _DtWmParseSetFile (pWmPB, pFile)
937  *
938  *
939  *  Description:
940  *  -----------
941  *  Sets the file pointer in a parse buffer. This is used when parsing 
942  *  from a file is required. 
943  *
944  *
945  *  Inputs:
946  *  ------
947  *  pWmPB       = pointer to a parse buffer
948  *  pFile       = pointer to an opened FILE
949  * 
950  *  Outputs:
951  *  -------
952  *  Return      = pFile
953  *
954  *
955  *  Comments:
956  *  --------
957  *  You fopen the file first, then pass in the FILE * returned to this
958  *  routine.
959  *
960  *  Resets line number count.
961  * 
962  *************************************<->***********************************/
963
964 void 
965 _DtWmParseSetFile (
966     DtWmpParseBuf *pWmPB,
967     FILE *pFile
968     )
969
970 {
971     if (pWmPB)
972     {
973         pWmPB->pchLine[0] = '\0';
974         pWmPB->pchNext = NULL;
975         pWmPB->pFile = pFile;
976         pWmPB->lineNumber = 0;
977     }
978
979 } /* END OF FUNCTION _DtWmParseSetFile */
980
981 \f
982 /*************************************<->*************************************
983  *
984  *  (unsigned char *) _DtWmParseNextLine ( pWmPB )
985  *
986  *
987  *  Description:
988  *  -----------
989  *  Returns a pointer to the next line to parse.
990  *
991  *
992  *  Inputs:
993  *  ------
994  *  pWmPB       = pointer to a parse buffer
995  *
996  * 
997  *  Outputs:
998  *  -------
999  *  Return      = pointer to next line to parse or NULL on EOF.
1000  *
1001  *
1002  *  Comments:
1003  *  --------
1004  *  
1005  * 
1006  *************************************<->***********************************/
1007
1008 unsigned char * 
1009 _DtWmParseNextLine (
1010     DtWmpParseBuf *pWmPB
1011     )
1012
1013 {
1014 /***********************************************************************
1015  *
1016  * The following code is duplicated from WmResParse.c (GetNextLine)
1017  * It works here through the magic of #defines.
1018  *
1019  ***********************************************************************/
1020
1021     register unsigned char      *string;
1022     int                         len;
1023
1024 #ifdef MULTIBYTE
1025     int   chlen;
1026     wchar_t last;
1027     wchar_t wdelim;
1028     char delim;
1029     int lastlen;
1030 #endif
1031
1032     if (cfileP != NULL)
1033     /* read fopened file */
1034     {
1035         if ((string = (unsigned char *) 
1036                       fgets ((char *)line, MAXLINE, cfileP)) != NULL)
1037         {
1038 #ifdef PARSE_LIB
1039             if (strlen((char *)string) > (size_t)pWmPB->cLineSize)
1040             {
1041                 /*
1042                  * Bump size of destination buffer
1043                  */
1044                 pWmPB->cLineSize = 1 + strlen((char *)string);
1045                 pWmPB->pchLine = (unsigned char *) 
1046                     XtRealloc ((char *)pWmPB->pchLine, 
1047                                (pWmPB->cLineSize));
1048             }
1049 #endif /* PARSE_LIB */
1050 #ifndef NO_MULTIBYTE
1051
1052             lastlen = 0;
1053             while (*string &&
1054                    ((len = mblen((char *)string, MB_CUR_MAX)) > 0))
1055             {
1056                 mbtowc(&last, (char *)string, MB_CUR_MAX);
1057                 lastlen = len;
1058                 string += len;
1059             }
1060             delim = '\\';
1061             mbtowc(&wdelim, &delim, MB_CUR_MAX);
1062             if (lastlen == 1 && last == wdelim)
1063             {
1064                 do
1065                 {
1066                     if (!fgets((char *)string, MAXLINE - (string - line), cfileP))
1067                         break;
1068
1069                     lastlen = 0;
1070                     while (*string &&
1071                            ((len = mblen((char *)string, MB_CUR_MAX)) > 0))
1072                     {
1073                         mbtowc(&last, (char *)string, MB_CUR_MAX);
1074                         lastlen = len;
1075                         string += len;
1076                     }
1077                     linec++;
1078                 }
1079                 while (lastlen == 1 && last == wdelim);
1080             }
1081             string = line;
1082 #else
1083             len = strlen((char *)string) - 2;
1084             if ((len > 0) && string[len] == '\\')
1085             {
1086                 do {
1087                     string = &string[len];
1088                     if (fgets((char *)string, 
1089                               MAXLINE - (string-line), cfileP) == NULL)
1090                        break;
1091                     len = strlen((char *)string) - 2;
1092                     linec++;
1093                 } while ((len >= 0) && string[len] == '\\');
1094                 string = line;
1095             }
1096 #endif
1097         }
1098     }
1099     else if ((parseP != NULL) && (*parseP != '\0'))
1100     /* read parse string */
1101     {
1102 #ifdef PARSE_LIB
1103         if (strlen((char *)parseP) > (size_t)pWmPB->cLineSize)
1104         {
1105             /*
1106              * Bump size of destination buffer
1107              */
1108             pWmPB->cLineSize = 1 + strlen((char *)parseP);
1109             pWmPB->pchLine = (unsigned char *) 
1110                 XtRealloc ((char *)pWmPB->pchLine, 
1111                            (pWmPB->cLineSize));
1112         }
1113 #endif /* PARSE_LIB */
1114         string = line;
1115 #ifdef MULTIBYTE
1116         while ((*parseP != '\0') &&
1117                ((chlen = mblen ((char *)parseP, MB_CUR_MAX)) != 0) &&
1118                (*parseP != '\n'))
1119         /* copy all but end-of-line and newlines to line buffer */
1120         {
1121             if (chlen == -1)
1122                *(parseP)++;
1123             else
1124             {
1125                 while (chlen--)
1126                 {
1127                    *(string++) = *(parseP++);
1128                 }
1129             }
1130         }
1131 #else
1132         while ((*parseP != '\0') && (*parseP != '\n'))
1133         /* copy all but end-of-line and newlines to line buffer */
1134         {
1135             *(string++) = *(parseP++);
1136         }
1137 #endif
1138         *string = '\0';
1139         if (*parseP == '\n')
1140         {
1141             parseP++;
1142         }
1143     }
1144     else
1145     {
1146         string = NULL;
1147     }
1148
1149     linec++;
1150 #ifdef PARSE_LIB
1151     if (cfileP)
1152     {
1153         /* update pchNext to get next line */
1154         pWmPB->pchNext = string;
1155     }
1156 #endif /* PARSE_LIB */
1157     return (string);
1158
1159
1160 } /* END OF FUNCTION _DtWmParseNextLine */
1161
1162 \f
1163 /*************************************<->*************************************
1164  *
1165  *  (unsigned char *) _DtWmParseCurrentChar (pWmPB)
1166  *
1167  *
1168  *  Description:
1169  *  -----------
1170  *  Returns a pointer to the rest of the current line.
1171  *
1172  *
1173  *  Inputs:
1174  *  ------
1175  *  pWmPB       = pointer to a parse buffer
1176  *
1177  * 
1178  *  Outputs:
1179  *  -------
1180  *  Return      = pointer to the rest of the current line
1181  *
1182  *
1183  *  Comments:
1184  *  --------
1185  *  Useful in cases where you want to look at a char before getting the
1186  *  next token or if you want to treat the rest of the line as a
1187  *  single token.
1188  * 
1189  *************************************<->***********************************/
1190
1191 unsigned char * 
1192 _DtWmParseCurrentChar (
1193     DtWmpParseBuf *pWmPB
1194     )
1195
1196 {
1197     return (pWmPB ? pWmPB->pchNext : (unsigned char *)NULL);
1198
1199 } /* END OF FUNCTION _DtWmParseCurrentChar */
1200
1201
1202 \f
1203 /*************************************<->*************************************
1204  *
1205  *  (unsigned char *) _DtWmParseNextChar (pWmPB)
1206  *
1207  *
1208  *  Description:
1209  *  -----------
1210  *  Advances the pointer to the next char and returns a pointer
1211  *  to the new current char.
1212  *
1213  *
1214  *  Inputs:
1215  *  ------
1216  *  pWmPB       = pointer to a parse buffer
1217  *
1218  * 
1219  *  Outputs:
1220  *  -------
1221  *  Return      = pointer to the rest of the current line
1222  *
1223  *
1224  *  Comments:
1225  *  --------
1226  * 
1227  *************************************<->***********************************/
1228
1229 unsigned char * 
1230 _DtWmParseNextChar (
1231     DtWmpParseBuf *pWmPB
1232     )
1233
1234 {
1235     unsigned char *pch = NULL;
1236     int chlen;
1237
1238 #ifdef MULTIBYTE
1239     if (pWmPB &&
1240         pWmPB->pchNext &&
1241         (chlen = mblen((char *)pWmPB->pchNext, MB_CUR_MAX) > 0))
1242     {
1243         pch = (pWmPB->pchNext += chlen);
1244     }
1245 #else /* MULTIBYTE */
1246     if (pWmPB && pWmPB->pchNext && *pWmPB->pchNext)
1247     {
1248         pch = ++pWmPB->pchNext;
1249     }
1250 #endif /* MULTIBYTE */
1251
1252     return (pch);
1253 }
1254
1255 \f
1256 /*************************************<->*************************************
1257  *
1258  *  (int) _DtWmParseLineNumber (pWmPB)
1259  *
1260  *
1261  *  Description:
1262  *  -----------
1263  *  Returns the number of the current line of what's being parsed.
1264  *
1265  *
1266  *  Inputs:
1267  *  ------
1268  *  pWmPB       = ptr to parse buffer
1269  *
1270  * 
1271  *  Outputs:
1272  *  -------
1273  *  Return      = number of current line
1274  *
1275  *
1276  *  Comments:
1277  *  --------
1278  *  Used for error reporting.
1279  *
1280  *  The line number is computed by counting '\n' characters.
1281  * 
1282  *************************************<->***********************************/
1283
1284 int 
1285 _DtWmParseLineNumber (
1286     DtWmpParseBuf *pWmPB
1287     )
1288
1289 {
1290     return (pWmPB ? pWmPB->lineNumber : 0);
1291
1292 } /* END OF FUNCTION _DtWmParseLineNumber */
1293
1294 \f
1295 /*************************************<->*************************************
1296  *
1297  *  _DtWmParseToLower (string)
1298  *
1299  *
1300  *  Description:
1301  *  -----------
1302  *  Lower all characters in a string.
1303  *
1304  *
1305  *  Inputs:
1306  *  ------
1307  *  string = NULL-terminated character string or NULL
1308  *
1309  * 
1310  *  Outputs:
1311  *  -------
1312  *  string = NULL-terminated lower case character string or NULL
1313  *
1314  *
1315  *  Comments:
1316  *  --------
1317  *  Can handle multi-byte characters
1318  * 
1319  *************************************<->***********************************/
1320
1321 void _DtWmParseToLower (unsigned char  *string)
1322 {
1323     unsigned char *pch = string;
1324 #ifdef MULTIBYTE
1325     int            chlen;
1326
1327     while ((chlen = mblen ((char *)pch, MB_CUR_MAX)) > 0)
1328     {
1329         if ((chlen == 1) && (isupper (*pch)))
1330         {
1331             *pch = tolower(*pch);
1332         }
1333         pch += chlen;
1334     }
1335 #else
1336     while (*pch != NULL)
1337     {
1338         if (isupper (*pch))
1339         {
1340             *pch = tolower(*pch);
1341         }
1342         pch++;
1343     }
1344 #endif
1345
1346 } /* END OF FUNCTION _DtWmParseToLower */
1347
1348
1349 \f
1350 /*************************************<->*************************************
1351  *
1352  *  _DtWmParsePeekAhead (currentChar, currentLev)
1353  *
1354  *
1355  *  Description:
1356  *  -----------
1357  *  Returns a new level value if this is a new nesting level of quoted string
1358  *  Otherwise it returns a zero
1359  *
1360  *
1361  *  Inputs:
1362  *  ------
1363  *  currentChar = current position in the string
1364  *  currentLev = current level of nesting
1365  *
1366  * 
1367  *  Outputs:
1368  *  -------
1369  *  Returns either a new level of nesting or zero if the character is copied in
1370  *
1371  *
1372  *  Comments:
1373  *  --------
1374  * 
1375  *************************************<->***********************************/
1376 unsigned int _DtWmParsePeekAhead(unsigned char *currentChar,
1377                        unsigned int currentLev)
1378
1379
1380 {
1381     Boolean             done = False;
1382     unsigned int        tmpLev = 1;
1383 #ifdef MULTIBYTE
1384     unsigned int        chlen;
1385
1386     while (((chlen = mblen ((char *)currentChar, MB_CUR_MAX)) > 0) &&
1387            (chlen == 1) && ((*currentChar == '"') || (*currentChar == '\\'))
1388            && (done == False))
1389     {
1390         currentChar++;
1391
1392         if(((chlen = mblen ((char *)currentChar, MB_CUR_MAX)) > 0) && 
1393            (chlen == 1) &&
1394            ((*currentChar == '"') || (*currentChar == '\\')))
1395         {
1396             tmpLev++;
1397             if(*currentChar == '"')
1398             {
1399                 done = True;
1400             }
1401             else
1402             {
1403                 currentChar++;
1404             }
1405         }
1406     }
1407 #else
1408     while((*currentChar != NULL) && (done == False) &&
1409           ((*currentChar == '"') || (*currentChar == '\\')))
1410     {
1411         currentChar++;
1412         if((*currentChar != NULL) &&
1413            ((*currentChar == '"') || (*currentChar == '\\')))
1414         {
1415             tmpLev++;
1416             if(*currentChar == '"')
1417             {
1418                 done = True;
1419             }
1420             else
1421             {
1422                 currentChar++;
1423             }
1424         }
1425     }
1426 #endif /*MULTIBYTE*/
1427
1428     /*
1429      * Figure out if this is truly a new level of nesting - else ignore it
1430      * This section probably could do some error checking and return -1
1431          * If so, change type of routine from unsigned int to int
1432      */
1433     if(done == True)
1434     {
1435         return(tmpLev);
1436     }
1437     else
1438     {
1439         return(0);
1440     }
1441 } /* END OF FUNCTION _DtWmParsePeekAhead */
1442
1443
1444 \f
1445 /*************************************<->*************************************
1446  *
1447  *  (unsigned char *) _DtWmParseFilenameExpand (pchFilename)
1448  *
1449  *
1450  *  Description:
1451  *  -----------
1452  *  Returns a copy of a file name with environment variables
1453  *  expanded.
1454  *
1455  *
1456  *  Inputs:
1457  *  ------
1458  *  pchFilename = ptr to a zero terminated character string (filename)
1459  *
1460  * 
1461  *  Outputs:
1462  *  -------
1463  *  Return      = ptr to a new file name with environment variables 
1464  *                expanded.
1465  *
1466  *
1467  *  Comments:
1468  *  --------
1469  *  The passed in string is temporarily modified inside here.
1470  * 
1471  *  Free the returned string with XtFree().
1472  *
1473  *  Returns NULL on a memory allocation error.
1474  *
1475  *  Environment variables that can't be expanded are removed from
1476  *  the returned copy.
1477  *
1478  *  If no environment variables, you get a copy of the string back.
1479  * 
1480  *************************************<->***********************************/
1481 unsigned char *
1482 _DtWmParseFilenameExpand (
1483     unsigned char *pchFilename
1484     )
1485
1486 {
1487     unsigned char *pchN, *pchNew, *pchO;
1488     unsigned char *pchEnv, *pchEnv0, *pchEnv1;
1489     unsigned char chSave;
1490     int len, n, nx, ix;
1491     unsigned char pchBrk[] = { DTWM_CHAR_ENVIRONMENT,
1492                                DTWM_CHAR_DIRECTORY,
1493                                '\0'
1494                              };
1495
1496     len = strlen ((char *)pchFilename);
1497     pchNew = (unsigned char *) XtMalloc (1+len);
1498     pchO = pchFilename;
1499     chSave = '\0';
1500     ix = 0;
1501
1502     while (pchNew && pchO && *pchO)
1503     {
1504         /* find next environment variable */
1505         pchEnv0 = (unsigned char *) 
1506                 strchr ((char *)pchO, (int) DTWM_CHAR_ENVIRONMENT);
1507         if (pchEnv0)
1508         {
1509             /* length to this point */
1510             n = pchEnv0 - pchO;
1511
1512             /* copy up to environment character */
1513             if (n) 
1514             {
1515                 memcpy (&pchNew[ix], pchO, n);
1516                 ix += n;
1517             }
1518
1519             /* skip environment character */
1520             pchEnv0++;  
1521
1522             /* end of variable is at one of:
1523              *   start of next variable,
1524              *   start of next directory,
1525              *   end of string 
1526              */
1527             pchEnv1 = (unsigned char *) 
1528                         strpbrk ((char *)pchEnv0, (char *)pchBrk);
1529
1530             if (pchEnv1)
1531             {
1532                 /* next string starts after this one */
1533                 pchO = pchEnv1; 
1534                 n = pchEnv1 - pchEnv0 + 1;
1535
1536                 /* replace this char with NULL for now */
1537                 chSave = *pchEnv1;
1538                 *pchEnv1 = '\0';
1539             }
1540             else
1541             {
1542                 /* This environment variable is the last thing on
1543                  * the line. Signal all done.
1544                  */
1545                 n = strlen ((char *) pchO);
1546                 pchO += n;
1547             }
1548
1549             pchEnv = (unsigned char *) getenv ((char *)pchEnv0);
1550             if (pchEnv) 
1551             {
1552                 nx = strlen ((char *) pchEnv);
1553                 if (nx > n)
1554                 {
1555                     len += nx - n;
1556                     pchNew = (unsigned char *) 
1557                         XtRealloc ((char *)pchNew, 1+len);
1558                 }
1559                 if (pchNew)
1560                 {
1561                     memcpy (&pchNew[ix], pchEnv, nx);
1562                     ix += nx;
1563                 }
1564                 else 
1565                 {
1566                     continue;
1567                 }
1568             }
1569
1570             if (chSave)
1571             {
1572                 *pchO = chSave;
1573                 chSave = '\0';
1574             }
1575             /* keep a kosher string */
1576             pchNew[ix] = '\0';
1577         }
1578         else
1579         {
1580             /* copy the rest of the string */
1581             n = strlen ((char *) pchO);
1582             memcpy (&pchNew[ix], pchO, n);
1583             pchO += n;
1584
1585             /* remember the NULL! (a famous battle cry) */
1586             pchNew[ix + n] = '\0';
1587         }
1588     }
1589
1590     return (pchNew);
1591
1592 } /* END OF FUNCTION _DtWmParseFilenameExpand */
1593
1594 \f
1595 /*************************************<->*************************************
1596  *
1597  *  unsigned char * _DtWmParseExpandEnvironmentVariables (pch, pchBrk)
1598  *
1599  *
1600  *  Description:
1601  *  -----------
1602  *  Expands environment variables in a string.
1603  *
1604  *
1605  *  Inputs:
1606  *  ------
1607  *  pch   = ptr to a zero terminated character string 
1608  *  pchBrk = array of "break" characters (see strpbrk()).
1609  *           defaults are used if this is NULL.
1610  * 
1611  *  Outputs:
1612  *  -------
1613  *  Return = string with expanded environment variables. (free with XtFree)
1614  *           NULL string if no environment variables or backslashes
1615  *           found in the string passed in.
1616  *
1617  *
1618  *  Comments:
1619  *  --------
1620  *  Free returned string with XtFree()
1621  *
1622  *  Environment variables that can't be expanded are removed from
1623  *  the returned copy.
1624  *
1625  *  Default delimiter set is [Space], [Tab], '$', [Newline], '\', '/'.
1626  *
1627  *  Variables of form $(..) and ${..} supported.
1628  *
1629  *  A backslash '\' in front of any character quotes it. The backslash 
1630  *  is removed in the returned string. A literal backslash needs to be
1631  *  quoted with a backslash.
1632  * 
1633  *************************************<->***********************************/
1634 unsigned char *
1635 _DtWmParseExpandEnvironmentVariables (
1636     unsigned char *pch,
1637     unsigned char *pchBrk
1638     )
1639
1640 {
1641 #ifdef MULTIBYTE
1642     int chlen;
1643 #else /* MULTIBYTE */
1644     int chlen = 1;      /* length of character is always '1' */
1645 #endif /* MULTIBYTE */
1646     unsigned char *pchStart;
1647     unsigned char chSave;
1648     unsigned char *pchEnvStart;
1649     unsigned char *pchEnvValue;
1650     unsigned char *pchReturn = NULL;
1651     unsigned char *pchNext;
1652     unsigned char *pchBreak;
1653     Boolean bEatBreak;
1654     Boolean bAlreadyAdvanced;
1655     int lenOriginal;
1656     int lenNonEnv;
1657     int lenEnvVar;
1658     int lenEnvValue;
1659     int lenReturn;
1660     int lenSave;
1661     static unsigned char pchDefaultBrk[] = { 
1662                                 DTWM_CHAR_ENVIRONMENT,
1663                                 DTWM_CHAR_SPACE,
1664                                 DTWM_CHAR_TAB,
1665                                 DTWM_CHAR_NEW_LINE,
1666                                 DTWM_CHAR_BACKSLASH,
1667                                 DTWM_CHAR_DIRECTORY,
1668                                 '\0' };
1669     unsigned char pchParenBrk[] = {   
1670                                 DTWM_CHAR_R_PAREN,
1671                                 '\0' };
1672     unsigned char pchBraceBrk[] = {   
1673                                 DTWM_CHAR_R_BRACE,
1674                                 '\0' };
1675
1676     /* There needs to be something to look at */
1677     if (!pch)
1678         return (NULL);
1679
1680     pchStart = pch;
1681     lenOriginal = strlen ((char *)pch);
1682 #ifdef MULTIBYTE
1683     chlen = mblen ((char *)pch, MB_CUR_MAX);
1684 #endif /* MULTIBYTE */
1685     chSave = '\0';
1686
1687     while (*pch && (chlen > 0))
1688     {
1689         if (chlen == 1)
1690         {
1691             bAlreadyAdvanced = False;
1692             switch (*pch)
1693             {
1694                 case DTWM_CHAR_BACKSLASH:
1695                     /*
1696                      * Copy up to start of quoted char
1697                      */
1698                     if (!pchReturn)
1699                     {
1700                         lenReturn = lenOriginal + 1;
1701                         pchReturn = (unsigned char *) 
1702                             XtMalloc (lenReturn * sizeof (unsigned char));
1703                         pchReturn[0] = '\0';
1704                     }
1705                     chSave = *pch;
1706                     *pch = '\0';
1707                     strcat ((char *) pchReturn, (char *)pchStart);
1708                     *pch = chSave;
1709                     chSave = '\0';
1710
1711                     /* 
1712                      * The next character is "escaped", skip over it.
1713                      */
1714                     pchStart = pch += chlen;
1715 #ifdef MULTIBYTE
1716                     chlen = mblen ((char *)pch, MB_CUR_MAX);
1717 #endif /* MULTIBYTE */
1718                     break;
1719
1720                 case DTWM_CHAR_ENVIRONMENT:
1721                     /* save start of environment variable */
1722                     pchEnvStart = pch;
1723                     pch += chlen;
1724 #ifdef MULTIBYTE
1725                     chlen = mblen ((char *)pch, MB_CUR_MAX);
1726 #endif /* MULTIBYTE */
1727
1728                     /*
1729                      * Copy up to start of environment variable 
1730                      */
1731                     if (!pchReturn)
1732                     {
1733                         lenReturn = lenOriginal + 1;
1734                         pchReturn = (unsigned char *) 
1735                             XtMalloc (lenReturn * sizeof (unsigned char));
1736                         pchReturn[0] = '\0';
1737                         lenSave = 0;
1738                     }
1739                     else
1740                     {
1741                         lenSave = strlen ((char *)pchReturn);
1742                     }
1743                     lenNonEnv = pchEnvStart - pchStart;
1744                     memcpy (&pchReturn[lenSave], pchStart, lenNonEnv);
1745                     pchReturn[lenSave+lenNonEnv] = '\0';
1746
1747                     /* 
1748                      * Determine how we find the end of this 
1749                      * environment variable.
1750                      */
1751                     bEatBreak = False;
1752                     if ((chlen == 1)  &&
1753                         (*pch == DTWM_CHAR_L_PAREN))
1754                     {
1755                         pch += chlen;
1756 #ifdef MULTIBYTE
1757                         chlen = mblen ((char *)pch, MB_CUR_MAX);
1758 #endif /* MULTIBYTE */
1759                         pchBreak = pchParenBrk;
1760                         bEatBreak = True;
1761                     }
1762                     else if ((chlen == 1)  &&
1763                         (*pch == DTWM_CHAR_L_BRACE))
1764                     {
1765                         pch += chlen;
1766 #ifdef MULTIBYTE
1767                         chlen = mblen ((char *)pch, MB_CUR_MAX);
1768 #endif /* MULTIBYTE */
1769                         pchBreak = pchBraceBrk;
1770                         bEatBreak = True;
1771                     }
1772                     else if (pchBrk && *pchBrk)
1773                     {
1774                         pchBreak = pchBrk;
1775                     }
1776                     else
1777                     {
1778                         pchBreak = pchDefaultBrk;
1779                     }
1780
1781                     /* 
1782                      * Look for end of environment variable
1783                      */
1784                     pchNext = (unsigned char *)
1785                             strpbrk ((char *)pch, (char *)pchBreak);
1786
1787                     if (!pchNext)
1788                     {
1789                         /* it's the rest of the string */
1790                         chSave = NULL;
1791                         bEatBreak = False;
1792                         pchNext = pch + strlen ((char *) pch);
1793                     }
1794                     else
1795                     {
1796                         /* temporarily put a string terminator here */
1797                         chSave = *pchNext;
1798                         *pchNext = '\0';
1799                     }
1800
1801                     /*
1802                      * Lookup environment variable
1803                      */
1804                     lenEnvVar = strlen ((char *)pch);
1805                     pchEnvValue = (unsigned char *) getenv ((char *)pch);
1806
1807                     if (pchEnvValue)
1808                     {
1809                         /*
1810                          * Insure there's enough room in the return string
1811                          */
1812                         lenEnvValue = strlen ((char *)pchEnvValue);
1813                         if (!pchReturn)
1814                         {
1815                             lenReturn = lenOriginal + 1 - lenEnvVar +
1816                                                 lenEnvValue;
1817                             pchReturn = (unsigned char *) 
1818                                 XtMalloc (lenReturn * sizeof (unsigned char));
1819                             pchReturn[0] = '\0';
1820                         }
1821                         else
1822                         {
1823                             lenReturn = lenReturn + 1 - lenEnvVar +
1824                                                 lenEnvValue;
1825                             pchReturn = (unsigned char *) 
1826                                 XtRealloc ((char *)pchReturn, 
1827                                     lenReturn * sizeof (unsigned char));
1828                         }
1829
1830                         /*
1831                          * Tack it onto the return string
1832                          */
1833                         strcat ((char *)pchReturn, (char *)pchEnvValue);
1834                     }
1835
1836                     /*
1837                      * Advance the pointer for the next pass
1838                      */
1839                     if (chSave)
1840                     {
1841                         /* restore saved character */
1842                         *pchNext = chSave;
1843                         chSave = '\0';
1844
1845                         /* 
1846                          *  If this was a closing paren, then skip it
1847                          */
1848                         if (bEatBreak)
1849                         {
1850 #ifdef MULTIBYTE
1851                             chlen = mblen ((char *)pchNext, MB_CUR_MAX);
1852 #endif /* MULTIBYTE */
1853                             pchNext += chlen;
1854                         }
1855                     }
1856                     pchStart = pch = pchNext;
1857 #ifdef MULTIBYTE
1858                     chlen = mblen ((char *)pch, MB_CUR_MAX);
1859 #endif /* MULTIBYTE */
1860                     /*
1861                      * We're already pointing at the next character
1862                      * to process, don't advance again!
1863                      */
1864                     bAlreadyAdvanced = True;
1865
1866                     break;
1867
1868                 default:
1869                     /* this character is not interesting */
1870                     break;
1871             }
1872
1873             /*
1874              * Move to the next character if we're not already
1875              * there.
1876              */
1877             if (!bAlreadyAdvanced)
1878             {
1879                 pch += chlen; 
1880 #ifdef MULTIBYTE
1881                 chlen = mblen ((char *)pch, MB_CUR_MAX);
1882 #endif /* MULTIBYTE */
1883             }
1884         }
1885 #ifdef MULTIBYTE
1886         else
1887         {
1888             pch += chlen;
1889             chlen = mblen ((char *)pch, MB_CUR_MAX);
1890         }
1891 #endif /* MULTIBYTE */
1892     }
1893
1894     if (pchReturn && *pchStart)
1895     {
1896         /*
1897          * Copy remaining parts of the string
1898          */
1899         strcat ((char *)pchReturn, (char *)pchStart);
1900     }
1901
1902     return (pchReturn);
1903
1904 } /* END OF FUNCTION _DtWmParseExpandEnvironmentVariables */
1905
1906
1907 \f
1908 /******************************<->*************************************
1909  *
1910  *  _DtWmParseMakeQuotedString (pchLine)
1911  *
1912  *
1913  *  Description:
1914  *  -----------
1915  *  Encapsulates the passed in "line" into a string argument quoted
1916  *  by double quotes. Special characters are "escaped" as needed.
1917  *
1918  *  Inputs:
1919  *  ------
1920  *  pchLine = ptr to string to enclose in quotes
1921  *
1922  *  Outputs:
1923  *  -------
1924  *  Return = ptr to quoted string 
1925  *
1926  *  Comment:
1927  *  -------
1928  *  Returned string should be freed with XtFree().
1929  * 
1930  ******************************<->***********************************/
1931
1932 unsigned char *
1933 _DtWmParseMakeQuotedString (unsigned char *pchLine)
1934 {
1935     unsigned char *pchRet;
1936     int iLen0, iLen1;
1937     int cSpecial;
1938     int i,j;
1939 #ifdef MULTIBYTE
1940     int   chlen;
1941 #endif
1942
1943     iLen0 = strlen ((char *)pchLine);
1944     iLen1 = iLen0 + 2; /* for starting, ending quotes */
1945
1946     for (i=0; i < iLen0; i++)
1947     {
1948         /*
1949          * Count special chars to get estimate of new length
1950          */
1951 #ifdef MULTIBYTE
1952         chlen = mblen ((char *) &pchLine[i], MB_CUR_MAX);
1953         if ((chlen == 1) &&
1954             ((pchLine[i] == '\\') ||
1955              (pchLine[i] == '"')))
1956         {
1957             iLen1++;
1958         }
1959         else if (chlen < 1)
1960         {
1961             break;
1962         }
1963         else 
1964         {
1965             i += chlen-1;
1966         }
1967 #else /* MULTIBYTE */
1968         if ((pchLine[i] == '\\') ||
1969             (pchLine[i] == '"'))
1970         {
1971             iLen1++;
1972         }
1973 #endif /* MULTIBYTE */
1974     }
1975
1976     pchRet = (unsigned char *) XtMalloc (1+iLen1);
1977
1978     if (pchRet)
1979     {
1980         pchRet[0] = '"';        /* starting quote */
1981         /*
1982          * Copy chars from old string to new one
1983          */
1984         for (i=0, j=1; i < iLen0; i++, j++)
1985         {
1986 #ifdef MULTIBYTE
1987             chlen = mblen ((char *) &pchLine[i], MB_CUR_MAX);
1988             if ((chlen == 1) &&
1989                 ((pchLine[i] == '\\') ||
1990                  (pchLine[i] == '"')))
1991             {
1992                 /* quote next char */
1993                 pchRet[j++] = '\\';
1994             }
1995             else if (chlen < 1)
1996             {
1997                 break;
1998             }
1999             else while (chlen > 1)
2000             {
2001                 /* copy first bytes of multibyte char */
2002                 pchRet[j++] = pchLine[i++];
2003                 chlen--;
2004             }
2005 #else /* MULTIBYTE */
2006             if ((pchLine[i] == '\\') ||
2007                 (pchLine[i] == '"'))
2008             {
2009                 /* quote next char */
2010                 pchRet[j++] = '\\';
2011             }
2012 #endif /* MULTIBYTE */
2013             /* copy char */
2014             pchRet[j] = pchLine[i];
2015         }
2016         pchRet[j++] = '"';      /* ending quote */
2017         pchRet[j] = '\0';       /* end of string */
2018     }
2019
2020     return (pchRet);
2021
2022 } /* END OF FUNCTION _DtWmParseMakeQuotedString */
2023
2024
2025 /*==================== END OF FILE   WmParse.c ====================*/