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