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