dce59977947059cf332550c8b652d471dc54e710
[oweals/cde.git] / cde / lib / DtSvc / DtUtil2 / CmdUtility.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*
24  * File:         CmdUtility.c $XConsortium: CmdUtility.c /main/4 1995/10/26 15:18:41 rswiston $
25  * Language:     C
26  *
27  * (c) Copyright 1988, Hewlett-Packard Company, all rights reserved.
28  *
29  * (c) Copyright 1993, 1994 Hewlett-Packard Company                     *
30  * (c) Copyright 1993, 1994 International Business Machines Corp.       *
31  * (c) Copyright 1993, 1994 Sun Microsystems, Inc.                      *
32  * (c) Copyright 1993, 1994 Novell, Inc.                                *
33  */
34
35 #include <ctype.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <sys/param.h>
39
40 #ifdef __apollo
41 #include "/sys5/usr/include/unistd.h"
42 #else
43 #include <unistd.h>
44 #endif
45 #include <limits.h>
46 #include <sys/stat.h>
47
48 #include <X11/Xlib.h>
49 #include <X11/Intrinsic.h>
50 #include <X11/StringDefs.h>
51 #include <Dt/Utility.h>
52 #include <Dt/DtNlUtils.h>
53
54
55 #define _SINGLE  "\'"
56 #define _DOUBLE  "\""
57
58
59 /********    Static Function Declarations    ********/
60
61 static void SkipWhiteSpace( 
62                         String string,
63                         int *position) ;
64 static void FillWord( 
65                         char *string,
66                         char *word,
67                         int *position) ;
68 static void GetWordWithQuotes( 
69                         String string,
70                         String word,
71                         int *position) ;
72
73 /********    End Static Function Declarations    ********/
74
75
76 /*****************************************************************************
77  *
78  * SkipWhiteSpace - takes a string and in index ("position") into the 
79  *   string and advances "position" until a non-whitespace character is 
80  *   encountered.
81  *
82  *   A "whitespace" character is defined by "isspace". 
83  *
84  * PARAMETERS:
85  *
86  *   String string;     - The string to search.
87  *
88  *   int *position;     - MODIFIED: Set to the location of the first 
89  *                        non-whitespace character.
90  *
91  *****************************************************************************/
92
93 static void 
94 SkipWhiteSpace(
95         String string,
96         int *position )
97 {
98    string += (*position);
99
100    while (
101 #ifdef NLS16
102       (!is_multibyte || (mblen (string, MB_CUR_MAX) == 1)) &&
103 #endif
104          isspace ((u_char)*string)) {
105             string++;
106             (*position)++;
107          }
108 }
109
110 /*****************************************************************************
111  *
112  * FillWord - parses "string" for a complete argument and puts the
113  * result in "word".
114  *
115  * The algorithm was derived by empirical observation and checking the
116  * (poorly written) Bourne Shell tutorial.  A BNF for the shell meta
117  * characters ", ', and \ was not availble.
118  *
119  * The algorithm - until the end of the word is found:
120  *
121  *  For each character "c":
122  *     If c = \, remove the \ and pass on the next char. 
123  *
124  *     If c = ' or ", must save this in "qchar" and loop until the 
125  *     ending quote is found: 
126  *        c = the next char
127  *        If c = qchar, found the ending quote, exit this loop
128  *        If c = \
129  *           If qchar = double quote and c2 = next char
130  *              if c2 = \, ", $, or `, remove c and pass on c2
131  *              otherwise, pass on both c and c2
132  *           If qchar = single quote, and c2 = next char:
133  *              if c2 = ', pass on c, remove the ' and exit this loop
134  *                 (the ' cannot be escaped)
135  *              otherwise, pass on both c and c2
136  *        Othewise, pass on c
137  *
138  *     If c = white space, found the end of the word, return
139  *     Otherwise, pass on the char
140  *
141  *
142  * PARAMETERS:
143  *
144  *   char *string;      - The string to search.
145  *
146  *   char *word;        - MODIFIED: Points to the beginning of the word.
147  *
148  *   int *position      - MODIFIED: Starts at the beginning of the string 
149  *                        and gets advanced past "word".
150  *
151  *****************************************************************************/
152
153 static void 
154 FillWord(
155         char *string,
156         char *word,
157         int *position )
158 {
159    char *qchar;
160    int len, i;
161    Boolean found = False;
162    Boolean done  = False;
163    int j = 0;
164    char *pbeg = string;
165
166    while ((*string != '\0') && (!found)) {
167       /* 
168        * Check for multibyte chars. The assumption here is that if
169        * is_multibyte is true and "string" points to a multi-byte char,
170        * then that entire char should be copied to "word".
171        */
172 #ifdef NLS16
173       if (is_multibyte && ((len = mblen (string, MB_CUR_MAX)) > 1))
174          for (i=0; i < len; i++, j++, string++)
175             word[j] = *string;
176       else 
177 #endif
178        {
179          switch (*string) {
180             case '\\':
181                /* Remove the slash and add the following character. */
182                string++;
183 #ifdef NLS16
184                if (is_multibyte && ((len = mblen (string, MB_CUR_MAX)) > 1))
185                   for (i=0; i < len; i++, j++, string++)
186                      word[j] = *string;
187                else 
188 #endif
189                   word[j++] = *(string)++;
190                break;
191             case '\'':
192             case '\"':
193                qchar = _DOUBLE;
194                if (*string == '\'') qchar = _SINGLE;
195                string++;
196                /* Search for the ending quote. */ 
197                done = False;
198                while ((!done) && (*string != '\0')) {
199 #ifdef NLS16
200                   if (is_multibyte && ((len = mblen (string, MB_CUR_MAX)) > 1))
201                      for (i=0; i < len; i++, j++, string++)
202                         word[j] = *string;
203                   else 
204 #endif
205                   {
206                      if (*string == *qchar) {
207                         done = True;
208                         string++;
209                         break;
210                      }
211                      if (*string == '\\') {
212                         /* Must follow the rules of the single or double
213                          * quote - which ever "qchar" points to.
214                          */
215                         if (!strcmp (qchar, _DOUBLE)) {
216                            if ((DtStrcspn (string+1, "\"\\$`")) == 0) {
217                               /* Skip past the '\', but fill in the 
218                                * following character. 
219                                */
220                               string++;
221                            }
222                            else
223                               /* Want to pass on both the '\' and the 
224                                * following char. 
225                                */
226                               word[j++] = *(string)++;
227                            /* The '\' is skipped.  Fill in the next char. */
228 #ifdef NLS16
229                            if (is_multibyte && ((len = mblen (string, MB_CUR_MAX)) > 1))
230                               for (i=0; i < len; i++, j++, string++)
231                                  word[j] = *string;
232                            else
233 #endif
234                               word[j++] = *(string)++;
235                            /* The \ and following char are now skipped. */
236                         }
237                         else if (!strcmp (qchar, _SINGLE)) {
238                            /* Must be working on a _SINGLE quoted word. */
239                            if ((DtStrcspn (string+1, "\'")) == 0) {
240                               /* 
241                                * Have \', which passes on the \, skips 
242                                * the single quote and ends the word. An 
243                                * assumption here is that the char following 
244                                * the '\' was a single byte single quote 
245                                * and there is no need for checking multi-byte. 
246                                */
247                               word[j++] = *(string)++;
248                               /* Now skip the quote. */
249                               string++;
250                               done = True;
251                               break;
252                            }
253                            else {
254                               /* 
255                                * Need to pass on both chars.  Pass on the 
256                                * first char here.  
257                                */
258                               word[j++] = *(string)++;
259
260                               /* 
261                                * The '\' is skipped if present.  Fill in the
262                                * next char.
263                                */
264 #ifdef NLS16
265                               if (is_multibyte && ((len = mblen (string, MB_CUR_MAX)) > 1))
266                                  for (i=0; i < len; i++, j++, string++)
267                                     word[j] = *string;
268                               else
269 #endif
270                                  /* Pass on what ever char is there. */
271                                  word[j++] = *(string)++;
272                            }
273                         }
274                      }
275                      else
276                         /* This char was not escaped, just add it. */
277                         word[j++] = *(string)++;
278                   }
279                }
280                break;
281             case ' ':
282             case '\t':
283                /* Found the end of the word. */
284                found = True;
285                string++;
286                break;
287             default: {
288                word[j++] = *(string)++;
289             }
290          }
291       }
292    }
293    word [j] = '\0';
294    *position = *position + (string - pbeg);
295 }
296
297 /*****************************************************************************
298  *
299  * GetWordWithQuotes - takes the strings "string" and "word" and an index
300  * into "string" and fills "word" with one word from "string".
301  *
302  * A word is defined in the function "FillWord".
303  *
304  * Note that if an ending quote is not found, "position" will be advanced to
305  * the end of the string.  
306  *
307  * PARAMETERS:
308  *
309  *   String string;     - String containing the word to be extracted.
310  *
311  *   String word;       - MODIFIED - contains the next word in "string".
312  *
313  *   int *position;     - MODIFIED - starts at beginning of word, ends 
314  *                        at end of word.
315  *
316  *****************************************************************************/
317
318 static void 
319 GetWordWithQuotes(
320         String string,
321         String word,
322         int *position )
323 {
324    int len = strlen(string);
325    SkipWhiteSpace (string, position);
326
327    if ((*position) >= len) {
328       word[0] = '\0';
329       return;
330    }
331
332    string += (*position);
333
334    FillWord (string, word, position);
335 }
336
337 /*****************************************************************************
338  *
339  * _DtCmdStringToArrayOfStrings - takes a string and an array of pointers
340  *   to strings and breaks the string into whitespace separated words.
341  *
342  *   A "word" is a sequence of characters that has no whitespace with
343  *   the following exception:
344  *
345  *     -  A word may contain contain whitespace if it is delimited
346  *        by a pair of matching single or double qotes.
347  *
348  *   "Whitespace" is a tab or blank character.
349  *
350  *
351  * NOTES: 
352  *
353  *   -  The space for the "words" is malloc'd and must be free'd by
354  *   the caller.
355  *
356  *   -  "theArray" is NULL terminated.
357  *
358  * PARAMETERS:
359  *
360  *   char theString[];          - The string to parse.
361  *
362  *   char *theArray[];          - MODIFIED: gets filled with pointers to
363  *                                the words that are parsed.
364  *
365  *****************************************************************************/
366
367 void 
368 _DtCmdStringToArrayOfStrings(
369         char theString[],
370         char *theArray[] )
371 {
372    int len, i, position;
373    char *tmp;
374    tmp = (char *) XtMalloc (1 + strlen (theString));
375
376    len=strlen(theString);
377    for (position=0, i=0; (position <= len) && 
378         (theString[position] != '\0'); ) {
379       (void) strcpy (tmp, "");
380       GetWordWithQuotes (theString, tmp, &position);
381       /* Check word to see if it contains only trailing blanks. */
382       if (tmp[0] == '\0') 
383       {
384          if (position < len)
385          {
386             /*
387              * This parameter is empty, such as "" or '' but we are
388              * not at the end of the line.  Consequently, put an
389              * empty string in "theArray".
390              */
391             theArray[i] = XtNewString ("");
392          }
393          else
394          {
395             /*
396              * Must be at the end of the line.
397              */
398             theArray[i] = (char *) NULL;
399             break;
400          }
401       }
402       else
403          theArray[i] = XtNewString (tmp);
404       i++;
405    }
406
407    /* Null terminate the array of string pointers. */
408    theArray[i]=NULL;
409
410    XtFree ((char *) tmp);
411 }
412
413 /******************************************************************************
414  *
415  * _DtCmdFreeStringVector - takes an array of pointers to strings and
416  *   frees the malloc'd space for the strings.
417  *
418  *   This does NOT free the string vector itself; It assumes that
419  *   stringv is a static i.e. char *stringv[N].
420  *
421  * PARAMETERS:
422  *
423  *   char **stringv;    - MODIFIED:  Each string in the array is freed.
424  *
425  *****************************************************************************/
426
427 void 
428 _DtCmdFreeStringVector(
429         char **stringv )
430 {
431    char **pch;
432
433    for (pch = stringv; *pch != NULL; pch++) {
434       XtFree (*pch);
435       *pch = NULL;
436    }
437 }
438
439