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