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