| 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 | */
|
|---|
| 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 | */
|
|---|
| 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 | */
|
|---|
| 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 | */
|
|---|
| 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 | */
|
|---|
| 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 | */
|
|---|
| 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 | */
|
|---|
| 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 | */
|
|---|
| 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 | */
|
|---|
| 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 | */
|
|---|
| 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 | */
|
|---|
| 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 | */
|
|---|
| 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 | */
|
|---|
| 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 | */
|
|---|
| 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 |
|
|---|
| 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 | }
|
|---|