1 /*!
2   \file lib/gis/strings.c
3 
4   \brief GIS Library - string/chring movement functions
5 
6   \todo merge interesting functions from ../datetime/scan.c here
7 
8   (C) 1999-2008, 2011 by the GRASS Development Team
9 
10   This program is free software under the GNU General Public License
11   (>=v2). Read the file COPYING that comes with GRASS for details.
12 
13   \author Dave Gerdes (USACERL)
14   \author Michael Shapiro (USACERL)
15   \author Amit Parghi (USACERL)
16   \author Bernhard Reiter (Intevation GmbH, Germany) and many others
17 */
18 
19 #include <string.h>
20 #include <stdlib.h>
21 #include <ctype.h>
22 #include <sys/types.h>
23 #include <grass/gis.h>
24 
25 #ifndef NULL
26 #define NULL		0
27 #endif
28 
29 static int _strncasecmp(const char *, const char *, int);
30 
31 /*!
32   \brief String compare ignoring case (upper or lower)
33 
34   Returning a value that has the same sign as the difference between
35   the first differing pair of characters.
36 
37   Note: strcasecmp() is affected by the locale (LC_CTYPE), while
38   G_strcasecmp() isn't.
39 
40   \param x first string to compare
41   \param y second string to compare
42 
43   \return 0 the two strings are equal
44   \return -1, 1
45 */
G_strcasecmp(const char * x,const char * y)46 int G_strcasecmp(const char *x, const char *y)
47 {
48     return _strncasecmp(x, y, -1);
49 }
50 
51 /*!
52   \brief String compare ignoring case (upper or lower) - limited
53   number of characters
54 
55   Returning a value that has the same sign as the difference between
56   the first differing pair of characters.
57 
58   Note: strcasecmp() is affected by the locale (LC_CTYPE), while
59   G_strcasecmp() isn't.
60 
61   \param x first string to compare
62   \param y second string to compare
63   \param n number or characters to compare
64 
65   \return 0 the two strings are equal
66   \return -1, 1
67 */
G_strncasecmp(const char * x,const char * y,int n)68 int G_strncasecmp(const char *x, const char *y, int n)
69 {
70     return _strncasecmp(x, y, n);
71 }
72 
73 /*!
74   \brief Copy string to allocated memory.
75 
76   This routine allocates enough memory to hold the string <b>s</b>,
77   copies <em>s</em> to the allocated memory, and returns a pointer
78   to the allocated memory.
79 
80   If <em>s</em> is NULL then empty string is returned.
81 
82   \param s string
83 
84   \return pointer to newly allocated string
85 */
G_store(const char * s)86 char *G_store(const char *s)
87 {
88     char *buf;
89 
90     if (s == NULL) {
91 	buf = G_malloc(sizeof(char));
92 	buf[0] = '\0';
93     }
94     else {
95 	buf = G_malloc(strlen(s) + 1);
96 	strcpy(buf, s);
97     }
98 
99     return buf;
100 }
101 
102 /*!
103   \brief Copy string to allocated memory and convert copied string
104   to upper case
105 
106   This routine allocates enough memory to hold the string <b>s</b>,
107   copies <em>s</em> to the allocated memory, and returns a pointer
108   to the allocated memory.
109 
110   If <em>s</em> is NULL then empty string is returned.
111 
112   \param s string
113 
114   \return pointer to newly allocated upper case string
115 */
G_store_upper(const char * s)116 char *G_store_upper(const char *s)
117 {
118     char *u_s;
119 
120     u_s = G_store(s);
121     G_str_to_upper(u_s);
122 
123     return u_s;
124 }
125 
126 /*!
127   \brief Copy string to allocated memory and convert copied string
128   to lower case
129 
130   This routine allocates enough memory to hold the string <b>s</b>,
131   copies <em>s</em> to the allocated memory, and returns a pointer
132   to the allocated memory.
133 
134   If <em>s</em> is NULL then empty string is returned.
135 
136   \param s string
137 
138   \return pointer to newly allocated lower case string
139 */
G_store_lower(const char * s)140 char *G_store_lower(const char *s)
141 {
142     char *l_s;
143 
144     l_s = G_store(s);
145     G_str_to_lower(l_s);
146 
147     return l_s;
148 }
149 
150 /*!
151   \brief Replace all occurrences of character in string bug with new
152 
153   \param[in,out] bug base string
154   \param character character to replace
155   \param new new character
156 
157   \return bug string
158 */
G_strchg(char * bug,char character,char new)159 char *G_strchg(char *bug, char character, char new)
160 {
161     char *help = bug;
162 
163     while (*help) {
164 	if (*help == character)
165 	    *help = new;
166 	help++;
167     }
168     return bug;
169 }
170 
171 /*!
172   \brief Replace all occurrences of old_str in buffer with new_str
173 
174   Code example:
175   \code
176   char *name;
177   name = G_str_replace ( inbuf, ".exe", "" );
178   ...
179   G_free (name);
180   \endcode
181 
182   \param buffer input string buffer
183   \param old_str string to be replaced
184   \param new_str new string
185 
186   \return the newly allocated string, input buffer is unchanged
187 */
G_str_replace(const char * buffer,const char * old_str,const char * new_str)188 char *G_str_replace(const char *buffer, const char *old_str, const char *new_str)
189 {
190     char *R;
191     const char *N, *B;
192     char *replace;
193     int count, len;
194 
195     /* Make sure old_str and new_str are not NULL */
196     if (old_str == NULL || new_str == NULL)
197 	return G_store(buffer);
198     /* Make sure buffer is not NULL */
199     if (buffer == NULL)
200 	return NULL;
201 
202     /* Make sure old_str occurs */
203     B = strstr(buffer, old_str);
204     if (B == NULL)
205 	/* return NULL; */
206 	return G_store(buffer);
207 
208     if (strlen(new_str) > strlen(old_str)) {
209 	/* Count occurrences of old_str */
210 	count = 0;
211 	len = strlen(old_str);
212 	B = buffer;
213 	while (B != NULL && *B != '\0') {
214 	    B = strstr(B, old_str);
215 	    if (B != NULL) {
216 		B += len;
217 		count++;
218 	    }
219 	}
220 
221 	len = count * (strlen(new_str) - strlen(old_str))
222 	    + strlen(buffer);
223 
224     }
225     else
226 	len = strlen(buffer);
227 
228     /* Allocate new replacement */
229     replace = G_malloc(len + 1);
230     if (replace == NULL)
231 	return NULL;
232 
233     /* Replace old_str with new_str */
234     B = buffer;
235     R = replace;
236     len = strlen(old_str);
237     while (*B != '\0') {
238 	if (*B == old_str[0] && strncmp(B, old_str, len) == 0) {
239 	    N = new_str;
240 	    while (*N != '\0')
241 		*R++ = *N++;
242 	    B += len;
243 	}
244 	else {
245 	    *R++ = *B++;
246 	}
247     }
248     *R = '\0';
249 
250     return replace;
251 }
252 
253 /*!
254   \brief Removes all leading and trailing white space from string.
255 
256   \param[in,out] buf buffer to be worked on
257 */
G_strip(char * buf)258 void G_strip(char *buf)
259 {
260     char *a, *b;
261 
262     /* remove leading white space */
263     for (a = b = buf; *a == ' ' || *a == '\t'; a++) ;
264     if (a != b)
265 	while ((*b++ = *a++)) ;
266     /* remove trailing white space */
267     for (a = buf; *a; a++) ;
268     if (a != buf) {
269 	for (a--; *a == ' ' || *a == '\t'; a--) ;
270 	a++;
271 	*a = 0;
272     }
273 }
274 
275 /*!
276   \brief Chop leading and trailing white spaces.
277 
278   \verbatim space, \f, \n, \r, \t, \v \endverbatim
279 
280   Modified copy of G_squeeze() by RB in March 2000.
281 
282   \param line buffer to be worked on
283 
284   \return pointer to string
285 */
G_chop(char * line)286 char *G_chop(char *line)
287 {
288     char *f = line, *t = line;
289 
290     while (isspace(*f))		/* go to first non white-space char */
291 	f++;
292 
293     if (!*f) {			/* no more chars in string */
294 	*t = '\0';
295 	return (line);
296     }
297 
298     for (t = f; *t; t++)	/* go from first non white-space char to end */
299 	;
300     while (isspace(*--t)) ;
301     *++t = '\0';		/* remove trailing white-spaces */
302 
303     if (f != line) {
304 	t = line;
305 	while (*f)		/* leading white spaces, shift */
306 	    *t++ = *f++;
307 	*t = '\0';
308     }
309 
310     return (line);
311 }
312 
313 /*!
314   \brief Convert string to upper case
315 
316   \param[in,out] str pointer to string
317 */
G_str_to_upper(char * str)318 void G_str_to_upper(char *str)
319 {
320     int i = 0;
321 
322     if (!str)
323 	return;
324 
325     while (str[i]) {
326 	str[i] = toupper(str[i]);
327 	i++;
328     }
329 }
330 
331 /*!
332   \brief Convert string to lower case
333 
334   \param[in,out] str pointer to string
335 */
G_str_to_lower(char * str)336 void G_str_to_lower(char *str)
337 {
338     int i = 0;
339 
340     if (!str)
341 	return;
342 
343     while (str[i]) {
344 	str[i] = tolower(str[i]);
345 	i++;
346     }
347 }
348 
349 /*!
350   \brief Make string SQL compliant
351 
352   \param[in,out] str pointer to string
353 
354   \return number of changed characters
355 */
G_str_to_sql(char * str)356 int G_str_to_sql(char *str)
357 {
358     int count;
359     char *c;
360 
361     count = 0;
362 
363     if (!str || !*str)
364 	return 0;
365 
366     c = str;
367     while (*c) {
368 	*c = toascii(*c);
369 
370 	if (!(*c >= 'A' && *c <= 'Z') && !(*c >= 'a' && *c <= 'z') &&
371 	    !(*c >= '0' && *c <= '9')) {
372 	    *c = '_';
373 	    count++;
374 	}
375 	c++;
376     }
377 
378     c = str;
379     if (!(*c >= 'A' && *c <= 'Z') && !(*c >= 'a' && *c <= 'z')) {
380 	*c = 'x';
381 	count++;
382     }
383 
384     return count;
385 }
386 
387 /*!
388   \brief Remove superfluous white space.
389 
390   Leading and trailing white space is removed from the string
391   <b>line</b> and internal white space which is more than one character
392   is reduced to a single space character. White space here means
393   spaces, tabs, linefeeds, newlines, and formfeeds.
394 
395   \param[in,out] line
396 
397   \return Pointer to <b>line</b>
398 */
G_squeeze(char * line)399 void G_squeeze(char *line)
400 {
401     char *f = line, *t = line;
402     int l;
403 
404     /* skip over space at the beginning of the line. */
405     while (isspace(*f))
406 	f++;
407 
408     while (*f)
409 	if (!isspace(*f))
410 	    *t++ = *f++;
411 	else if (*++f)
412 	    if (!isspace(*f))
413 		*t++ = ' ';
414     *t = '\0';
415     l = strlen(line) - 1;
416     if (*(line + l) == '\n')
417 	*(line + l) = '\0';
418 }
419 
420 /*!
421   \brief Finds the first occurrence of the sub-string in the
422   null-terminated string ignoring case (upper or lower)
423 
424   \param str string where to find sub-string
425   \param substr sub-string
426 
427   \return a pointer to the first occurrence of sub-string
428   \return NULL if no occurrences are found
429 */
G_strcasestr(const char * str,const char * substr)430 char *G_strcasestr(const char *str, const char *substr)
431 {
432     const char *p;
433     const char *q;
434     int length;
435 
436     p = substr;
437     q = str;
438     length = strlen(substr);
439 
440     do {
441 	/* match 1st substr char */
442 	while (*q != '\0' && toupper(*q) != toupper(*p)) {
443 	    q++;
444 	}
445     } while (*q != '\0' && G_strncasecmp(p, q, length) != 0 && q++);
446 
447     if (*q == '\0') {
448 	/* ran off end of str */
449 	return NULL;
450     }
451 
452     return (char *) q;
453 }
454 
_strncasecmp(const char * x,const char * y,int n)455 int _strncasecmp(const char *x, const char *y, int n)
456 {
457     int xx, yy, i;
458 
459     if (!x)
460 	return y ? -1 : 0;
461     if (!y)
462 	return x ? 1 : 0;
463 
464     i = 1;
465     while (*x && *y) {
466 	xx = *x++;
467 	yy = *y++;
468 	if (xx >= 'A' && xx <= 'Z')
469 	    xx = xx + 'a' - 'A';
470 	if (yy >= 'A' && yy <= 'Z')
471 	    yy = yy + 'a' - 'A';
472 	if (xx < yy)
473 	    return -1;
474 	if (xx > yy)
475 	    return 1;
476 
477 	if (n > -1 && i >= n)
478 	    return 0;
479 
480 	i++;
481     }
482 
483     if (*x)
484 	return 1;
485     if (*y)
486 	return -1;
487     return 0;
488 }
489