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