• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MapServer / MapServer / 26710430429

31 May 2026 10:44AM UTC coverage: 42.443% (+0.002%) from 42.441%
26710430429

push

github

web-flow
PostGIS: make sure identifier value is numeric when the declared type is numeric too (#7516)

Fixes https://github.com/MapServer/MapServer/security/advisories/GHSA-xp29-8wp5-wc3p

0 of 7 new or added lines in 1 file covered. (0.0%)

337 existing lines in 2 files now uncovered.

64665 of 152357 relevant lines covered (42.44%)

27398.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

62.32
/src/mapstring.cpp
1
/******************************************************************************
2
 * $Id$
3
 *
4
 * Project:  MapServer
5
 * Purpose:  Various string handling functions.
6
 * Author:   Steve Lime and the MapServer team.
7
 *
8
 * Notes: A couple of string handling functions (strrstr, strlcat) were taken
9
 *from other sources. Copyright notices accompany those functions below.
10
 *
11
 ******************************************************************************
12
 * Copyright (c) 1996-2005 Regents of the University of Minnesota.
13
 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
14
 *
15
 * Permission is hereby granted, free of charge, to any person obtaining a
16
 * copy of this software and associated documentation files (the "Software"),
17
 * to deal in the Software without restriction, including without limitation
18
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
19
 * and/or sell copies of the Software, and to permit persons to whom the
20
 * Software is furnished to do so, subject to the following conditions:
21
 *
22
 * The above copyright notice and this permission notice shall be included in
23
 * all copies of this Software or works derived from this Software.
24
 *
25
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
26
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
28
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
30
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
31
 * DEALINGS IN THE SOFTWARE.
32
 ****************************************************************************/
33

34
#include "mapserver.h"
35
#include "mapthread.h"
36

37
#include "cpl_vsi.h"
38

39
#include <ctype.h>
40
#include <float.h>
41
#include <string.h>
42
#include <errno.h>
43

44
#include <algorithm>
45
#include <cctype>
46

47
/*
48
 * Find the first occurrence of find in s, ignore case.
49
 */
50

51
#ifdef USE_FRIBIDI
52
#if (defined(_WIN32) && !defined(__CYGWIN__)) || defined(HAVE_FRIBIDI2)
53
#include "fribidi.h"
54
#else
55
#include <fribidi/fribidi.h>
56
#endif
57
#define MAX_STR_LEN 65000
58
#endif
59

60
#ifdef USE_ICONV
61
#include "mapiconv.h"
62
#include <iconv.h>
63
#include <wchar.h>
64
#endif
65

66
#include "mapentities.h"
67

68
#ifndef HAVE_STRRSTR
69
/*
70
** Copyright (c) 2000-2004  University of Illinois Board of Trustees
71
** Copyright (c) 2000-2005  Mark D. Roth
72
** All rights reserved.
73
**
74
** Developed by: Campus Information Technologies and Educational Services,
75
** University of Illinois at Urbana-Champaign
76
**
77
** Permission is hereby granted, free of charge, to any person obtaining
78
** a copy of this software and associated documentation files (the
79
** ``Software''), to deal with the Software without restriction, including
80
** without limitation the rights to use, copy, modify, merge, publish,
81
** distribute, sublicense, and/or sell copies of the Software, and to
82
** permit persons to whom the Software is furnished to do so, subject to
83
** the following conditions:
84
**
85
** * Redistributions of source code must retain the above copyright
86
**   notice, this list of conditions and the following disclaimers.
87
**
88
** * Redistributions in binary form must reproduce the above copyright
89
**   notice, this list of conditions and the following disclaimers in the
90
**   documentation and/or other materials provided with the distribution.
91
**
92
** * Neither the names of Campus Information Technologies and Educational
93
**   Services, University of Illinois at Urbana-Champaign, nor the names
94
**   of its contributors may be used to endorse or promote products derived
95
**   from this Software without specific prior written permission.
96
**
97
** THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
98
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
99
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
100
** IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR
101
** ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
102
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
103
** OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
104
*/
105
char *strrstr(const char *string, const char *find) {
4✔
106
  size_t stringlen, findlen;
107
  const char *cp;
108

109
  findlen = strlen(find);
4✔
110
  stringlen = strlen(string);
4✔
111
  if (findlen > stringlen)
4✔
112
    return NULL;
113

114
  for (cp = string + stringlen - findlen; cp >= string; cp--)
11✔
115
    if (strncmp(cp, find, findlen) == 0)
11✔
116
      return (char *)cp;
4✔
117

118
  return NULL;
119
}
120
#endif
121

122
#ifndef HAVE_STRLCAT
123
/*
124
 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
125
 *
126
 * Permission to use, copy, modify, and distribute this software for any
127
 * purpose with or without fee is hereby granted, provided that the above
128
 * copyright notice and this permission notice appear in all copies.
129
 *
130
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
131
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
132
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
133
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
134
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
135
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
136
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
137
 */
138

139
/*
140
 * Appends src to string dst of size siz (unlike strncat, siz is the
141
 * full size of dst, not space left).  At most siz-1 characters
142
 * will be copied.  Always NUL terminates (unless siz <= strlen(dst)).
143
 * Returns strlen(src) + MS_MIN(siz, strlen(initial dst)).
144
 * If retval >= siz, truncation occurred.
145
 */
146
size_t strlcat(char *dst, const char *src, size_t siz) {
147
  char *d = dst;
148
  const char *s = src;
149
  size_t n = siz;
150
  size_t dlen;
151

152
  /* Find the end of dst and adjust bytes left but don't go past end */
153
  while (n-- != 0 && *d != '\0')
154
    d++;
155
  dlen = d - dst;
156
  n = siz - dlen;
157

158
  if (n == 0)
159
    return (dlen + strlen(s));
160
  while (*s != '\0') {
161
    if (n != 1) {
162
      *d++ = *s;
163
      n--;
164
    }
165
    s++;
166
  }
167
  *d = '\0';
168

169
  return (dlen + (s - src)); /* count does not include NUL */
170
}
171
#endif
172

173
#ifndef HAVE_STRLCPY
174
/*
175
 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
176
 * All rights reserved.
177
 *
178
 * Redistribution and use in source and binary forms, with or without
179
 * modification, are permitted provided that the following conditions
180
 * are met:
181
 * 1. Redistributions of source code must retain the above copyright
182
 *    notice, this list of conditions and the following disclaimer.
183
 * 2. Redistributions in binary form must reproduce the above copyright
184
 *    notice, this list of conditions and the following disclaimer in the
185
 *    documentation and/or other materials provided with the distribution.
186
 * 3. The name of the author may not be used to endorse or promote products
187
 *    derived from this software without specific prior written permission.
188
 *
189
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
190
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
191
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
192
 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
193
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
194
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
195
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
196
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
197
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
198
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
199
 */
200

201
/*
202
 * Copy src to string dst of size siz.  At most siz-1 characters
203
 * will be copied.  Always NUL terminates (unless siz == 0).
204
 * Returns strlen(src); if retval >= siz, truncation occurred.
205
 */
206
size_t strlcpy(char *dst, const char *src, size_t siz) {
207
  char *d = dst;
208
  const char *s = src;
209
  size_t n = siz;
210

211
  /* Copy as many bytes as will fit */
212
  if (n != 0 && --n != 0) {
213
    do {
214
      if ((*d++ = *s++) == 0)
215
        break;
216
    } while (--n != 0);
217
  }
218

219
  /* Not enough room in dst, add NUL and traverse rest of src */
220
  if (n == 0) {
221
    if (siz != 0)
222
      *d = '\0'; /* NUL-terminate dst */
223
    while (*s++)
224
      ;
225
  }
226

227
  return (s - src - 1); /* count does not include NUL */
228
}
229
#endif
230

231
#ifndef HAVE_STRCASESTR
232
/*-
233
 * Copyright (c) 1990, 1993
234
 *  The Regents of the University of California.  All rights reserved.
235
 *
236
 * This code is derived from software contributed to Berkeley by
237
 * Chris Torek.
238
 *
239
 * Redistribution and use in source and binary forms, with or without
240
 * modification, are permitted provided that the following conditions
241
 * are met:
242
 * 1. Redistributions of source code must retain the above copyright
243
 *    notice, this list of conditions and the following disclaimer.
244
 * 2. Redistributions in binary form must reproduce the above copyright
245
 *    notice, this list of conditions and the following disclaimer in the
246
 *    documentation and/or other materials provided with the distribution.
247
 * 3. Neither the name of the University nor the names of its contributors
248
 *    may be used to endorse or promote products derived from this software
249
 *    without specific prior written permission.
250
 *
251
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
252
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
253
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
254
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
255
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
256
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
257
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
258
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
259
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
260
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
261
 * SUCH DAMAGE.
262
 */
263
char *strcasestr(const char *s, const char *find) {
264
  char c, sc;
265
  size_t len;
266

267
  if ((c = *find++) != 0) {
268
    c = tolower((unsigned char)c);
269
    len = strlen(find);
270
    do {
271
      do {
272
        if ((sc = *s++) == 0)
273
          return (NULL);
274
      } while ((char)tolower((unsigned char)sc) != c);
275
    } while (strncasecmp(s, find, len) != 0);
276
    s--;
277
  }
278
  return ((char *)s);
279
}
280
#endif
281

282
#ifndef HAVE_STRNCASECMP
283
int strncasecmp(const char *s1, const char *s2, size_t len) {
284
  const char *cp1, *cp2;
285

286
  cp1 = s1;
287
  cp2 = s2;
288

289
  if (len == 0)
290
    return (0);
291

292
  if (!*cp1)
293
    return -1;
294
  else if (!*cp2)
295
    return 1;
296

297
  while (*cp1 && *cp2 && len) {
298
    int cmp;
299
    if ((cmp = (toupper(*cp1) - toupper(*cp2))) != 0)
300
      return (cmp);
301
    cp1++;
302
    cp2++;
303
    len--;
304
  }
305

306
  if (len == 0) {
307
    return (0);
308
  }
309
  if (*cp1 || *cp2) {
310
    if (*cp1)
311
      return (1);
312
    else
313
      return (-1);
314
  }
315
  return (0);
316
}
317
#endif
318

319
#ifndef HAVE_STRCASECMP
320
int strcasecmp(const char *s1, const char *s2) {
321
  const char *cp1, *cp2;
322

323
  cp1 = s1;
324
  cp2 = s2;
325
  if ((!cp1) || (!cp2)) {
326
    return (0);
327
  }
328
  while (*cp1 && *cp2) {
329
    int cmp;
330
    if ((cmp = (toupper(*cp1) - toupper(*cp2))) != 0)
331
      return (cmp);
332
    cp1++;
333
    cp2++;
334
  }
335
  if (*cp1 || *cp2) {
336
    if (*cp1)
337
      return (1);
338
    else
339
      return (-1);
340
  }
341

342
  return (0);
343
}
344
#endif
345

346
/*
347
** msStringToInt() and msStringToDouble() are helper functions to convert
348
*strings to numbers. They
349
** return MS_FAILURE if the input string is NULL or if the entire string did not
350
*convert successfully.
351
*/
352
int msStringToInt(const char *str, int *value, int base) {
295✔
353
  char *parse_check = NULL;
295✔
354

355
  if (!str)
295✔
356
    return MS_FAILURE;
357

358
  *value = (int)strtol(str, &parse_check, base);
295✔
359
  if (*parse_check != '\0')
295✔
360
    return MS_FAILURE;
361

362
  return MS_SUCCESS;
363
}
364

365
int msStringToDouble(const char *str, double *value) {
28✔
366
  char *parse_check = NULL;
28✔
367

368
  if (!str)
28✔
369
    return MS_FAILURE;
370

371
  *value = strtod(str, &parse_check);
28✔
372
  if (*parse_check != '\0')
28✔
373
    return MS_FAILURE;
374

375
  return MS_SUCCESS;
376
}
377

378
char *msLongToString(long value) {
×
379
  size_t bufferSize = 256;
380
  char *buffer = (char *)msSmallMalloc(bufferSize);
×
381

382
  snprintf(buffer, bufferSize, "%ld", value);
383
  return (buffer);
×
384
}
385

386
char *msDoubleToString(double value, int force_f) {
200✔
387
  size_t bufferSize = 256;
388
  char *buffer = (char *)msSmallMalloc(bufferSize);
200✔
389

390
  if (force_f == MS_TRUE)
200✔
391
    snprintf(buffer, bufferSize, "%f", value);
392
  else
393
    snprintf(buffer, bufferSize, "%g", value);
394
  return (buffer);
200✔
395
}
396

397
char *msIntToString(int value) {
232✔
398
  size_t bufferSize = 256;
399
  char *buffer = (char *)msSmallMalloc(bufferSize);
232✔
400

401
  snprintf(buffer, bufferSize, "%i", value);
402
  return (buffer);
232✔
403
}
404

405
void msStringToUpper(char *string) {
117✔
406
  int i;
407

408
  if (string != NULL) {
117✔
409
    for (i = 0; string[i]; i++) {
854✔
410
      string[i] = toupper(string[i]);
737✔
411
    }
412
    return;
413
  }
414
}
415

416
void msStringToLower(char *string) {
62✔
417
  int i;
418

419
  if (string != NULL) {
62✔
420
    for (i = 0; string[i]; i++) {
620✔
421
      string[i] = tolower(string[i]);
558✔
422
    }
423
    return;
424
  }
425
}
426

427
std::string msStringToLower(const std::string &s) {
20,867✔
428
  std::string ret(s);
429
  std::transform(ret.begin(), ret.end(), ret.begin(),
430
                 [](unsigned char c) { return std::tolower(c); });
279,054✔
431
  return ret;
20,867✔
432
}
433

434
/**
435
 * Force the first character to uppercase and the rest of the characters to
436
 * lower case for EACH word in the string.
437
 */
438
void msStringInitCap(char *string) {
×
439
  int i;
440
  int start = 1;
441
  if (string != NULL) {
×
442
    for (i = 0; i < (int)strlen(string); i++) {
×
443
      if (string[i] == ' ')
×
444
        start = 1;
445
      else if (start) {
×
446
        string[i] = toupper(string[i]);
×
447
        start = 0;
448
      } else {
449
        string[i] = tolower(string[i]);
×
450
      }
451
    }
452
  }
453
}
×
454

455
/**
456
 * Force the first character to uppercase for the FIRST word in the string
457
 * and the rest of the characters to lower case.
458
 */
459
void msStringFirstCap(char *string) {
117✔
460
  int i;
461
  int start = 1;
462
  if (string != NULL) {
117✔
463
    for (i = 0; i < (int)strlen(string); i++) {
854✔
464
      if (string[i] != ' ') {
737✔
465
        if (start) {
723✔
466
          string[i] = toupper(string[i]);
117✔
467
          start = 0;
468
        } else
469
          string[i] = tolower(string[i]);
606✔
470
      }
471
    }
472
  }
473
}
117✔
474

475
char *msStringChop(char *string) {
×
476
  int n;
477

478
  n = strlen(string);
×
479
  if (n > 0)
×
480
    string[n - 1] = '\0';
×
481

482
  return (string);
×
483
}
484

485
/*
486
** Trim leading and trailing white space.
487
*/
488
void msStringTrim(char *str) {
352✔
489
  int i;
490

491
  /* Send nulls home without supper. */
492
  if (!str)
352✔
493
    return;
494

495
  /* Move non-white string to the front. */
496
  i = strspn(str, " ");
352✔
497
  if (i) {
352✔
498
    memmove(str, str + i, strlen(str) - i + 1);
×
499
  }
500
  /* Nothing left? Exit. */
501
  if (strlen(str) == 0) {
352✔
502
    return;
503
  }
504
  /* Null-terminate end of non-white string. */
505
  for (i = strlen(str) - 1; i >= 0; i--) { /* step backwards from end */
352✔
506
    if (str[i] != ' ') {
352✔
507
      str[i + 1] = '\0';
352✔
508
      return;
352✔
509
    }
510
  }
511
  return;
512
}
513

514
void msStringTrim(std::string &string) {
3,379✔
515
  const size_t npos = string.find_first_not_of(' ');
3,379✔
516
  if (npos != std::string::npos)
3,379✔
517
    string.erase(0, npos);
3,379✔
518
  msStringTrimBlanks(string);
3,379✔
519
}
3,379✔
520

521
/*
522
** Remove leading white spaces and shift everything to the left.
523
*/
524
char *msStringTrimLeft(char *string) {
×
525
  char *read, *write;
526
  int i, length;
527

528
  if (string && strlen(string) > 0) {
×
529
    length = strlen(string);
×
530
    read = string;
531
    write = string;
532

533
    for (i = 0; i < length; i++) {
×
534
      if (isspace(string[i]))
×
535
        read++;
×
536
      else
537
        break;
538
    }
539

540
    if (read > write) {
×
541
      while (*read) {
×
542
        *write = *read;
×
543
        read++;
×
544
        write++;
×
545
      }
546
      *write = '\0';
×
547
    }
548
  }
549
  return string;
×
550
}
551

552
void msStringTrimLeft(std::string &string) {
140✔
553
  const size_t length = string.length();
554
  for (size_t i = 0; i < length; i++) {
140✔
555
    if (!isspace(string[i])) {
140✔
556
      if (i > 0) {
140✔
557
        string.erase(0, i - 1);
×
558
      }
559
      return;
140✔
560
    }
561
  }
562
  string.clear();
563
}
564

565
/* -------------------------------------------------------------------------------
566
 */
567
/*       Trims trailing blanks from a string */
568
/* -------------------------------------------------------------------------------
569
 */
570
void msStringTrimBlanks(char *string) {
979✔
571
  int i, n;
572

573
  n = strlen(string);
979✔
574
  for (i = n - 1; i >= 0; i--) { /* step backwards through the string */
1,695✔
575
    if (string[i] != ' ') {
1,479✔
576
      string[i + 1] = '\0';
763✔
577
      return;
763✔
578
    }
579
  }
580
}
581

582
void msStringTrimBlanks(std::string &string) {
3,519✔
583
  const size_t npos = string.find_last_not_of(' ');
3,519✔
584
  if (npos != std::string::npos)
3,519✔
585
    string.resize(npos + 1);
3,519✔
586
}
3,519✔
587

588
/* -------------------------------------------------------------------------------
589
 */
590
/*       Trims end-of-line marker from a string */
591
/*       Useful in conjunction with fgets() calls */
592
/* -------------------------------------------------------------------------------
593
 */
594
void msStringTrimEOL(char *string) {
×
595
  int i;
596

597
  for (i = 0; string[i] != '\0'; i++) {
×
598
    if (string[i] == '\n') {
×
599
      string[i] = '\0'; /* Terminate the string at the newline */
×
600
      return;
×
601
    }
602
  }
603
}
604

605
/* -------------------------------------------------------------------------------
606
 */
607
/*       Replace all occurrences of old with new in str. */
608
/*       It is assumed that str was dynamically created using malloc. */
609
/* -------------------------------------------------------------------------------
610
 */
611
char *msReplaceSubstring(char *str, const char *old, const char *newstr) {
75,540✔
612
  size_t str_len, old_len, new_len, tmp_offset;
613
  char *tmp_ptr;
614

615
  if (newstr == NULL)
75,540✔
616
    newstr = "";
617

618
  /*
619
  ** If old is not found then leave str alone
620
  */
621
  if ((tmp_ptr = strstr(str, old)) == NULL)
75,540✔
622
    return (str);
623

624
  /*
625
  ** Grab some info about incoming strings
626
  */
627
  str_len = strlen(str);
4,847✔
628
  old_len = strlen(old);
4,847✔
629
  new_len = strlen(newstr);
4,847✔
630

631
  /*
632
  ** Now loop until old is NOT found in new
633
  */
634
  while (tmp_ptr != NULL) {
9,726✔
635

636
    /*
637
    ** re-allocate memory for buf assuming 1 replacement of old with new
638
    ** don't bother reallocating if old is larger than new)
639
    */
640
    if (old_len < new_len) {
4,879✔
641
      tmp_offset = tmp_ptr - str;
571✔
642
      str_len = str_len - old_len + new_len;
571✔
643
      str = (char *)msSmallRealloc(
571✔
644
          str, (str_len + 1)); /* make new space for a copy */
645
      tmp_ptr = str + tmp_offset;
571✔
646
    }
647

648
    /*
649
    ** Move the trailing part of str to make some room unless old_len == new_len
650
    */
651
    if (old_len != new_len) {
4,879✔
652
      memmove(tmp_ptr + new_len, tmp_ptr + old_len,
3,899✔
653
              strlen(tmp_ptr) - old_len + 1);
3,899✔
654
    }
655

656
    /*
657
    ** Now copy new over old
658
    */
659
    memcpy(tmp_ptr, newstr, new_len);
660

661
    /*
662
    ** And look for more matches in the rest of the string
663
    */
664
    tmp_ptr = strstr(tmp_ptr + new_len, old);
4,879✔
665
  }
666

667
  return (str);
668
}
669

670
/*
671
 * same goal as msReplaceSubstring, but for the known case
672
 * when we won't have to do reallocs etc
673
 * used to replace the wrap characetr by a newline for labels
674
 */
675
void msReplaceChar(char *str, char old, char newstr) {
4✔
676
  while (*(str++))
24✔
677
    if (*str == old)
20✔
678
      *str = newstr;
×
679
}
4✔
680

681
/*
682
** how many times does ch occur in str
683
*/
684
int msCountChars(char *str, char ch) {
1,464✔
685
  int i, l, n = 0;
686

687
  l = strlen(str);
1,464✔
688
  for (i = 0; i < l; i++)
62,669✔
689
    if (str[i] == ch)
61,205✔
690
      n++;
1,766✔
691

692
  return (n);
1,464✔
693
}
694

695
/* -------------------------------------------------------------------------------
696
 */
697
/*       Strip filename from a full path */
698
/* -------------------------------------------------------------------------------
699
 */
700
char *msStripPath(char *fn) {
×
701
  char *pSlash;
702
  char *pBackslash;
703

704
  /* try to locate both, the last slash or backslash */
705
  pSlash = strrchr(fn, '/');
706
  pBackslash = strrchr(fn, '\\');
707

708
  if (pSlash != NULL && pBackslash != NULL) {
×
709
    if (pSlash < pBackslash)
×
710
      return ++pBackslash;
×
711
    else
712
      return ++pSlash;
×
713
  } else if (pSlash != NULL)
×
714
    return ++pSlash; /* skip past the "slash" */
×
715
  else if (pBackslash != NULL)
×
716
    return ++pBackslash; /* skip past the "backslash" */
×
717
  else
718
    return (fn);
719
}
720

721
/*
722
** Returns the *path* portion of the filename fn. Memory is allocated using
723
*malloc.
724
*/
725
char *msGetPath(const char *fn) {
6,938✔
726
  char *str;
727
  int i, length;
728

729
  length = strlen(fn);
6,938✔
730
  if ((str = msStrdup(fn)) == NULL)
6,938✔
731
    return (NULL);
732

733
  for (i = length - 1; i >= 0; i--) { /* step backwards through the string */
96,781✔
734
    if ((str[i] == '/') || (str[i] == '\\')) {
93,680✔
735
      str[i + 1] = '\0';
3,837✔
736
      break;
3,837✔
737
    }
738
  }
739

740
  if (strcmp(str, fn) == 0) {
6,938✔
741
    msFree(str);
3,101✔
742
#if defined(_WIN32) && !defined(__CYGWIN__)
743
    str = msStrdup(".\\");
744
#else
745
    str = msStrdup("./");
3,101✔
746
#endif
747
  }
748

749
  return (str);
750
}
751

752
/*
753
** Returns a *path* built from abs_path and path.
754
** The pszReturnPath must be declared by the caller function as an array
755
** of MS_MAXPATHLEN char
756
*/
757
char *msBuildPath(char *pszReturnPath, const char *abs_path, const char *path) {
28,444✔
758
  int abslen = 0;
759
  int pathlen = 0;
760

761
  if (path == NULL) {
28,444✔
762
    msSetError(MS_IOERR, NULL, "msBuildPath");
3✔
763
    return NULL;
3✔
764
  }
765

766
  pathlen = strlen(path);
28,441✔
767
  if (abs_path)
28,441✔
768
    abslen = strlen(abs_path);
26,628✔
769

770
  if ((pathlen + abslen + 2) > MS_MAXPATHLEN) {
28,441✔
771
    msSetError(MS_IOERR, "Path is too long.  Check server logs.",
×
772
               "msBuildPath()");
773
    msDebug("msBuildPath(): (%s%s): path is too long.\n", abs_path, path);
×
774
    return NULL;
×
775
  }
776

777
  /* Check if path is absolute */
778
  if ((abs_path == NULL) || (abslen == 0) || (path[0] == '\\') ||
28,441✔
779
      (path[0] == '/') || (pathlen > 1 && (path[1] == ':'))) {
25,707✔
780
    strlcpy(pszReturnPath, path, MS_MAXPATHLEN);
781
    return (pszReturnPath);
2,734✔
782
  }
783

784
  /* else return abs_path/path */
785
  if ((abs_path[abslen - 1] == '/') || (abs_path[abslen - 1] == '\\')) {
25,707✔
786
    snprintf(pszReturnPath, MS_MAXPATHLEN, "%s%s", abs_path, path);
787
  } else {
788
    snprintf(pszReturnPath, MS_MAXPATHLEN, "%s/%s", abs_path, path);
789
  }
790

791
  return (pszReturnPath);
792
}
793

794
/*
795
** Returns a *path* built from abs_path, path1 and path2.
796
** abs_path/path1/path2
797
** The pszReturnPath must be declared by the caller function as an array
798
** of MS_MAXPATHLEN char
799
*/
800
char *msBuildPath3(char *pszReturnPath, const char *abs_path, const char *path1,
4,791✔
801
                   const char *path2) {
802
  char szPath[MS_MAXPATHLEN];
803

804
  return msBuildPath(pszReturnPath, abs_path,
4,791✔
805
                     msBuildPath(szPath, path1, path2));
4,791✔
806
}
807

808
/*
809
** Similar to msBuildPath(), but the input path is only qualified by the
810
** absolute path if this will result in it pointing to a readable file.
811
**
812
** Returns NULL if the resulting path doesn't point to a readable file.
813
*/
814

815
char *msTryBuildPath(char *szReturnPath, const char *abs_path, const char *path)
251✔
816

817
{
818
  VSILFILE *fp;
819

820
  if (msBuildPath(szReturnPath, abs_path, path) == NULL)
251✔
821
    return NULL;
822

823
  fp = VSIFOpenL(szReturnPath, "r");
251✔
824
  if (fp == NULL) {
251✔
825
    strlcpy(szReturnPath, path, MS_MAXPATHLEN);
826
    return NULL;
1✔
827
  } else
828
    VSIFCloseL(fp);
250✔
829

830
  return szReturnPath;
250✔
831
}
832

833
/*
834
** Similar to msBuildPath3(), but the input path is only qualified by the
835
** absolute path if this will result in it pointing to a readable file.
836
**
837
** Returns NULL if the resulting path doesn't point to a readable file.
838
*/
839

840
char *msTryBuildPath3(char *szReturnPath, const char *abs_path,
1,578✔
841
                      const char *path1, const char *path2)
842

843
{
844
  VSILFILE *fp;
845

846
  if (msBuildPath3(szReturnPath, abs_path, path1, path2) == NULL)
1,578✔
847
    return NULL;
848

849
  fp = VSIFOpenL(szReturnPath, "r");
1,578✔
850
  if (fp == NULL) {
1,578✔
851
    strlcpy(szReturnPath, path2, MS_MAXPATHLEN);
852
    return NULL;
216✔
853
  } else
854
    VSIFCloseL(fp);
1,362✔
855

856
  return szReturnPath;
1,362✔
857
}
858

859
/*
860
** Splits a string into multiple strings based on ch. Consecutive ch's are
861
*ignored.
862
*/
863
char **msStringSplit(const char *string, char ch, int *num_tokens) {
17,431✔
864
  int i, j, k;
865
  int length, n;
866
  char **token;
867
  char last_ch = '\0';
868

869
  n = 1; /* always at least 1 token, the string itself */
870
  length = strlen(string);
17,431✔
871
  for (i = 0; i < length; i++) {
328,596✔
872
    if (string[i] == ch && last_ch != ch)
311,165✔
873
      n++;
22,242✔
874
    last_ch = string[i];
875
  }
876

877
  token = (char **)msSmallMalloc(sizeof(char *) * n);
17,431✔
878

879
  k = 0;
880
  token[k] = (char *)msSmallMalloc(sizeof(char) * (length + 1));
17,431✔
881

882
  j = 0;
883
  last_ch = '\0';
884
  for (i = 0; i < length; i++) {
328,596✔
885
    if (string[i] == ch) {
311,165✔
886

887
      if (last_ch == ch)
22,266✔
888
        continue;
24✔
889

890
      token[k][j] = '\0'; /* terminate current token */
22,242✔
891

892
      k++;
22,242✔
893
      token[k] = (char *)msSmallMalloc(sizeof(char) * (length + 1));
22,242✔
894

895
      j = 0;
896
    } else {
897
      token[k][j] = string[i];
288,899✔
898
      j++;
288,899✔
899
    }
900

901
    last_ch = string[i];
311,141✔
902
  }
903

904
  token[k][j] = '\0'; /* terminate last token */
17,431✔
905

906
  *num_tokens = n;
17,431✔
907

908
  return (token);
17,431✔
909
}
910

911
std::vector<std::string> msStringSplit(const char *string, char ch) {
3,406✔
912
  int num_tokens = 0;
3,406✔
913
  char **tmp = msStringSplit(string, ch, &num_tokens);
3,406✔
914
  std::vector<std::string> res;
915
  res.reserve(num_tokens);
3,406✔
916
  for (int i = 0; i < num_tokens; i++)
10,272✔
917
    res.push_back(tmp[i]);
13,732✔
918
  msFreeCharArray(tmp, num_tokens);
3,406✔
919
  return res;
3,406✔
920
}
×
921

922
/*
923
 This function is a copy of CSLTokenizeString2() function of the CPL component.
924
 See the port/cpl_string.cpp file in gdal source for the complete documentation.
925
 Available Flags:
926
 * - MS_ALLOWEMPTYTOKENS: allow the return of empty tokens when two
927
 * delimiters in a row occur with no other text between them.  If not set,
928
 * empty tokens will be discarded;
929
 * - MS_STRIPLEADSPACES: strip leading space characters from the token (as
930
 * reported by isspace());
931
 * - MS_STRIPENDSPACES: strip ending space characters from the token (as
932
 * reported by isspace());
933
 * - MS_HONOURSTRINGS: double quotes can be used to hold values that should
934
 * not be broken into multiple tokens;
935
 * - MS_PRESERVEQUOTES: string quotes are carried into the tokens when this
936
 * is set, otherwise they are removed;
937
 * - MS_PRESERVEESCAPES: if set backslash escapes (for backslash itself,
938
 * and for literal double quotes) will be preserved in the tokens, otherwise
939
 * the backslashes will be removed in processing.
940
 */
941
char **msStringSplitComplex(const char *pszString, const char *pszDelimiters,
120✔
942
                            int *num_tokens, int nFlags)
943

944
{
945
  char **papszRetList = NULL;
946
  int nRetMax = 0, nRetLen = 0;
947
  char *pszToken;
948
  int nTokenMax, nTokenLen;
949
  int bHonourStrings = (nFlags & MS_HONOURSTRINGS);
120✔
950
  int bAllowEmptyTokens = (nFlags & MS_ALLOWEMPTYTOKENS);
120✔
951
  int bStripLeadSpaces = (nFlags & MS_STRIPLEADSPACES);
120✔
952
  int bStripEndSpaces = (nFlags & MS_STRIPENDSPACES);
120✔
953

954
  pszToken = (char *)msSmallMalloc(sizeof(char) * 10);
120✔
955
  ;
956
  nTokenMax = 10;
957

958
  while (pszString != NULL && *pszString != '\0') {
320✔
959
    int bInString = MS_FALSE;
960
    int bStartString = MS_TRUE;
961

962
    nTokenLen = 0;
963

964
    /* Try to find the next delimiter, marking end of token */
965
    for (; *pszString != '\0'; pszString++) {
1,936✔
966

967
      /* End if this is a delimiter skip it and break. */
968
      if (!bInString && strchr(pszDelimiters, *pszString) != NULL) {
1,821✔
969
        pszString++;
85✔
970
        break;
85✔
971
      }
972

973
      /* If this is a quote, and we are honouring constant
974
         strings, then process the constant strings, with out delim
975
         but don't copy over the quotes */
976
      if (bHonourStrings && *pszString == '"') {
1,736✔
977
        if (nFlags & MS_PRESERVEQUOTES) {
×
978
          pszToken[nTokenLen] = *pszString;
×
979
          nTokenLen++;
×
980
        }
981

982
        if (bInString) {
×
983
          bInString = MS_FALSE;
984
          continue;
×
985
        } else {
986
          bInString = MS_TRUE;
987
          continue;
×
988
        }
989
      }
990

991
      /*
992
       * Within string constants we allow for escaped quotes, but in
993
       * processing them we will unescape the quotes and \\ sequence
994
       * reduces to \
995
       */
996
      if (bInString && pszString[0] == '\\') {
1,736✔
997
        if (pszString[1] == '"' || pszString[1] == '\\') {
×
998
          if (nFlags & MS_PRESERVEESCAPES) {
×
999
            pszToken[nTokenLen] = *pszString;
×
1000
            nTokenLen++;
×
1001
          }
1002

1003
          pszString++;
×
1004
        }
1005
      }
1006

1007
      /*
1008
       * Strip spaces at the token start if requested.
1009
       */
1010
      if (!bInString && bStripLeadSpaces && bStartString &&
1,736✔
1011
          isspace((unsigned char)*pszString))
×
1012
        continue;
×
1013

1014
      bStartString = MS_FALSE;
1015

1016
      /*
1017
       * Extend token buffer if we are running close to its end.
1018
       */
1019
      if (nTokenLen >= nTokenMax - 3) {
1,736✔
1020
        nTokenMax = nTokenMax * 2 + 10;
78✔
1021
        pszToken = (char *)msSmallRealloc(pszToken, sizeof(char) * nTokenMax);
78✔
1022
      }
1023

1024
      pszToken[nTokenLen] = *pszString;
1,736✔
1025
      nTokenLen++;
1,736✔
1026
    }
1027

1028
    /*
1029
     * Strip spaces at the token end if requested.
1030
     */
1031
    if (!bInString && bStripEndSpaces) {
200✔
1032
      while (nTokenLen && isspace((unsigned char)pszToken[nTokenLen - 1]))
×
1033
        nTokenLen--;
×
1034
    }
1035

1036
    pszToken[nTokenLen] = '\0';
200✔
1037

1038
    /*
1039
     * Add the token.
1040
     */
1041
    if (pszToken[0] != '\0' || bAllowEmptyTokens) {
200✔
1042
      if (nRetLen >= nRetMax - 1) {
200✔
1043
        nRetMax = nRetMax * 2 + 10;
120✔
1044
        papszRetList =
1045
            (char **)msSmallRealloc(papszRetList, sizeof(char *) * nRetMax);
120✔
1046
      }
1047

1048
      papszRetList[nRetLen++] = msStrdup(pszToken);
200✔
1049
      papszRetList[nRetLen] = NULL;
200✔
1050
    }
1051
  }
1052

1053
  /*
1054
   * If the last token was empty, then we need to capture
1055
   * it now, as the loop would skip it.
1056
   */
1057
  if (pszString != NULL && *pszString == '\0' && bAllowEmptyTokens &&
120✔
1058
      nRetLen > 0 && strchr(pszDelimiters, *(pszString - 1)) != NULL) {
120✔
1059
    if (nRetLen >= nRetMax - 1) {
5✔
1060
      nRetMax = nRetMax * 2 + 10;
×
1061
      papszRetList =
1062
          (char **)msSmallRealloc(papszRetList, sizeof(char *) * nRetMax);
×
1063
    }
1064

1065
    papszRetList[nRetLen++] = msStrdup("");
5✔
1066
    papszRetList[nRetLen] = NULL;
5✔
1067
  }
1068

1069
  if (papszRetList == NULL)
120✔
1070
    papszRetList = (char **)msSmallMalloc(sizeof(char *) * 1);
×
1071

1072
  *num_tokens = nRetLen;
120✔
1073
  free(pszToken);
120✔
1074

1075
  return papszRetList;
120✔
1076
}
1077

1078
/* This method is similar to msStringSplit but support quoted strings.
1079
   It also support multi-characters delimiter and allows to preserve quotes */
1080
char **msStringTokenize(const char *pszLine, const char *pszDelim,
961✔
1081
                        int *num_tokens, int preserve_quote) {
1082
  char **papszResult = NULL;
1083
  int n = 1, iChar, nLength = strlen(pszLine), iTokenChar = 0,
961✔
1084
      bInQuotes = MS_FALSE;
1085
  char *pszToken = (char *)msSmallMalloc(sizeof(char) * (nLength + 1));
961✔
1086
  int nDelimLen = strlen(pszDelim);
961✔
1087

1088
  /* Compute the number of tokens */
1089
  for (iChar = 0; pszLine[iChar] != '\0'; iChar++) {
18,158✔
1090
    if (bInQuotes && pszLine[iChar] == '"' && pszLine[iChar + 1] == '"') {
17,197✔
1091
      iChar++;
×
1092
    } else if (pszLine[iChar] == '"') {
17,197✔
1093
      bInQuotes = !bInQuotes;
1,884✔
1094
    } else if (!bInQuotes &&
15,313✔
1095
               strncmp(pszLine + iChar, pszDelim, nDelimLen) == 0) {
6,292✔
1096
      iChar += nDelimLen - 1;
1,094✔
1097
      n++;
1,094✔
1098
    }
1099
  }
1100

1101
  papszResult = (char **)msSmallMalloc(sizeof(char *) * n);
961✔
1102
  n = iTokenChar = bInQuotes = 0;
1103
  for (iChar = 0; pszLine[iChar] != '\0'; iChar++) {
18,158✔
1104
    if (bInQuotes && pszLine[iChar] == '"' && pszLine[iChar + 1] == '"') {
17,197✔
1105
      if (preserve_quote == MS_TRUE)
×
1106
        pszToken[iTokenChar++] = '"';
×
1107
      pszToken[iTokenChar++] = '"';
×
1108
      iChar++;
×
1109
    } else if (pszLine[iChar] == '"') {
17,197✔
1110
      if (preserve_quote == MS_TRUE)
1,884✔
1111
        pszToken[iTokenChar++] = '"';
952✔
1112
      bInQuotes = !bInQuotes;
1,884✔
1113
    } else if (!bInQuotes &&
15,313✔
1114
               strncmp(pszLine + iChar, pszDelim, nDelimLen) == 0) {
6,292✔
1115
      pszToken[iTokenChar++] = '\0';
1,094✔
1116
      papszResult[n] = pszToken;
1,094✔
1117
      pszToken = (char *)msSmallMalloc(sizeof(char) * (nLength + 1));
1,094✔
1118
      iChar += nDelimLen - 1;
1,094✔
1119
      iTokenChar = 0;
1120
      n++;
1,094✔
1121
    } else {
1122
      pszToken[iTokenChar++] = pszLine[iChar];
14,219✔
1123
    }
1124
  }
1125

1126
  pszToken[iTokenChar++] = '\0';
961✔
1127
  papszResult[n] = pszToken;
961✔
1128

1129
  *num_tokens = n + 1;
961✔
1130

1131
  return papszResult;
961✔
1132
}
1133

1134
/**********************************************************************
1135
 *                       msEncodeChar()
1136
 *
1137
 * Return 1 if the character argument should be encoded for safety
1138
 * in URL use and 0 otherwise. Specific character map taken from
1139
 * http://www.ietf.org/rfc/rfc2396.txt
1140
 *
1141
 **********************************************************************/
1142

1143
int msEncodeChar(const char c) {
155,436✔
1144
  if ((c >= 0x61 && c <= 0x7A) || /* Letters a-z */
155,436✔
1145
      (c >= 0x41 && c <= 0x5A) || /* Letters A-Z */
155,436✔
1146
      (c >= 0x30 && c <= 0x39) || /* Numbers 0-9 */
1147
      (c >= 0x27 && c <= 0x2A) || /* * ' ( )     */
1148
      (c >= 0x2D && c <= 0x2E) || /* - .         */
1149
      (c == 0x5F) ||              /* _           */
1150
      (c == 0x21) ||              /* !           */
1151
      (c == 0x7E)) {              /* ~           */
1152
    return (0);
1153
  } else {
1154
    return (1);
12,452✔
1155
  }
1156
}
1157

1158
char *msEncodeUrl(const char *data) {
5,063✔
1159
  /*
1160
   * Delegate to msEncodeUrlExcept, with a null second argument
1161
   * to render the except handling moot.
1162
   */
1163
  return (msEncodeUrlExcept(data, '\0'));
5,063✔
1164
}
1165

1166
/**********************************************************************
1167
 *                       msEncodeCharExcept()
1168
 *
1169
 * URL encoding, applies RFP2396 encoding to all characters
1170
 * except the one exception character. An exception character
1171
 * of '\0' implies no exception handling.
1172
 *
1173
 **********************************************************************/
1174

1175
char *msEncodeUrlExcept(const char *data, const char except) {
5,195✔
1176
  static const char *hex = "0123456789ABCDEF";
1177
  const char *i;
1178
  char *j, *code;
1179
  int inc;
1180
  unsigned char ch;
1181

1182
  for (inc = 0, i = data; *i != '\0'; i++)
82,977✔
1183
    if (msEncodeChar(*i))
77,782✔
1184
      inc += 2;
6,290✔
1185

1186
  code = (char *)msSmallMalloc(strlen(data) + inc + 1);
5,195✔
1187

1188
  for (j = code, i = data; *i != '\0'; i++, j++) {
82,977✔
1189
    if (except != '\0' && *i == except) {
77,782✔
1190
      *j = except;
128✔
1191
    } else if (msEncodeChar(*i)) {
77,654✔
1192
      ch = *i;
6,162✔
1193
      *j++ = '%';
6,162✔
1194
      *j++ = hex[ch / 16];
6,162✔
1195
      *j = hex[ch % 16];
6,162✔
1196
    } else
1197
      *j = *i;
71,492✔
1198
  }
1199
  *j = '\0';
5,195✔
1200

1201
  return code;
5,195✔
1202
}
1203

1204
/************************************************************************/
1205
/*                            msEscapeJSonString()                      */
1206
/************************************************************************/
1207

1208
/* The input (and output) string are not supposed to start/end with chQuote
1209
 * characters. It is the responsibility of the caller to do that. */
1210
char *msEscapeJSonLikeString(const char *pszString, char chQuote) {
22✔
1211
  /* Worst case is one character to become \uABCD so 6 characters */
1212
  char *pszRet;
1213
  int i = 0, j = 0;
1214
  static const char *pszHex = "0123456789ABCDEF";
1215

1216
  pszRet = (char *)msSmallMalloc(strlen(pszString) * 6 + 1);
22✔
1217
  /* From http://www.json.org/ */
1218
  for (i = 0; pszString[i] != '\0'; i++) {
433✔
1219
    unsigned char ch = pszString[i];
411✔
1220
    if (ch == '\b') {
411✔
1221
      pszRet[j++] = '\\';
×
1222
      pszRet[j++] = 'b';
×
1223
    } else if (ch == '\f') {
411✔
1224
      pszRet[j++] = '\\';
×
1225
      pszRet[j++] = 'f';
×
1226
    } else if (ch == '\n') {
411✔
1227
      pszRet[j++] = '\\';
1✔
1228
      pszRet[j++] = 'n';
1✔
1229
    } else if (ch == '\r') {
410✔
1230
      pszRet[j++] = '\\';
×
1231
      pszRet[j++] = 'r';
×
1232
    } else if (ch == '\t') {
410✔
1233
      pszRet[j++] = '\\';
1✔
1234
      pszRet[j++] = 't';
1✔
1235
    } else if (ch < 32) {
409✔
1236
      pszRet[j++] = '\\';
×
1237
      pszRet[j++] = 'u';
×
1238
      pszRet[j++] = '0';
×
1239
      pszRet[j++] = '0';
×
1240
      pszRet[j++] = pszHex[ch / 16];
×
1241
      pszRet[j++] = pszHex[ch % 16];
×
1242
    } else if (ch == chQuote) {
409✔
1243
      pszRet[j++] = '\\';
3✔
1244
      pszRet[j++] = chQuote;
3✔
1245
    } else if (ch == '\\') {
406✔
1246
      pszRet[j++] = '\\';
1✔
1247
      pszRet[j++] = '\\';
1✔
1248
    } else {
1249
      pszRet[j++] = ch;
405✔
1250
    }
1251
  }
1252
  pszRet[j] = '\0';
22✔
1253
  return pszRet;
22✔
1254
}
1255

1256
/* The input (and output) string are not supposed to start/end with double
1257
 * quote characters. It is the responsibility of the caller to do that. */
1258
char *msEscapeJSonString(const char *pszString) {
15✔
1259
  return msEscapeJSonLikeString(pszString, '"');
15✔
1260
}
1261

1262
/* msEncodeHTMLEntities()
1263
**
1264
** Return a copy of string after replacing some problematic chars with their
1265
** HTML entity equivalents.
1266
**
1267
** The replacements performed are:
1268
**  '&' -> "&amp;", '"' -> "&quot;", '<' -> "&lt;" and '>' -> "&gt;"
1269
**/
1270
char *msEncodeHTMLEntities(const char *string) {
37,490✔
1271
  int buflen, i;
1272
  char *newstring;
1273
  const char *c;
1274

1275
  if (string == NULL)
37,490✔
1276
    return NULL;
1277

1278
  /* Start with 100 extra chars for replacements...  */
1279
  /* should be good enough for most cases */
1280
  buflen = strlen(string) + 100;
37,488✔
1281
  newstring = (char *)malloc(buflen + 1);
37,488✔
1282
  MS_CHECK_ALLOC(newstring, buflen + 1, NULL);
37,488✔
1283

1284
  for (i = 0, c = string; *c != '\0'; c++) {
486,062✔
1285
    /* Need to realloc buffer? */
1286
    if (i + 6 > buflen) {
448,574✔
1287
      /* If we had to realloc then this string must contain several */
1288
      /* entities... so let's go with twice the previous buffer size */
UNCOV
1289
      buflen *= 2;
×
1290
      /* cppcheck-suppress memleakOnRealloc */
UNCOV
1291
      newstring = (char *)realloc(newstring, buflen + 1);
×
UNCOV
1292
      MS_CHECK_ALLOC(newstring, buflen + 1, NULL);
×
1293
    }
1294

1295
    switch (*c) {
448,574✔
1296
    case '&':
936✔
1297
      strcpy(newstring + i, "&amp;");
936✔
1298
      i += 5;
1299
      break;
936✔
1300
    case '<':
98✔
1301
      strcpy(newstring + i, "&lt;");
98✔
1302
      i += 4;
98✔
1303
      break;
98✔
1304
    case '>':
97✔
1305
      strcpy(newstring + i, "&gt;");
97✔
1306
      i += 4;
97✔
1307
      break;
97✔
1308
    case '"':
275✔
1309
      strcpy(newstring + i, "&quot;");
275✔
1310
      i += 6;
275✔
1311
      break;
275✔
1312
    case '\'':
218✔
1313
      strcpy(newstring + i,
218✔
1314
             "&#39;"); /* changed from &apos; and i += 6 (bug 1040) */
1315
      i += 5;
1316
      break;
218✔
1317
    default:
446,950✔
1318
      newstring[i++] = *c;
446,950✔
1319
    }
1320
  }
1321

1322
  newstring[i++] = '\0';
37,488✔
1323

1324
  return newstring;
37,488✔
1325
}
1326

1327
/* msDecodeHTMLEntities()
1328
**
1329
** Modify the string to replace encoded characters by their true value
1330
**
1331
** The replacements performed are:
1332
**  "&amp;" -> '&', "&quot;" -> '"', "&lt;" -> '<' and "&gt;" -> '>'
1333
**/
1334
void msDecodeHTMLEntities(const char *string) {
178✔
1335
  char *pszAmp = NULL, *pszSemiColon = NULL, *pszReplace = NULL, *pszEnd = NULL;
1336
  char *pszBuffer = NULL;
1337
  size_t bufferSize = 0;
1338

1339
  if (string == NULL)
178✔
1340
    return;
1341
  else
1342
    pszBuffer = (char *)string;
1343

1344
  bufferSize = strlen(pszBuffer);
178✔
1345
  pszReplace = (char *)msSmallMalloc(bufferSize + 1);
178✔
1346
  pszEnd = (char *)msSmallMalloc(bufferSize + 1);
178✔
1347

1348
  while ((pszAmp = strchr(pszBuffer, '&')) != NULL) {
218✔
1349
    /* Get the &...; */
1350
    strlcpy(pszReplace, pszAmp, bufferSize);
1351
    pszSemiColon = strchr(pszReplace, ';');
1352
    if (pszSemiColon == NULL)
75✔
1353
      break;
1354
    else
1355
      pszSemiColon++;
40✔
1356

1357
    /* Get everything after the &...; */
1358
    strlcpy(pszEnd, pszSemiColon, bufferSize);
1359

1360
    pszReplace[pszSemiColon - pszReplace] = '\0';
40✔
1361

1362
    /* Replace the &...; */
1363
    if (strcasecmp(pszReplace, "&amp;") == 0) {
40✔
1364
      pszBuffer[pszAmp - pszBuffer] = '&';
40✔
1365
      pszBuffer[pszAmp - pszBuffer + 1] = '\0';
40✔
1366
      strcat(pszBuffer, pszEnd);
1367
    } else if (strcasecmp(pszReplace, "&lt;") == 0) {
×
UNCOV
1368
      pszBuffer[pszAmp - pszBuffer] = '<';
×
1369
      pszBuffer[pszAmp - pszBuffer + 1] = '\0';
×
1370
      strcat(pszBuffer, pszEnd);
1371
    } else if (strcasecmp(pszReplace, "&gt;") == 0) {
×
UNCOV
1372
      pszBuffer[pszAmp - pszBuffer] = '>';
×
1373
      pszBuffer[pszAmp - pszBuffer + 1] = '\0';
×
1374
      strcat(pszBuffer, pszEnd);
1375
    } else if (strcasecmp(pszReplace, "&quot;") == 0) {
×
UNCOV
1376
      pszBuffer[pszAmp - pszBuffer] = '"';
×
UNCOV
1377
      pszBuffer[pszAmp - pszBuffer + 1] = '\0';
×
1378
      strcat(pszBuffer, pszEnd);
UNCOV
1379
    } else if (strcasecmp(pszReplace, "&apos;") == 0) {
×
UNCOV
1380
      pszBuffer[pszAmp - pszBuffer] = '\'';
×
UNCOV
1381
      pszBuffer[pszAmp - pszBuffer + 1] = '\0';
×
1382
      strcat(pszBuffer, pszEnd);
1383
    }
1384

1385
    pszBuffer = pszAmp + 1;
40✔
1386
  }
1387

1388
  free(pszReplace);
178✔
1389
  free(pszEnd);
178✔
1390

1391
  return;
178✔
1392
}
1393

1394
/*
1395
** msIsXMLValid
1396
**
1397
** Check if the string is an XML valid string. It should contains only
1398
** A-Z, a-z, 0-9, '_', '-', '.', and ':'
1399
** Return MS_TRUE or MS_FALSE
1400
*/
1401
int msIsXMLTagValid(const char *string) {
19,551✔
1402
  int i, nLen;
1403

1404
  nLen = strlen(string);
19,551✔
1405

1406
  for (i = 0; i < nLen; i++) {
171,405✔
1407
    if (!(string[i] >= 'A' && string[i] <= 'Z') &&
151,862✔
1408
        !(string[i] >= 'a' && string[i] <= 'z') &&
1409
        !(string[i] >= '0' && string[i] <= '9') && string[i] != '-' &&
1410
        string[i] != '.' && string[i] != ':' && string[i] != '_')
1411
      return MS_FALSE;
1412
  }
1413

1414
  return MS_TRUE;
1415
}
1416

1417
/*
1418
 * Concatenate pszSrc to pszDest and reallocate memory if necessary.
1419
 */
1420
char *msStringConcatenate(char *pszDest, const char *pszSrc) {
67,124✔
1421
  int nLen;
1422

1423
  if (pszSrc == NULL)
67,124✔
1424
    return pszDest;
1425

1426
  /* if destination is null, allocate memory */
1427
  if (pszDest == NULL) {
67,122✔
1428
    pszDest = msStrdup(pszSrc);
8,465✔
1429
  } else { /* if dest is not null, reallocate memory */
1430
    char *pszTemp;
1431

1432
    nLen = strlen(pszDest) + strlen(pszSrc);
58,657✔
1433

1434
    pszTemp = (char *)realloc(pszDest, nLen + 1);
58,657✔
1435
    if (pszTemp) {
58,657✔
1436
      pszDest = pszTemp;
1437
      strcat(pszDest, pszSrc);
1438
      pszDest[nLen] = '\0';
58,657✔
1439
    } else {
UNCOV
1440
      msSetError(MS_MEMERR, "Error while reallocating memory.",
×
1441
                 "msStringConcatenate()");
UNCOV
1442
      return NULL;
×
1443
    }
1444
  }
1445

1446
  return pszDest;
1447
}
1448

1449
char *msJoinStrings(char **array, int arrayLength, const char *delimiter) {
×
1450
  char *string;
1451
  int stringLength = 0;
1452
  int delimiterLength;
1453
  int i;
1454

1455
  if (!array || arrayLength <= 0 || !delimiter)
×
1456
    return NULL;
1457

1458
  delimiterLength = strlen(delimiter);
×
1459

UNCOV
1460
  for (i = 0; i < arrayLength; i++)
×
1461
    stringLength += strlen(array[i]) + delimiterLength;
×
1462

UNCOV
1463
  string = (char *)calloc(stringLength + 1, sizeof(char));
×
UNCOV
1464
  MS_CHECK_ALLOC(string, (stringLength + 1) * sizeof(char), NULL);
×
1465
  string[0] = '\0';
×
1466

UNCOV
1467
  for (i = 0; i < arrayLength - 1; i++) {
×
UNCOV
1468
    strlcat(string, array[i], stringLength);
×
1469
    strlcat(string, delimiter, stringLength);
1470
  }
UNCOV
1471
  strlcat(string, array[i], stringLength); /* add last element, no delimiter */
×
1472

1473
  return string;
1474
}
1475

1476
#define HASH_SIZE 16
1477
/*
1478
 * Return a hashed string for a given input string.
1479
 * The caller should free the return value.
1480
 */
UNCOV
1481
char *msHashString(const char *pszStr) {
×
UNCOV
1482
  unsigned char sums[HASH_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0,
×
1483
                                   0, 0, 0, 0, 0, 0, 0, 0};
1484
  char *pszOutBuf = NULL;
1485
  size_t bufferSize = 0;
1486
  int i = 0;
1487

1488
  bufferSize = HASH_SIZE * 2 + 1;
1489
  pszOutBuf = (char *)msSmallMalloc(bufferSize);
×
1490

UNCOV
1491
  for (i = 0; pszStr && pszStr[i]; i++) {
×
UNCOV
1492
    sums[i % HASH_SIZE] += (unsigned char)(pszStr[i]);
×
1493
  }
1494

UNCOV
1495
  for (i = 0; i < HASH_SIZE; i++) {
×
UNCOV
1496
    snprintf(pszOutBuf + i * 2, bufferSize - (i * 2), "%02x", sums[i]);
×
1497
  }
1498

UNCOV
1499
  return pszOutBuf;
×
1500
}
1501

1502
char *msCommifyString(char *str) {
117✔
1503
  int i, j, old_length, new_length;
1504
  int num_commas = 0, num_decimal_points = 0;
1505
  int add_commas;
1506

1507
  char comma = ',', decimal_point = '.';
1508

1509
  if (!str)
117✔
1510
    return NULL;
1511

1512
  num_decimal_points = msCountChars(str, decimal_point);
117✔
1513
  if (num_decimal_points > 1)
117✔
1514
    return str;
1515

1516
  old_length = strlen(str);
117✔
1517
  if (num_decimal_points == 0) {
117✔
UNCOV
1518
    num_commas = floor((old_length - 1) / 3);
×
1519
    add_commas = 1; /* add commas right away */
1520
  } else {
1521
    num_commas =
117✔
1522
        floor(((old_length - strlen(strchr(str, decimal_point))) - 1) / 3);
117✔
1523
    add_commas = 0; /* wait until after the decimal point */
1524
  }
1525

1526
  if (num_commas < 1)
117✔
1527
    return str; /* nothing to add */
1528

1529
  new_length = old_length + num_commas;
114✔
1530
  str = (char *)msSmallRealloc(str, new_length + 1);
114✔
1531
  str[new_length] = '\0';
114✔
1532

1533
  j = 0;
1534
  for (i = new_length - 1; i >= 0;
1,824✔
1535
       i--) { /* step backwards through the string */
1536

1537
    if (num_decimal_points == 1 &&
1,824✔
1538
        add_commas == 0) { /* to the right of the decimal point, no commas */
1,824✔
1539
      str[i] = str[i - num_commas];
684✔
1540
      if (str[i] == decimal_point)
684✔
1541
        add_commas = 1;
1542
    } else if (add_commas == 1 && j > 2) { /* need a comma */
1,140✔
1543
      str[i] = comma;
285✔
1544
      num_commas--; /* need one fewer now */
285✔
1545
      j = 0;        /* reset */
1546
    } else {
1547
      str[i] = str[i - num_commas]; /* shift to the right */
855✔
1548
      j++;
855✔
1549
    }
1550

1551
    if (num_commas == 0)
1,824✔
1552
      break; /* done, rest of string is ok "as is" */
1553
  }
1554

1555
  return str;
1556
}
1557

1558
/************************************************************************/
1559
/*                              msToString()                            */
1560
/************************************************************************/
1561

1562
char *msToString(const char *format, double value) {
263✔
1563
  bool pctAlreadyFound = false;
1564
  // Validate that the formatting string is OK for a single input double value
1565
  int extra_size = 0;
1566
  for (const char *ptr = format; *ptr; ++ptr) {
530✔
1567
    if (*ptr == '%' && ptr[1] == '%') {
273✔
1568
      ++ptr;
2✔
1569
    } else if (*ptr == '%') {
271✔
1570
      if (pctAlreadyFound) {
265✔
1571
        msSetError(MS_MISCERR, "More than one conversion specifier",
2✔
1572
                   "msToString()");
1573
        return nullptr;
2✔
1574
      }
1575
      pctAlreadyFound = true;
1576
      ++ptr;
263✔
1577
      // Skip flag characters
1578
      while (*ptr == '+' || *ptr == '-' || *ptr == ' ' || *ptr == '\'' ||
265✔
1579
             *ptr == '0') {
1580
        ++ptr;
2✔
1581
      }
1582
      // Skip width
1583
      if (*ptr >= '1' && *ptr <= '9') {
263✔
1584
        extra_size = atoi(ptr);
1585
        do {
1586
          ++ptr;
22✔
1587
        } while (*ptr >= '0' && *ptr <= '9');
22✔
1588
        if (extra_size > 1024) {
8✔
1589
          // To avoid arbitrary memory allocatin
1590
          msSetError(MS_MISCERR, "Too large width", "msToString()");
2✔
1591
          return nullptr;
2✔
1592
        }
1593
      }
1594
      // maximum double value is of the order of ~1e308
1595
      if (extra_size < DBL_MAX_10_EXP)
261✔
1596
        extra_size = DBL_MAX_10_EXP;
1597
      extra_size += 32; // extra margin
261✔
1598

1599
      // Skip precision
1600
      if (*ptr == '.') {
261✔
1601
        ++ptr;
235✔
1602
        while (*ptr >= '0' && *ptr <= '9')
470✔
1603
          ++ptr;
235✔
1604
      }
1605
      // Check conversion specifier
1606
      if (!(*ptr == 'e' || *ptr == 'E' || *ptr == 'f' || *ptr == 'F' ||
261✔
1607
            *ptr == 'g' || *ptr == 'G')) {
1608
        msSetError(MS_MISCERR, "Invalid conversion specifier", "msToString()");
2✔
1609
        return nullptr;
2✔
1610
      }
1611
    }
1612
  }
1613
  // extra_size / 3 if thousands' grouping characters is used
1614
  const size_t nBufferSize = strlen(format) + extra_size + (extra_size / 3) + 1;
257✔
1615
  char *ret = static_cast<char *>(msSmallMalloc(nBufferSize));
257✔
1616
  snprintf(ret, nBufferSize, format, value);
1617
  return ret;
257✔
1618
}
1619

1620
/* -------------------------------------------------------------------------------
1621
 */
1622
/*       Replace all occurrences of old with new in str. */
1623
/*       It is assumed that str was dynamically created using malloc. */
1624
/*       Same function as msReplaceSubstring but this is case insensitive */
1625
/* -------------------------------------------------------------------------------
1626
 */
1627
char *msCaseReplaceSubstring(char *str, const char *old, const char *newstr) {
6,579✔
1628
  size_t str_len, old_len, new_len, tmp_offset;
1629
  char *tmp_ptr;
1630

1631
  /*
1632
  ** If old is not found then leave str alone
1633
  */
1634
  if ((tmp_ptr = (char *)strcasestr(str, old)) == NULL)
6,579✔
1635
    return (str);
1636

1637
  if (newstr == NULL)
163✔
1638
    newstr = "";
1639

1640
  /*
1641
  ** Grab some info about incoming strings
1642
  */
1643
  str_len = strlen(str);
163✔
1644
  old_len = strlen(old);
163✔
1645
  new_len = strlen(newstr);
163✔
1646

1647
  /*
1648
  ** Now loop until old is NOT found in new
1649
  */
1650
  while (tmp_ptr != NULL) {
326✔
1651

1652
    /*
1653
    ** re-allocate memory for buf assuming 1 replacement of old with new
1654
    ** don't bother reallocating if old is larger than new)
1655
    */
1656
    if (old_len < new_len) {
163✔
1657
      tmp_offset = tmp_ptr - str;
68✔
1658
      str_len = str_len - old_len + new_len;
68✔
1659
      str = (char *)msSmallRealloc(
68✔
1660
          str, (str_len + 1)); /* make new space for a copy */
1661
      tmp_ptr = str + tmp_offset;
68✔
1662
    }
1663

1664
    /*
1665
    ** Move the trailing part of str to make some room unless old_len == new_len
1666
    */
1667
    if (old_len != new_len) {
163✔
1668
      memmove(tmp_ptr + new_len, tmp_ptr + old_len,
151✔
1669
              strlen(tmp_ptr) - old_len + 1);
151✔
1670
    }
1671

1672
    /*
1673
    ** Now copy new over old
1674
    */
1675
    memcpy(tmp_ptr, newstr, new_len);
1676

1677
    /*
1678
    ** And look for more matches in the rest of the string
1679
    */
1680
    tmp_ptr = (char *)strcasestr(tmp_ptr + new_len, old);
163✔
1681
  }
1682

1683
  return (str);
1684
}
1685

1686
/*
1687
** Converts a 2 character hexadecimal string to an integer.
1688
*/
1689
int msHexToInt(char *hex) {
10,683✔
1690
  int number;
1691

1692
  number = (hex[0] >= 'A' ? ((hex[0] & 0xdf) - 'A') + 10 : (hex[0] - '0'));
10,683✔
1693
  number *= 16;
10,683✔
1694
  number += (hex[1] >= 'A' ? ((hex[1] & 0xdf) - 'A') + 10 : (hex[1] - '0'));
10,683✔
1695

1696
  return (number);
10,683✔
1697
}
1698

1699
/*
1700
** Use FRIBIDI to encode the string.
1701
** The return value must be freed by the caller.
1702
*/
1703
#ifdef USE_FRIBIDI
UNCOV
1704
char *msGetFriBidiEncodedString(const char *string, const char *encoding) {
×
1705
  FriBidiChar logical[MAX_STR_LEN];
1706
  FriBidiParType base;
1707
  size_t len;
1708

1709
#ifdef FRIBIDI_NO_CHARSETS
1710
  iconv_t to_ucs4, from_ucs4;
1711
#else
1712
  FriBidiCharSet to_char_set_num;
1713
  FriBidiCharSet from_char_set_num;
1714
#endif
1715

1716
  len = strlen(string);
×
1717

1718
#ifdef FRIBIDI_NO_CHARSETS
1719
  to_ucs4 = iconv_open("WCHAR_T", encoding);
1720
  from_ucs4 = iconv_open("UTF-8", "WCHAR_T");
1721
#else
UNCOV
1722
  to_char_set_num = fribidi_parse_charset((char *)encoding);
×
1723
  from_char_set_num = fribidi_parse_charset("UTF-8");
×
1724
#endif
1725

1726
#ifdef FRIBIDI_NO_CHARSETS
1727
  if (to_ucs4 == (iconv_t)(-1) || from_ucs4 == (iconv_t)(-1))
1728
#else
UNCOV
1729
  if (!to_char_set_num || !from_char_set_num)
×
1730
#endif
1731
  {
UNCOV
1732
    msSetError(MS_IDENTERR, "Encoding not supported (%s).",
×
1733
               "msGetFriBidiEncodedString()", encoding);
UNCOV
1734
    return NULL;
×
1735
  }
1736

1737
#ifdef FRIBIDI_NO_CHARSETS
1738
  {
1739
    char *st = string, *ust = (char *)logical;
1740
    int in_len = (int)len;
1741
    len = sizeof logical;
1742
    iconv(to_ucs4, &st, &in_len, &ust, (int *)&len);
1743
    len = (FriBidiChar *)ust - logical;
1744
  }
1745
#else
UNCOV
1746
  len =
×
UNCOV
1747
      fribidi_charset_to_unicode(to_char_set_num, (char *)string, len, logical);
×
1748
#endif
1749

1750
  {
1751
    FriBidiChar *visual;
1752
    char outstring[MAX_STR_LEN];
1753
    FriBidiStrIndex *ltov, *vtol;
1754
    FriBidiLevel *levels;
1755
    FriBidiStrIndex new_len;
1756
    fribidi_boolean log2vis;
1757

UNCOV
1758
    visual = (FriBidiChar *)msSmallMalloc(sizeof(FriBidiChar) * (len + 1));
×
1759
    ltov = NULL;
1760
    vtol = NULL;
1761
    levels = NULL;
1762

1763
    // fribidi_log2vis() doesn't support multi-line paragraphs.
1764
    // See:
1765
    // https://lists.freedesktop.org/archives/fribidi/2008-January/000515.html
1766
    for (size_t i = 0; i < len; i++) {
×
1767
      if (logical[i] == '\n') {
×
UNCOV
1768
        msSetError(
×
1769
            MS_IDENTERR,
1770
            "Input string is a multi-line paragraph, which is not supported.",
1771
            "msGetFriBidiEncodedString()");
1772
        msFree(visual);
×
UNCOV
1773
        return NULL;
×
1774
      }
1775
    }
1776

1777
    /* Create a bidi string. */
UNCOV
1778
    log2vis = fribidi_log2vis(logical, len, &base,
×
1779
                              /* output */
1780
                              visual, ltov, vtol, levels);
1781

UNCOV
1782
    if (!log2vis) {
×
UNCOV
1783
      msSetError(MS_IDENTERR, "Failed to create bidi string.",
×
1784
                 "msGetFriBidiEncodedString()");
UNCOV
1785
      msFree(visual);
×
UNCOV
1786
      return NULL;
×
1787
    }
1788

1789
    /* Convert it to utf-8 for display. */
1790
#ifdef FRIBIDI_NO_CHARSETS
1791
    {
1792
      char *str = outstring, *ust = (char *)visual;
1793
      int in_len = len * sizeof visual[0];
1794
      new_len = sizeof outstring;
1795
      iconv(from_ucs4, &ust, &in_len, &str, (int *)&new_len);
1796
      *str = '\0';
1797
      new_len = str - outstring;
1798
    }
1799
#else
1800
    new_len =
1801
        fribidi_unicode_to_charset(from_char_set_num, visual, len, outstring);
×
1802

1803
    /* scan str and compress out FRIBIDI_CHAR_FILL UTF8 characters */
1804

1805
    int j = 0;
1806
    for (int i = 0; i < new_len; i++, j++) {
×
UNCOV
1807
      if (outstring[i] == '\xef' && outstring[i + 1] == '\xbb' &&
×
UNCOV
1808
          outstring[i + 2] == '\xbf') {
×
1809
        i += 3;
×
1810
      }
UNCOV
1811
      if (i != j) {
×
UNCOV
1812
        outstring[j] = outstring[i];
×
1813
      }
1814
    }
UNCOV
1815
    outstring[j] = '\0';
×
1816

1817
#endif
1818

UNCOV
1819
    msFree(visual);
×
UNCOV
1820
    return msStrdup(outstring);
×
1821
  }
1822
}
1823
#endif
1824

1825
/*
1826
** Simple charset converter. Converts string from specified encoding to UTF-8.
1827
** The return value must be freed by the caller.
1828
*/
UNCOV
1829
char *msGetEncodedString(const char *string, const char *encoding) {
×
1830
#ifdef USE_ICONV
1831
  iconv_t cd = NULL;
1832
  const char *inp;
1833
  char *outp, *out = NULL;
1834
  size_t len, bufsize, bufleft;
1835
  assert(encoding);
1836

1837
#ifdef USE_FRIBIDI
1838
  msAcquireLock(TLOCK_FRIBIDI);
×
UNCOV
1839
  if (fribidi_parse_charset((char *)encoding)) {
×
1840
    char *ret = msGetFriBidiEncodedString(string, encoding);
×
UNCOV
1841
    msReleaseLock(TLOCK_FRIBIDI);
×
1842
    return ret;
×
1843
  }
UNCOV
1844
  msReleaseLock(TLOCK_FRIBIDI);
×
1845
#endif
1846
  len = strlen(string);
×
1847

UNCOV
1848
  if (len == 0 || strcasecmp(encoding, "UTF-8") == 0)
×
1849
    return msStrdup(string); /* Nothing to do: string already in UTF-8 */
×
1850

UNCOV
1851
  cd = iconv_open("UTF-8", encoding);
×
1852
  if (cd == (iconv_t)-1) {
×
1853
    msSetError(MS_IDENTERR, "Encoding not supported by libiconv (%s).",
×
1854
               "msGetEncodedString()", encoding);
1855
    return NULL;
×
1856
  }
1857

1858
  bufsize = len * 6 + 1; /* Each UTF-8 char can be up to 6 bytes */
×
UNCOV
1859
  inp = string;
×
UNCOV
1860
  out = (char *)malloc(bufsize);
×
1861
  if (out == NULL) {
×
UNCOV
1862
    msSetError(MS_MEMERR, NULL, "msGetEncodedString()");
×
1863
    iconv_close(cd);
×
UNCOV
1864
    return NULL;
×
1865
  }
1866
  strlcpy(out, string, bufsize);
1867
  outp = out;
×
1868

1869
  bufleft = bufsize;
×
1870

1871
  while (len > 0) {
×
1872
    const size_t iconv_status =
UNCOV
1873
        msIconv(cd, (char **)&inp, &len, &outp, &bufleft);
×
1874
    if (iconv_status == static_cast<size_t>(-1)) {
×
UNCOV
1875
      msFree(out);
×
1876
      iconv_close(cd);
×
UNCOV
1877
      return msStrdup(string);
×
1878
    }
1879
  }
UNCOV
1880
  out[bufsize - bufleft] = '\0';
×
1881

UNCOV
1882
  iconv_close(cd);
×
1883

1884
  return out;
1885
#else
1886
  if (*string == '\0' || (encoding && strcasecmp(encoding, "UTF-8") == 0))
1887
    return msStrdup(string); /* Nothing to do: string already in UTF-8 */
1888

1889
  msSetError(MS_MISCERR, "Not implemented since Iconv is not enabled.",
1890
             "msGetEncodedString()");
1891
  return NULL;
1892
#endif
1893
}
1894

1895
char *msConvertWideStringToUTF8(const wchar_t *string, const char *encoding) {
459✔
1896
#ifdef USE_ICONV
1897

1898
  char *output = NULL;
1899
  const char *errormessage = NULL;
1900
  iconv_t cd = NULL;
1901
  size_t nStr;
1902
  size_t nInSize;
1903
  size_t nOutSize;
1904
  size_t nBufferSize;
1905

1906
  char *pszUTF8 = NULL;
459✔
1907
  const wchar_t *pwszWide = NULL;
459✔
1908

1909
  if (string != NULL) {
459✔
1910
    nStr = wcslen(string);
459✔
1911
    nBufferSize = ((nStr * 6) + 1);
459✔
1912
    output = (char *)msSmallMalloc(nBufferSize);
459✔
1913

1914
    if (nStr == 0) {
459✔
1915
      /* return an empty 8 byte string */
UNCOV
1916
      output[0] = '\0';
×
UNCOV
1917
      return output;
×
1918
    }
1919

1920
    cd = iconv_open("UTF-8", encoding);
459✔
1921

1922
    nOutSize = nBufferSize;
459✔
1923
    if ((iconv_t)-1 != cd) {
459✔
1924
      nInSize = sizeof(wchar_t) * nStr;
459✔
1925
      pszUTF8 = output;
459✔
1926
      pwszWide = string;
459✔
1927
      size_t iconv_status =
1928
          msIconv(cd, (char **)&pwszWide, &nInSize, &pszUTF8, &nOutSize);
459✔
1929
      if ((size_t)-1 == iconv_status) {
459✔
UNCOV
1930
        switch (errno) {
×
1931
        case E2BIG:
1932
          errormessage = "There is not sufficient room in buffer";
1933
          break;
UNCOV
1934
        case EILSEQ:
×
1935
          errormessage =
1936
              "An invalid multibyte sequence has been encountered in the input";
UNCOV
1937
          break;
×
1938
        case EINVAL:
×
1939
          errormessage = "An incomplete multibyte sequence has been "
1940
                         "encountered in the input";
UNCOV
1941
          break;
×
UNCOV
1942
        default:
×
1943
          errormessage = "Unknown";
1944
          break;
×
1945
        }
UNCOV
1946
        msSetError(MS_MISCERR,
×
1947
                   "Unable to convert string in encoding '%s' to UTF8 %s",
1948
                   "msConvertWideStringToUTF8()", encoding, errormessage);
1949
        iconv_close(cd);
×
UNCOV
1950
        msFree(output);
×
1951
        return NULL;
×
1952
      }
1953
      iconv_close(cd);
459✔
1954
    } else {
UNCOV
1955
      msSetError(MS_MISCERR, "Encoding not supported by libiconv (%s).",
×
1956
                 "msConvertWideStringToUTF8()", encoding);
UNCOV
1957
      msFree(output);
×
UNCOV
1958
      return NULL;
×
1959
    }
1960

1961
  } else {
1962
    /* we were given a NULL wide string, nothing we can do here */
1963
    return NULL;
1964
  }
1965

1966
  /* NULL-terminate the output string */
1967
  output[nBufferSize - nOutSize] = '\0';
459✔
1968
  return output;
459✔
1969
#else
1970
  msSetError(MS_MISCERR, "Not implemented since Iconv is not enabled.",
1971
             "msConvertWideStringToUTF8()");
1972
  return NULL;
1973
#endif
1974
}
1975

UNCOV
1976
wchar_t *msConvertWideStringFromUTF8(const char *string, const char *encoding) {
×
1977
#ifdef USE_ICONV
1978
  wchar_t *output = NULL;
1979
  const char *errormessage = NULL;
1980
  iconv_t cd = NULL;
1981
  size_t nStr;
1982
  size_t nInSize;
1983
  size_t nOutSize;
1984
  size_t nBufferSize;
1985

1986
  const char *pszUTF8 = NULL;
×
UNCOV
1987
  wchar_t *pwszWide = NULL;
×
1988

UNCOV
1989
  if (string != NULL) {
×
1990
    nStr = strlen(string);
×
1991
    nBufferSize = ((nStr * 6) + 1);
×
UNCOV
1992
    output = (wchar_t *)msSmallMalloc(nBufferSize);
×
1993

1994
    if (nStr == 0) {
×
1995
      /* return an empty 8 byte string */
1996
      output[0] = '\0';
×
1997
      return output;
×
1998
    }
1999

2000
    cd = iconv_open(encoding, "UTF-8");
×
2001

UNCOV
2002
    nOutSize = nBufferSize;
×
2003
    if ((iconv_t)-1 != cd) {
×
2004
      nInSize = sizeof(char) * nStr;
×
UNCOV
2005
      pszUTF8 = string;
×
UNCOV
2006
      pwszWide = output;
×
UNCOV
2007
      size_t iconv_status = msIconv(cd, (char **)&pszUTF8, &nInSize,
×
2008
                                    (char **)&pwszWide, &nOutSize);
UNCOV
2009
      if ((size_t)-1 == iconv_status) {
×
UNCOV
2010
        switch (errno) {
×
2011
        case E2BIG:
2012
          errormessage = "There is not sufficient room in buffer";
2013
          break;
UNCOV
2014
        case EILSEQ:
×
2015
          errormessage =
2016
              "An invalid multibyte sequence has been encountered in the input";
UNCOV
2017
          break;
×
2018
        case EINVAL:
×
2019
          errormessage = "An incomplete multibyte sequence has been "
2020
                         "encountered in the input";
UNCOV
2021
          break;
×
UNCOV
2022
        default:
×
2023
          errormessage = "Unknown";
2024
          break;
×
2025
        }
UNCOV
2026
        msSetError(MS_MISCERR,
×
2027
                   "Unable to convert string in UTF8 to encoding '%s' %s",
2028
                   "msConvertWideStringFromUTF8()", encoding, errormessage);
2029
        iconv_close(cd);
×
UNCOV
2030
        msFree(output);
×
2031
        return NULL;
×
2032
      }
UNCOV
2033
      iconv_close(cd);
×
2034
    } else {
UNCOV
2035
      msSetError(MS_MISCERR, "Encoding not supported by libiconv (%s).",
×
2036
                 "msConvertWideStringFromUTF8()", encoding);
UNCOV
2037
      msFree(output);
×
UNCOV
2038
      return NULL;
×
2039
    }
2040
  } else {
2041
    /* we were given a NULL wide string, nothing we can do here */
2042
    return NULL;
2043
  }
2044

2045
  /* NULL-terminate the output string */
UNCOV
2046
  if (nOutSize >= sizeof(wchar_t))
×
UNCOV
2047
    *((wchar_t *)pwszWide) = L'\0';
×
2048

2049
  return output;
2050
#else
2051
  msSetError(MS_MISCERR, "Not implemented since Iconv is not enabled.",
2052
             "msConvertWideStringFromUTF8()");
2053
  return NULL;
2054
#endif
2055
}
2056

2057
/*
2058
** Returns the next glyph in string and advances *in_ptr to the next
2059
** character.
2060
**
2061
** If out_string is not NULL then the character (bytes) is copied to this
2062
** buffer and null-terminated. out_string must be a pre-allocated buffer of
2063
** at least 11 bytes.
2064
**
2065
** The function returns the number of bytes in this glyph.
2066
**
2067
** This function treats 3 types of glyph encodings:
2068
*   - as an html entity, for example &#123; , &#x1af; , or &eacute;
2069
*   - as an utf8 encoded character
2070
*   - if utf8 decoding fails, as a raw character
2071
*
2072
** This function mimics the character decoding function used in gdft.c of
2073
* libGD. It is necessary to have the same behavior, as input strings must be
2074
* split into the same glyphs as what gd does.
2075
**
2076
** In UTF-8, the number of leading 1 bits in the first byte specifies the
2077
** number of bytes in the entire sequence.
2078
** Source: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
2079
**
2080
** U-00000000 U-0000007F: 0xxxxxxx
2081
** U-00000080 U-000007FF: 110xxxxx 10xxxxxx
2082
** U-00000800 U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
2083
** U-00010000 U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
2084
** U-00200000 U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
2085
** U-04000000 U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
2086
*/
2087
int msGetNextGlyph(const char **in_ptr, char *out_string) {
×
2088
  unsigned char in;
2089
  int numbytes = 0;
2090
  unsigned int unicode;
2091
  int i;
2092

2093
  in = (unsigned char)**in_ptr;
×
2094

UNCOV
2095
  if (in == 0)
×
2096
    return -1; /* Empty string */
UNCOV
2097
  if ((numbytes = msGetUnicodeEntity(*in_ptr, &unicode)) > 0) {
×
2098
    if (out_string) {
×
2099
      for (i = 0; i < numbytes; i++) {
×
UNCOV
2100
        out_string[i] = (*in_ptr)[i];
×
2101
      }
UNCOV
2102
      out_string[numbytes] = '\0';
×
2103
    }
UNCOV
2104
    *in_ptr += numbytes;
×
UNCOV
2105
    return numbytes;
×
2106
  }
UNCOV
2107
  if (in < 0xC0) {
×
2108
    /*
2109
     * Handles properly formed UTF-8 characters between
2110
     * 0x01 and 0x7F.  Also treats \0 and naked trail
2111
     * bytes 0x80 to 0xBF as valid characters representing
2112
     * themselves.
2113
     */
2114
    /*goto end of loop to return just the char*/
UNCOV
2115
  } else if (in < 0xE0) {
×
2116
    if (((*in_ptr)[1] & 0xC0) == 0x80) {
×
2117
      if (out_string) {
×
UNCOV
2118
        out_string[0] = in;
×
2119
        out_string[1] = (*in_ptr)[1];
×
2120
        out_string[2] = '\0';
×
2121
      }
2122
      *in_ptr += 2;
×
2123
      return 2; /*110xxxxx 10xxxxxx*/
×
2124
    }
2125
  } else if (in < 0xF0) {
×
2126
    if (((*in_ptr)[1] & 0xC0) == 0x80 && ((*in_ptr)[2] & 0xC0) == 0x80) {
×
UNCOV
2127
      if (out_string) {
×
2128
        out_string[0] = in;
×
2129
        *in_ptr += numbytes;
×
UNCOV
2130
        out_string[1] = (*in_ptr)[1];
×
2131
        out_string[2] = (*in_ptr)[2];
×
2132
        out_string[3] = '\0';
×
2133
      }
2134
      *in_ptr += 3;
×
2135
      return 3; /* 1110xxxx 10xxxxxx 10xxxxxx */
×
2136
    }
2137
  } else if (in < 0xF8) {
×
2138
    if (((*in_ptr)[1] & 0xC0) == 0x80 && ((*in_ptr)[2] & 0xC0) == 0x80 &&
×
2139
        ((*in_ptr)[3] & 0xC0) == 0x80) {
×
UNCOV
2140
      if (out_string) {
×
2141
        out_string[0] = in;
×
2142
        out_string[1] = (*in_ptr)[1];
×
UNCOV
2143
        out_string[2] = (*in_ptr)[2];
×
2144
        out_string[3] = (*in_ptr)[3];
×
2145
        out_string[4] = '\0';
×
2146
      }
2147
      *in_ptr += 4;
×
2148
      return 4; /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
×
2149
    }
2150
  } else if (in < 0xFC) {
×
2151
    if (((*in_ptr)[1] & 0xC0) == 0x80 && ((*in_ptr)[2] & 0xC0) == 0x80 &&
×
2152
        ((*in_ptr)[3] & 0xC0) == 0x80 && ((*in_ptr)[4] & 0xC0) == 0x80) {
×
2153
      if (out_string) {
×
UNCOV
2154
        out_string[0] = in;
×
2155
        out_string[1] = (*in_ptr)[1];
×
2156
        out_string[2] = (*in_ptr)[2];
×
UNCOV
2157
        out_string[3] = (*in_ptr)[3];
×
2158
        out_string[4] = (*in_ptr)[4];
×
2159
        out_string[5] = '\0';
×
2160
      }
2161
      *in_ptr += 5;
×
2162
      return 5; /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
×
2163
    }
2164
  } else if (in < 0xFE) {
×
2165
    if (((*in_ptr)[1] & 0xC0) == 0x80 && ((*in_ptr)[2] & 0xC0) == 0x80 &&
×
2166
        ((*in_ptr)[3] & 0xC0) == 0x80 && ((*in_ptr)[4] & 0xC0) == 0x80 &&
×
2167
        ((*in_ptr)[5] & 0xC0) == 0x80) {
×
2168
      if (out_string) {
×
2169
        out_string[0] = in;
×
UNCOV
2170
        out_string[1] = (*in_ptr)[1];
×
2171
        out_string[2] = (*in_ptr)[2];
×
2172
        out_string[3] = (*in_ptr)[3];
×
UNCOV
2173
        out_string[4] = (*in_ptr)[4];
×
UNCOV
2174
        out_string[5] = (*in_ptr)[5];
×
UNCOV
2175
        out_string[6] = '\0';
×
2176
      }
2177
      *in_ptr += 6;
×
2178
      return 6; /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
×
2179
    }
2180
  }
2181

UNCOV
2182
  if (out_string) {
×
UNCOV
2183
    out_string[0] = in;
×
UNCOV
2184
    out_string[1] = '\0'; /* 0xxxxxxx */
×
2185
  }
UNCOV
2186
  (*in_ptr)++;
×
2187
  return 1;
×
2188
}
2189

2190
/*
2191
** Returns the number of glyphs in string
2192
*/
2193
int msGetNumGlyphs(const char *in_ptr) {
×
2194
  int numchars = 0;
2195

UNCOV
2196
  while (msGetNextGlyph(&in_ptr, NULL) != -1)
×
UNCOV
2197
    numchars++;
×
2198

UNCOV
2199
  return numchars;
×
2200
}
2201

2202
static int cmp_entities(const void *e1, const void *e2) {
2203
  struct mapentities_s *en1 = (struct mapentities_s *)e1;
2204
  struct mapentities_s *en2 = (struct mapentities_s *)e2;
2205
  return strcmp(en1->name, en2->name);
189✔
2206
}
2207
/*
2208
 * this function tests if the string pointed by inptr represents
2209
 * an HTML entity, in decimal form ( e.g. &#197;), in hexadecimal
2210
 * form ( e.g. &#x6C34; ), or from html 4.0 spec ( e.g. &eacute; )
2211
 * - returns returns 0 if the string doesn't represent such an entity.
2212
 * - if the string does start with such entity,it returns the number of
2213
 * bytes occupied by said entity, and stores the unicode value in *unicode
2214
 */
2215
int msGetUnicodeEntity(const char *inptr, unsigned int *unicode) {
2,546✔
2216
  unsigned char *in = (unsigned char *)inptr;
2217
  int l, val = 0;
2218
  if (*in == '&') {
2,546✔
2219
    in++;
2,546✔
2220
    if (*in == '#') {
2,546✔
2221
      in++;
2,519✔
2222
      if (*in == 'x' || *in == 'X') {
2,519✔
2223
        in++;
×
2224
        for (l = 3; l < 8; l++) {
×
2225
          char byte;
UNCOV
2226
          if (*in >= '0' && *in <= '9')
×
UNCOV
2227
            byte = *in - '0';
×
2228
          else if (*in >= 'a' && *in <= 'f')
×
2229
            byte = *in - 'a' + 10;
×
UNCOV
2230
          else if (*in >= 'A' && *in <= 'F')
×
2231
            byte = *in - 'A' + 10;
×
2232
          else
2233
            break;
UNCOV
2234
          in++;
×
UNCOV
2235
          val = (val * 16) + byte;
×
2236
        }
UNCOV
2237
        if (*in == ';' && l > 3) {
×
UNCOV
2238
          *unicode = val;
×
UNCOV
2239
          return ++l;
×
2240
        }
2241
      } else {
2242
        for (l = 2; l < 8; l++) {
13,732✔
2243
          if (*in >= '0' && *in <= '9') {
13,732✔
2244
            val = val * 10 + *in - '0';
11,213✔
2245
            in++;
11,213✔
2246
          } else
2247
            break;
2248
        }
2249
        if (*in == ';' && l > 2) {
2,519✔
2250
          *unicode = val;
2,519✔
2251
          return ++l;
2,519✔
2252
        }
2253
      }
2254
    } else {
2255
      char entity_name_buf[MAP_ENTITY_NAME_LENGTH_MAX + 1];
2256
      char *p;
2257
      struct mapentities_s key;
2258
      key.name = p = entity_name_buf;
2259
      key.value = 0; // unused, but makes cppcheck happy
2260
      for (l = 1; l <= MAP_ENTITY_NAME_LENGTH_MAX + 1; l++) {
135✔
2261
        if (*in == '\0') /*end of string before possible entity: return*/
135✔
2262
          break;
2263
        if (*in == ';') { /*possible end of entity: do a lookup*/
135✔
2264
          *p++ = '\0';
27✔
2265
          const struct mapentities_s *res =
2266
              static_cast<const struct mapentities_s *>(
2267
                  bsearch(&key, mapentities, MAP_NR_OF_ENTITIES,
2268
                          sizeof(mapentities[0]), cmp_entities));
2269
          if (res) {
27✔
2270
            *unicode = res->value;
27✔
2271
            return ++l;
27✔
2272
          }
2273
          break; /*the string was of the form of an entity but didn't correspond
2274
                    to an existing one: return*/
2275
        }
2276
        *p++ = *in;
108✔
2277
        in++;
108✔
2278
      }
2279
    }
2280
  }
2281
  return 0;
2282
}
2283

2284
/**
2285
 * msStringIsInteger()
2286
 *
2287
 * determines whether a given string is an integer
2288
 *
2289
 * @param string the string to be tested
2290
 *
2291
 * @return MS_SUCCESS or MS_FAILURE
2292
 */
2293

2294
int msStringIsInteger(const char *string) {
76✔
2295
  int length, i;
2296

2297
  length = strlen(string);
76✔
2298

2299
  if (length == 0)
76✔
2300
    return MS_FAILURE;
2301

2302
  for (i = 0; i < length; i++) {
307✔
2303
    if (!isdigit(string[i]))
261✔
2304
      return MS_FAILURE;
2305
  }
2306

2307
  return MS_SUCCESS;
2308
}
2309

2310
/************************************************************************/
2311
/*                             msStrdup()                               */
2312
/************************************************************************/
2313

2314
/* Safe version of msStrdup(). This function is taken from gdal/cpl. */
2315

2316
char *msStrdup(const char *pszString) {
1,337,359✔
2317
  size_t nStringLength;
2318
  char *pszReturn;
2319

2320
  if (pszString == NULL)
1,337,359✔
2321
    pszString = "";
2322

2323
  nStringLength = strlen(pszString) + 1; /* null terminated byte */
1,337,359✔
2324
  pszReturn = static_cast<char *>(malloc(nStringLength));
1,337,359✔
2325

2326
  if (pszReturn == NULL) {
1,337,359✔
UNCOV
2327
    fprintf(stderr, "msSmallMalloc(): Out of memory allocating %ld bytes.\n",
×
UNCOV
2328
            (long)strlen(pszString));
×
UNCOV
2329
    exit(1);
×
2330
  }
2331

2332
  memcpy(pszReturn, pszString, nStringLength);
2333

2334
  return pszReturn;
1,337,359✔
2335
}
2336

2337
/************************************************************************/
2338
/*                             msStringEscape()                         */
2339
/************************************************************************/
2340

2341
/* Checks if a string contains single or double quotes and escape them.
2342
   NOTE: the user must free the returned char* if it is different than the
2343
   one passed in */
2344

2345
char *msStringEscape(const char *pszString) {
385✔
2346
  char *string_tmp, *string_ptr;
2347
  int i, ncharstoescape = 0;
2348

2349
  if (pszString == NULL || strlen(pszString) == 0)
385✔
UNCOV
2350
    return msStrdup("");
×
2351

2352
  for (i = 0; pszString[i]; i++)
2,402✔
2353
    ncharstoescape += ((pszString[i] == '\"') || (pszString[i] == '\''));
2,081✔
2354

2355
  if (!ncharstoescape) {
385✔
2356
    return (char *)pszString;
2357
  }
2358

2359
  string_tmp = (char *)msSmallMalloc(strlen(pszString) + ncharstoescape + 1);
46✔
2360
  for (string_ptr = (char *)pszString, i = 0; *string_ptr != '\0';
422✔
2361
       ++string_ptr, ++i) {
376✔
2362
    if ((*string_ptr == '\"') || (*string_ptr == '\'')) {
376✔
2363
      string_tmp[i] = '\\';
64✔
2364
      ++i;
64✔
2365
    }
2366
    string_tmp[i] = *string_ptr;
376✔
2367
  }
2368

2369
  string_tmp[i] = '\0';
46✔
2370
  return string_tmp;
46✔
2371
}
2372

2373
std::string msStdStringEscape(const char *pszString) {
385✔
2374
  char *tmp = msStringEscape(pszString);
385✔
2375
  std::string ret(tmp);
385✔
2376
  if (tmp != pszString)
385✔
2377
    msFree(tmp);
46✔
2378
  return ret;
385✔
2379
}
2380

2381
/************************************************************************/
2382
/*                             msStringInArray()                        */
2383
/************************************************************************/
2384

2385
/* Check if a string is in a array */
2386
int msStringInArray(const char *pszString, char **array, int numelements) {
36✔
2387
  int i;
2388
  for (i = 0; i < numelements; ++i) {
89✔
2389
    if (strcasecmp(pszString, array[i]) == 0)
79✔
2390
      return MS_TRUE;
2391
  }
2392
  return MS_FALSE;
2393
}
2394

2395
bool msStringInArray(const char *pszString,
64✔
2396
                     const std::vector<std::string> &array) {
2397
  for (const auto &str : array) {
123✔
2398
    if (strcasecmp(pszString, str.c_str()) == 0)
65✔
2399
      return true;
2400
  }
2401
  return false;
2402
}
2403

2404
int msLayerEncodeShapeAttributes(layerObj *layer, shapeObj *shape) {
436✔
2405

2406
#ifdef USE_ICONV
2407
  iconv_t cd = NULL;
2408
  const char *inp;
2409
  char *outp, *out = NULL;
2410
  size_t len, bufsize, bufleft;
2411
  int i;
2412

2413
  if (!layer->encoding || !*layer->encoding ||
436✔
2414
      !strcasecmp(layer->encoding, "UTF-8"))
436✔
2415
    return MS_SUCCESS;
2416

2417
  cd = iconv_open("UTF-8", layer->encoding);
416✔
2418
  if (cd == (iconv_t)-1) {
416✔
UNCOV
2419
    msSetError(MS_IDENTERR, "Encoding not supported by libiconv (%s).",
×
2420
               "msGetEncodedString()", layer->encoding);
UNCOV
2421
    return MS_FAILURE;
×
2422
  }
2423

2424
  for (i = 0; i < shape->numvalues; i++) {
1,232✔
2425
    if (!shape->values[i] || (len = strlen(shape->values[i])) == 0) {
816✔
2426
      continue; /* Nothing to do */
146✔
2427
    }
2428

2429
    bufsize = len * 6 + 1; /* Each UTF-8 char can be up to 6 bytes */
670✔
2430
    inp = shape->values[i];
670✔
2431
    out = (char *)msSmallMalloc(bufsize);
670✔
2432

2433
    strlcpy(out, shape->values[i], bufsize);
670✔
2434
    outp = out;
670✔
2435

2436
    bufleft = bufsize;
670✔
2437

2438
    bool failedIconv = false;
2439
    while (len > 0) {
1,340✔
2440
      const size_t iconv_status =
2441
          msIconv(cd, (char **)&inp, &len, &outp, &bufleft);
670✔
2442
      if (iconv_status == static_cast<size_t>(-1)) {
670✔
2443
        failedIconv = true;
2444
        break;
2445
      }
2446
    }
2447
    if (failedIconv) {
670✔
UNCOV
2448
      msFree(out);
×
UNCOV
2449
      continue; /* silently ignore failed conversions */
×
2450
    }
2451
    out[bufsize - bufleft] = '\0';
670✔
2452
    msFree(shape->values[i]);
670✔
2453
    shape->values[i] = out;
670✔
2454
  }
2455
  iconv_close(cd);
416✔
2456

2457
  return MS_SUCCESS;
2458
#else
2459
  if (!layer->encoding || !*layer->encoding ||
2460
      !strcasecmp(layer->encoding, "UTF-8"))
2461
    return MS_SUCCESS;
2462
  msSetError(MS_MISCERR, "Not implemented since Iconv is not enabled.",
2463
             "msGetEncodedString()");
2464
  return MS_FAILURE;
2465
#endif
2466
}
2467

2468
/************************************************************************/
2469
/*                             msStringBuffer                           */
2470
/************************************************************************/
2471

2472
struct msStringBuffer {
2473
  size_t alloc_size;
2474
  size_t length;
2475
  char *str;
2476
};
2477

2478
/************************************************************************/
2479
/*                         msStringBufferAlloc()                        */
2480
/************************************************************************/
2481

2482
msStringBuffer *msStringBufferAlloc(void) {
2,750✔
2483
  return (msStringBuffer *)msSmallCalloc(sizeof(msStringBuffer), 1);
2,750✔
2484
}
2485

2486
/************************************************************************/
2487
/*                         msStringBufferFree()                         */
2488
/************************************************************************/
2489

2490
void msStringBufferFree(msStringBuffer *sb) {
2,750✔
2491
  if (sb)
2,750✔
2492
    msFree(sb->str);
2,750✔
2493
  msFree(sb);
2,750✔
2494
}
2,750✔
2495

2496
/************************************************************************/
2497
/*                       msStringBufferGetString()                      */
2498
/************************************************************************/
2499

2500
const char *msStringBufferGetString(msStringBuffer *sb) { return sb->str; }
55✔
2501

2502
/************************************************************************/
2503
/*                   msStringBufferReleaseStringAndFree()               */
2504
/************************************************************************/
2505

2506
char *msStringBufferReleaseStringAndFree(msStringBuffer *sb) {
2,695✔
2507
  char *str = sb->str;
2,695✔
2508
  sb->str = NULL;
2,695✔
2509
  sb->alloc_size = 0;
2,695✔
2510
  sb->length = 0;
2,695✔
2511
  msStringBufferFree(sb);
2,695✔
2512
  return str;
2,695✔
2513
}
2514

2515
/************************************************************************/
2516
/*                        msStringBufferAppend()                        */
2517
/************************************************************************/
2518

2519
int msStringBufferAppend(msStringBuffer *sb, const char *pszAppendedString) {
7,533✔
2520
  size_t nAppendLen = strlen(pszAppendedString);
7,533✔
2521
  if (sb->length + nAppendLen >= sb->alloc_size) {
7,533✔
2522
    size_t newAllocSize1 = sb->alloc_size + sb->alloc_size / 3;
6,288✔
2523
    size_t newAllocSize2 = sb->length + nAppendLen + 1;
6,288✔
2524
    size_t newAllocSize = std::max(newAllocSize1, newAllocSize2);
6,288✔
2525
    void *newStr = realloc(sb->str, newAllocSize);
6,288✔
2526
    if (newStr == NULL) {
6,288✔
UNCOV
2527
      msSetError(MS_MEMERR, "Not enough memory", "msStringBufferAppend()");
×
UNCOV
2528
      return MS_FAILURE;
×
2529
    }
2530
    sb->alloc_size = newAllocSize;
6,288✔
2531
    sb->str = (char *)newStr;
6,288✔
2532
  }
2533
  memcpy(sb->str + sb->length, pszAppendedString, nAppendLen + 1);
7,533✔
2534
  sb->length += nAppendLen;
7,533✔
2535
  return MS_SUCCESS;
7,533✔
2536
}
2537

2538
/************************************************************************/
2539
/*                           msStringUnescape()                         */
2540
/************************************************************************/
2541

2542
/** Modify in place pszString such that a sequence of two consecutive
2543
 * chEscapeChar is replaced by a single one.
2544
 * Does the reverse of FLTEscapePropertyName()
2545
 */
2546
void msStringUnescape(char *pszString, char chEscapeChar) {
1,803✔
2547
  char *pszDest = pszString;
2548
  for (; *pszString; ++pszString, ++pszDest) {
11,463✔
2549
    if (pszString[0] == chEscapeChar && pszString[1] == chEscapeChar) {
9,660✔
2550
      *pszDest = chEscapeChar;
4✔
2551
      ++pszString;
4✔
2552
    } else {
2553
      *pszDest = *pszString;
9,656✔
2554
    }
2555
  }
2556
  *pszDest = 0;
1,803✔
2557
}
1,803✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc