Coverage Report

Created: 2026-08-14 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgis/liblwgeom/lwutil.c
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * PostGIS - Spatial Types for PostgreSQL
4
 * http://postgis.net
5
 *
6
 * PostGIS is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 2 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * PostGIS is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with PostGIS.  If not, see <http://www.gnu.org/licenses/>.
18
 *
19
 **********************************************************************
20
 *
21
 * Copyright (C) 2004-2015 Sandro Santilli <strk@kbt.io>
22
 * Copyright (C) 2006 Mark Leslie <mark.leslie@lisasoft.com>
23
 * Copyright (C) 2008-2009 Mark Cave-Ayland <mark.cave-ayland@siriusit.co.uk>
24
 * Copyright (C) 2009-2015 Paul Ramsey <pramsey@cleverelephant.ca>
25
 * Copyright (C) 2010 Olivier Courtin <olivier.courtin@camptocamp.com>
26
 *
27
 **********************************************************************/
28
29
#include <stdio.h>
30
#include <stdlib.h>
31
#include <stdarg.h>
32
#include <string.h>
33
#include <ctype.h> /* for tolower */
34
35
/* Global variables */
36
#include "../postgis_config.h"
37
#include "liblwgeom_internal.h"
38
#include "stringbuffer.h"
39
#include "lwgeom_log.h"
40
41
/* Default allocators */
42
static void * default_allocator(size_t size);
43
static void default_freeor(void *mem);
44
static void * default_reallocator(void *mem, size_t size);
45
lwallocator lwalloc_var = default_allocator;
46
lwreallocator lwrealloc_var = default_reallocator;
47
lwfreeor lwfree_var = default_freeor;
48
49
/* Default reporters */
50
static void default_noticereporter(const char *fmt, va_list ap) __attribute__ ((format (printf, 1, 0)));
51
static void default_errorreporter(const char *fmt, va_list ap) __attribute__ ((format (printf, 1, 0)));
52
lwreporter lwnotice_var = default_noticereporter;
53
lwreporter lwerror_var = default_errorreporter;
54
55
/* Default logger */
56
static void default_debuglogger(int level, const char *fmt, va_list ap) __attribute__ ((format (printf, 2, 0)));
57
lwdebuglogger lwdebug_var = default_debuglogger;
58
59
0
#define LW_MSG_MAXLEN 256
60
61
static char *lwgeomTypeName[] =
62
{
63
  "Unknown",
64
  "Point",
65
  "LineString",
66
  "Polygon",
67
  "MultiPoint",
68
  "MultiLineString",
69
  "MultiPolygon",
70
  "GeometryCollection",
71
  "CircularString",
72
  "CompoundCurve",
73
  "CurvePolygon",
74
  "MultiCurve",
75
  "MultiSurface",
76
  "PolyhedralSurface",
77
  "Triangle",
78
  "Tin",
79
  "NurbsCurve"
80
};
81
82
/*
83
 * Default allocators
84
 *
85
 * We include some default allocators that use malloc/free/realloc
86
 * along with stdout/stderr since this is the most common use case
87
 *
88
 */
89
90
static void *
91
default_allocator(size_t size)
92
0
{
93
0
  void *mem = malloc(size);
94
0
  return mem;
95
0
}
96
97
static void
98
default_freeor(void *mem)
99
0
{
100
0
  free(mem);
101
0
}
102
103
static void *
104
default_reallocator(void *mem, size_t size)
105
0
{
106
0
  void *ret = realloc(mem, size);
107
0
  return ret;
108
0
}
109
110
/*
111
 * Default lwnotice/lwerror handlers
112
 *
113
 * Since variadic functions cannot pass their parameters directly, we need
114
 * wrappers for these functions to convert the arguments into a va_list
115
 * structure.
116
 */
117
118
static void
119
default_noticereporter(const char *fmt, va_list ap)
120
0
{
121
0
  char msg[LW_MSG_MAXLEN+1];
122
0
  vsnprintf (msg, LW_MSG_MAXLEN, fmt, ap);
123
0
  msg[LW_MSG_MAXLEN]='\0';
124
0
  fprintf(stderr, "%s\n", msg);
125
0
}
126
127
static void
128
default_debuglogger(int level, const char *fmt, va_list ap)
129
0
{
130
0
  char msg[LW_MSG_MAXLEN+1];
131
0
  if ( POSTGIS_DEBUG_LEVEL >= level )
132
0
  {
133
    /* Space pad the debug output */
134
0
    int i;
135
0
    for ( i = 0; i < level; i++ )
136
0
      msg[i] = ' ';
137
0
    vsnprintf(msg+i, LW_MSG_MAXLEN-i, fmt, ap);
138
0
    msg[LW_MSG_MAXLEN]='\0';
139
0
    fprintf(stderr, "%s\n", msg);
140
0
  }
141
0
}
142
143
static void
144
default_errorreporter(const char *fmt, va_list ap)
145
0
{
146
0
  char msg[LW_MSG_MAXLEN+1];
147
0
  vsnprintf (msg, LW_MSG_MAXLEN, fmt, ap);
148
0
  msg[LW_MSG_MAXLEN]='\0';
149
0
  fprintf(stderr, "%s\n", msg);
150
0
  exit(1);
151
0
}
152
153
/**
154
 * This function is called by programs which want to set up custom handling
155
 * for memory management and error reporting
156
 *
157
 * Only non-NULL values change their respective handler
158
 */
159
void
160
lwgeom_set_handlers(lwallocator allocator, lwreallocator reallocator,
161
          lwfreeor freeor, lwreporter errorreporter,
162
2
          lwreporter noticereporter) {
163
164
2
  if ( allocator ) lwalloc_var = allocator;
165
2
  if ( reallocator ) lwrealloc_var = reallocator;
166
2
  if ( freeor ) lwfree_var = freeor;
167
168
2
  if ( errorreporter ) lwerror_var = errorreporter;
169
2
  if ( noticereporter ) lwnotice_var = noticereporter;
170
2
}
171
172
void
173
2
lwgeom_set_debuglogger(lwdebuglogger debuglogger) {
174
175
2
  if ( debuglogger ) lwdebug_var = debuglogger;
176
2
}
177
178
void
179
lwnotice(const char *fmt, ...)
180
0
{
181
0
  va_list ap;
182
183
0
  va_start(ap, fmt);
184
185
  /* Call the supplied function */
186
0
  (*lwnotice_var)(fmt, ap);
187
188
0
  va_end(ap);
189
0
}
190
191
void
192
lwerror(const char *fmt, ...)
193
2.09k
{
194
2.09k
  va_list ap;
195
196
2.09k
  va_start(ap, fmt);
197
198
  /* Call the supplied function */
199
2.09k
  (*lwerror_var)(fmt, ap);
200
201
2.09k
  va_end(ap);
202
2.09k
}
203
204
void
205
lwdebug(int level, const char *fmt, ...)
206
0
{
207
0
  va_list ap;
208
209
0
  va_start(ap, fmt);
210
211
  /* Call the supplied function */
212
0
  (*lwdebug_var)(level, fmt, ap);
213
214
0
  va_end(ap);
215
0
}
216
217
const char*
218
lwtype_name(uint8_t type)
219
0
{
220
0
  if ( type > NUMTYPES-1 )
221
0
  {
222
    /* assert(0); */
223
0
    return "Invalid type";
224
0
  }
225
0
  return lwgeomTypeName[(int ) type];
226
0
}
227
228
void *
229
lwalloc(size_t size)
230
1.28M
{
231
1.28M
  void *mem = lwalloc_var(size);
232
1.28M
  return mem;
233
1.28M
}
234
235
void *
236
lwalloc0(size_t size)
237
0
{
238
0
  void *mem = lwalloc_var(size);
239
0
  memset(mem, 0, size);
240
0
  return mem;
241
0
}
242
243
void *
244
lwrealloc(void *mem, size_t size)
245
8.24k
{
246
8.24k
  return lwrealloc_var(mem, size);
247
8.24k
}
248
249
void
250
lwfree(void *mem)
251
646k
{
252
646k
  lwfree_var(mem);
253
646k
}
254
255
char *
256
lwstrdup(const char* a)
257
0
{
258
0
  size_t l = strlen(a)+1;
259
0
  char *b = lwalloc(l);
260
0
  strncpy(b, a, l);
261
0
  return b;
262
0
}
263
264
/*
265
 * Returns the characters to the left of the error position
266
 * to a maximum of maxlength.
267
 */
268
char *
269
lwmessage_truncate(const char *str, int errpos, int maxlength)
270
0
{
271
0
  int str_sz;
272
0
  int startpos;
273
274
0
  assert(str != NULL);
275
0
  assert(errpos >= 0);
276
0
  assert(maxlength > 0);
277
278
0
  str_sz = strlen(str);
279
280
0
  stringbuffer_t sb;
281
0
  stringbuffer_init(&sb);
282
283
  /* Error cannot be past end of WKT */
284
0
  if (errpos > str_sz)
285
0
    errpos = str_sz;
286
287
  /* Go back maxlength from errpos but not past zero */
288
0
  startpos = errpos - maxlength;
289
0
  if (startpos < 0)
290
0
    startpos = 0;
291
292
0
  if (str_sz < maxlength)
293
0
  {
294
0
    stringbuffer_append(&sb, str);
295
0
  }
296
0
  else
297
0
  {
298
    /* Add "..." prefix */
299
0
    stringbuffer_append(&sb, "...");
300
0
    stringbuffer_append_len(&sb, str + startpos, errpos-startpos);
301
0
  }
302
303
0
  return stringbuffer_getstringcopy(&sb);
304
0
}
305
306
307
int32_t
308
clamp_srid(int32_t srid)
309
0
{
310
0
  int newsrid = srid;
311
312
0
  if ( newsrid <= 0 ) {
313
0
    if ( newsrid != SRID_UNKNOWN ) {
314
0
      newsrid = SRID_UNKNOWN;
315
0
      lwnotice("SRID value %d converted to the officially unknown SRID value %d", srid, newsrid);
316
0
    }
317
0
  } else if ( srid > SRID_MAXIMUM ) {
318
0
    newsrid = SRID_USER_MAXIMUM + 1 +
319
      /* -1 is to reduce likelihood of clashes */
320
      /* NOTE: must match implementation in postgis_restore.pl */
321
0
      ( srid % ( SRID_MAXIMUM - SRID_USER_MAXIMUM - 1 ) );
322
0
    lwnotice("SRID value %d > SRID_MAXIMUM converted to %d", srid, newsrid);
323
0
  }
324
325
0
  return newsrid;
326
0
}
327
328
329
330
331
/* Structure for the type array */
332
struct geomtype_struct
333
{
334
  char *typename;
335
  int type;
336
  int z;
337
  int m;
338
};
339
340
/* Type array. Note that the order of this array is important in
341
   that any typename in the list must *NOT* occur within an entry
342
   before it. Otherwise if we search for "POINT" at the top of the
343
   list we would also match MULTIPOINT, for example. */
344
345
struct geomtype_struct geomtype_struct_array[] =
346
{
347
  { "GEOMETRYCOLLECTIONZM", COLLECTIONTYPE, 1, 1 },
348
  { "GEOMETRYCOLLECTIONZ", COLLECTIONTYPE, 1, 0 },
349
  { "GEOMETRYCOLLECTIONM", COLLECTIONTYPE, 0, 1 },
350
  { "GEOMETRYCOLLECTION", COLLECTIONTYPE, 0, 0 },
351
352
  { "GEOMETRYZM", 0, 1, 1 },
353
  { "GEOMETRYZ", 0, 1, 0 },
354
  { "GEOMETRYM", 0, 0, 1 },
355
  { "GEOMETRY", 0, 0, 0 },
356
357
  { "POLYHEDRALSURFACEZM", POLYHEDRALSURFACETYPE, 1, 1 },
358
  { "POLYHEDRALSURFACEZ", POLYHEDRALSURFACETYPE, 1, 0 },
359
  { "POLYHEDRALSURFACEM", POLYHEDRALSURFACETYPE, 0, 1 },
360
  { "POLYHEDRALSURFACE", POLYHEDRALSURFACETYPE, 0, 0 },
361
362
  { "TINZM", TINTYPE, 1, 1 },
363
  { "TINZ", TINTYPE, 1, 0 },
364
  { "TINM", TINTYPE, 0, 1 },
365
  { "TIN", TINTYPE, 0, 0 },
366
367
  { "CIRCULARSTRINGZM", CIRCSTRINGTYPE, 1, 1 },
368
  { "CIRCULARSTRINGZ", CIRCSTRINGTYPE, 1, 0 },
369
  { "CIRCULARSTRINGM", CIRCSTRINGTYPE, 0, 1 },
370
  { "CIRCULARSTRING", CIRCSTRINGTYPE, 0, 0 },
371
372
  { "COMPOUNDCURVEZM", COMPOUNDTYPE, 1, 1 },
373
  { "COMPOUNDCURVEZ", COMPOUNDTYPE, 1, 0 },
374
  { "COMPOUNDCURVEM", COMPOUNDTYPE, 0, 1 },
375
  { "COMPOUNDCURVE", COMPOUNDTYPE, 0, 0 },
376
377
  { "CURVEPOLYGONZM", CURVEPOLYTYPE, 1, 1 },
378
  { "CURVEPOLYGONZ", CURVEPOLYTYPE, 1, 0 },
379
  { "CURVEPOLYGONM", CURVEPOLYTYPE, 0, 1 },
380
  { "CURVEPOLYGON", CURVEPOLYTYPE, 0, 0 },
381
382
  { "MULTICURVEZM", MULTICURVETYPE, 1, 1 },
383
  { "MULTICURVEZ", MULTICURVETYPE, 1, 0 },
384
  { "MULTICURVEM", MULTICURVETYPE, 0, 1 },
385
  { "MULTICURVE", MULTICURVETYPE, 0, 0 },
386
387
  { "MULTISURFACEZM", MULTISURFACETYPE, 1, 1 },
388
  { "MULTISURFACEZ", MULTISURFACETYPE, 1, 0 },
389
  { "MULTISURFACEM", MULTISURFACETYPE, 0, 1 },
390
  { "MULTISURFACE", MULTISURFACETYPE, 0, 0 },
391
392
  { "MULTILINESTRINGZM", MULTILINETYPE, 1, 1 },
393
  { "MULTILINESTRINGZ", MULTILINETYPE, 1, 0 },
394
  { "MULTILINESTRINGM", MULTILINETYPE, 0, 1 },
395
  { "MULTILINESTRING", MULTILINETYPE, 0, 0 },
396
397
  { "MULTIPOLYGONZM", MULTIPOLYGONTYPE, 1, 1 },
398
  { "MULTIPOLYGONZ", MULTIPOLYGONTYPE, 1, 0 },
399
  { "MULTIPOLYGONM", MULTIPOLYGONTYPE, 0, 1 },
400
  { "MULTIPOLYGON", MULTIPOLYGONTYPE, 0, 0 },
401
402
  { "MULTIPOINTZM", MULTIPOINTTYPE, 1, 1 },
403
  { "MULTIPOINTZ", MULTIPOINTTYPE, 1, 0 },
404
  { "MULTIPOINTM", MULTIPOINTTYPE, 0, 1 },
405
  { "MULTIPOINT", MULTIPOINTTYPE, 0, 0 },
406
407
  { "LINESTRINGZM", LINETYPE, 1, 1 },
408
  { "LINESTRINGZ", LINETYPE, 1, 0 },
409
  { "LINESTRINGM", LINETYPE, 0, 1 },
410
  { "LINESTRING", LINETYPE, 0, 0 },
411
412
  { "TRIANGLEZM", TRIANGLETYPE, 1, 1 },
413
  { "TRIANGLEZ", TRIANGLETYPE, 1, 0 },
414
  { "TRIANGLEM", TRIANGLETYPE, 0, 1 },
415
  { "TRIANGLE", TRIANGLETYPE, 0, 0 },
416
417
  { "POLYGONZM", POLYGONTYPE, 1, 1 },
418
  { "POLYGONZ", POLYGONTYPE, 1, 0 },
419
  { "POLYGONM", POLYGONTYPE, 0, 1 },
420
  { "POLYGON", POLYGONTYPE, 0, 0 },
421
422
  { "POINTZM", POINTTYPE, 1, 1 },
423
  { "POINTZ", POINTTYPE, 1, 0 },
424
  { "POINTM", POINTTYPE, 0, 1 },
425
  { "POINT", POINTTYPE, 0, 0 },
426
427
  { "NURBSCURVEZM", NURBSCURVETYPE, 1, 1 },
428
  { "NURBSCURVEZ", NURBSCURVETYPE, 1, 0 },
429
  { "NURBSCURVEM", NURBSCURVETYPE, 0, 1 },
430
  { "NURBSCURVE", NURBSCURVETYPE, 0, 0 }
431
432
};
433
0
#define GEOMTYPE_STRUCT_ARRAY_LEN (sizeof geomtype_struct_array/sizeof(struct geomtype_struct))
434
435
/*
436
* We use a very simple upper case mapper here, because the system toupper() function
437
* is locale dependent and may have trouble mapping lower case strings to the upper
438
* case ones we expect (see, the "Turkisk I", http://www.i18nguy.com/unicode/turkish-i18n.html)
439
* We could also count on PgSQL sending us *lower* case inputs, as it seems to do that
440
* regardless of the case the user provides for the type arguments.
441
*/
442
const char dumb_upper_map[128] = "................................................0123456789.......ABCDEFGHIJKLMNOPQRSTUVWXYZ......ABCDEFGHIJKLMNOPQRSTUVWXYZ.....";
443
444
static char dumb_toupper(int in)
445
0
{
446
0
  if ( in < 0 || in > 127 )
447
0
    return '.';
448
0
  return dumb_upper_map[in];
449
0
}
450
451
lwflags_t lwflags(int hasz, int hasm, int geodetic)
452
652k
{
453
652k
  lwflags_t flags = 0;
454
652k
  if (hasz)
455
22.8k
    FLAGS_SET_Z(flags, 1);
456
652k
  if (hasm)
457
7.25k
    FLAGS_SET_M(flags, 1);
458
652k
  if (geodetic)
459
0
    FLAGS_SET_GEODETIC(flags, 1);
460
652k
  return flags;
461
652k
}
462
463
/**
464
* Calculate type integer and dimensional flags from string input.
465
* Case insensitive, and insensitive to spaces at front and back.
466
* Type == 0 in the case of the string "GEOMETRY" or "GEOGRAPHY".
467
* Return LW_SUCCESS for success.
468
*/
469
int geometry_type_from_string(const char *str, uint8_t *type, int *z, int *m)
470
0
{
471
0
  char *tmpstr;
472
0
  size_t tmpstartpos, tmpendpos;
473
0
  size_t i;
474
475
0
  assert(str);
476
0
  assert(type);
477
0
  assert(z);
478
0
  assert(m);
479
480
  /* Initialize. */
481
0
  *type = 0;
482
0
  *z = 0;
483
0
  *m = 0;
484
485
  /* Locate any leading/trailing spaces */
486
0
  tmpstartpos = 0;
487
0
  for (i = 0; i < strlen(str); i++)
488
0
  {
489
0
    if (str[i] != ' ')
490
0
    {
491
0
      tmpstartpos = i;
492
0
      break;
493
0
    }
494
0
  }
495
496
0
  tmpendpos = strlen(str) - 1;
497
0
  for (i = strlen(str) - 1; i != 0; i--)
498
0
  {
499
0
    if (str[i] != ' ')
500
0
    {
501
0
      tmpendpos = i;
502
0
      break;
503
0
    }
504
0
  }
505
506
  /* Copy and convert to upper case for comparison */
507
0
  tmpstr = lwalloc(tmpendpos - tmpstartpos + 2);
508
0
  for (i = tmpstartpos; i <= tmpendpos; i++)
509
0
    tmpstr[i - tmpstartpos] = dumb_toupper(str[i]);
510
511
  /* Add NULL to terminate */
512
0
  tmpstr[i - tmpstartpos] = '\0';
513
514
  /* Now check for the type */
515
0
  for (i = 0; i < GEOMTYPE_STRUCT_ARRAY_LEN; i++)
516
0
  {
517
0
    if (!strcmp(tmpstr, geomtype_struct_array[i].typename))
518
0
    {
519
0
      *type = geomtype_struct_array[i].type;
520
0
      *z = geomtype_struct_array[i].z;
521
0
      *m = geomtype_struct_array[i].m;
522
523
0
      lwfree(tmpstr);
524
525
0
      return LW_SUCCESS;
526
0
    }
527
528
0
  }
529
530
0
  lwfree(tmpstr);
531
532
0
  return LW_FAILURE;
533
0
}
534
535
536
537
538
539