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

MapServer / MapServer / 24030044940

06 Apr 2026 11:26AM UTC coverage: 42.336% (+0.001%) from 42.335%
24030044940

push

github

web-flow
Fix segmentation fault in PHP MapScriptNG (fixes #6787) (#7471)

2 of 6 new or added lines in 1 file covered. (33.33%)

6 existing lines in 1 file now uncovered.

64439 of 152207 relevant lines covered (42.34%)

27064.26 hits per line

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

73.27
/src/maputil.c
1
/******************************************************************************
2
 * $Id$
3
 *
4
 * Project:  MapServer
5
 * Purpose:  Various utility functions ... a real hodgepodge.
6
 * Author:   Steve Lime and the MapServer team.
7
 *
8
 * Notes: Some code (notably msAlphaBlend()) are directly derived from GD. See
9
 * the mapserver/GD-COPYING file for the GD license.  Use of this code in this
10
 * manner is compatible with the MapServer license.
11
 *
12
 ******************************************************************************
13
 * Copyright (c) 1996-2005 Regents of the University of Minnesota.
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 <math.h>
35
#include <time.h>
36

37
#include "mapserver.h"
38
#include "maptime.h"
39
#include "mapthread.h"
40
#include "mapcopy.h"
41
#include "mapows.h"
42

43
#include "gdal.h"
44
#include "cpl_conv.h"
45

46
#if defined(_WIN32) && !defined(__CYGWIN__)
47
#include <windows.h>
48
#include <tchar.h>
49
#include <fcntl.h>
50
#include <io.h>
51
#include <process.h>
52
#endif
53

54
#ifdef USE_RSVG
55
#include <glib-object.h>
56
#endif
57
#ifdef USE_GEOS
58
#include <geos_c.h>
59
#endif
60

61
extern char *msyystring_buffer;
62
extern int msyylex_destroy(void);
63
extern int yyparse(parseObj *);
64

65
int gdal_destroyed = 0;
66

67
int msScaleInBounds(double scale, double minscale, double maxscale) {
114,555✔
68
  if (scale > 0) {
114,555✔
69
    if (maxscale != -1 && scale >= maxscale)
113,278✔
70
      return MS_FALSE;
71
    if (minscale != -1 && scale < minscale)
112,627✔
72
      return MS_FALSE;
2✔
73
  }
74
  return MS_TRUE;
75
}
76

77
/*
78
** Helper functions to convert from strings to other types or objects.
79
*/
80
static int bindIntegerAttribute(int *attribute, const char *value) {
44✔
81
  if (!value || strlen(value) == 0)
44✔
82
    return MS_FAILURE;
83
  *attribute =
44✔
84
      MS_NINT(atof(value)); /*use atof instead of atoi as a fix for bug 2394*/
44✔
85
  return MS_SUCCESS;
44✔
86
}
87

88
static int bindDoubleAttribute(double *attribute, const char *value) {
89
  if (!value || strlen(value) == 0)
23,589✔
90
    return MS_FAILURE;
91
  *attribute = atof(value);
23,589✔
92
  return MS_SUCCESS;
23,589✔
93
}
94

95
static int bindColorAttribute(colorObj *attribute, const char *value) {
30✔
96
  int len;
97

98
  if (!value || ((len = strlen(value)) == 0))
30✔
99
    return MS_FAILURE;
100

101
  if (value[0] == '#' && (len == 7 || len == 9)) { /* got a hex color */
30✔
102
    char hex[2];
103

104
    hex[0] = value[1];
22✔
105
    hex[1] = value[2];
22✔
106
    attribute->red = msHexToInt(hex);
22✔
107
    hex[0] = value[3];
22✔
108
    hex[1] = value[4];
22✔
109
    attribute->green = msHexToInt(hex);
22✔
110
    hex[0] = value[5];
22✔
111
    hex[1] = value[6];
22✔
112
    attribute->blue = msHexToInt(hex);
22✔
113
    if (len == 9) {
22✔
114
      hex[0] = value[7];
3✔
115
      hex[1] = value[8];
3✔
116
      attribute->alpha = msHexToInt(hex);
3✔
117
    }
118
    return MS_SUCCESS;
119
  } else { /* try a space delimited string */
120
    char **tokens = NULL;
121
    int numtokens = 0;
8✔
122

123
    tokens = msStringSplit(value, ' ', &numtokens);
8✔
124
    if (tokens == NULL || numtokens != 3) {
8✔
125
      msFreeCharArray(tokens, numtokens);
×
126
      return MS_FAILURE; /* punt */
×
127
    }
128

129
    attribute->red = atoi(tokens[0]);
8✔
130
    attribute->green = atoi(tokens[1]);
8✔
131
    attribute->blue = atoi(tokens[2]);
8✔
132
    msFreeCharArray(tokens, numtokens);
8✔
133

134
    return MS_SUCCESS;
8✔
135
  }
136

137
  return MS_FAILURE; /* shouldn't get here */
138
}
139

140
static void bindStyle(layerObj *layer, shapeObj *shape, styleObj *style,
65,965✔
141
                      int drawmode) {
142
  int applyOpacity = MS_FALSE;
143
  assert(MS_DRAW_FEATURES(drawmode));
144
  if (style->numbindings > 0) {
65,965✔
145
    applyOpacity = MS_TRUE;
146
    if (style->bindings[MS_STYLE_BINDING_SYMBOL].index != -1) {
20,186✔
147
      style->symbol = msGetSymbolIndex(
918✔
148
          &(layer->map->symbolset),
459✔
149
          shape->values[style->bindings[MS_STYLE_BINDING_SYMBOL].index],
459✔
150
          MS_TRUE);
151
      if (style->symbol == -1)
459✔
152
        style->symbol =
×
153
            0; /* a reasonable default (perhaps should throw an error?) */
154
    }
155
    if (style->bindings[MS_STYLE_BINDING_ANGLE].index != -1) {
20,186✔
156
      style->angle = 360.0;
19,917✔
157
      bindDoubleAttribute(
19,917✔
158
          &style->angle,
159
          shape->values[style->bindings[MS_STYLE_BINDING_ANGLE].index]);
19,917✔
160
    }
161
    if (style->bindings[MS_STYLE_BINDING_SIZE].index != -1) {
20,186✔
162
      style->size = 1;
1,356✔
163
      bindDoubleAttribute(
1,356✔
164
          &style->size,
165
          shape->values[style->bindings[MS_STYLE_BINDING_SIZE].index]);
1,356✔
166
    }
167
    if (style->bindings[MS_STYLE_BINDING_WIDTH].index != -1) {
20,186✔
168
      style->width = 1;
12✔
169
      bindDoubleAttribute(
12✔
170
          &style->width,
171
          shape->values[style->bindings[MS_STYLE_BINDING_WIDTH].index]);
12✔
172
    }
173
    if (style->bindings[MS_STYLE_BINDING_COLOR].index != -1 &&
20,186✔
174
        !MS_DRAW_QUERY(drawmode)) {
19✔
175
      MS_INIT_COLOR(style->color, -1, -1, -1, 255);
19✔
176
      bindColorAttribute(
19✔
177
          &style->color,
178
          shape->values[style->bindings[MS_STYLE_BINDING_COLOR].index]);
19✔
179
    }
180
    if (style->bindings[MS_STYLE_BINDING_OUTLINECOLOR].index != -1 &&
20,186✔
181
        !MS_DRAW_QUERY(drawmode)) {
×
182
      MS_INIT_COLOR(style->outlinecolor, -1, -1, -1, 255);
×
183
      bindColorAttribute(
×
184
          &style->outlinecolor,
185
          shape->values[style->bindings[MS_STYLE_BINDING_OUTLINECOLOR].index]);
×
186
    }
187
    if (style->bindings[MS_STYLE_BINDING_OUTLINEWIDTH].index != -1) {
20,186✔
188
      style->outlinewidth = 1;
12✔
189
      bindDoubleAttribute(
12✔
190
          &style->outlinewidth,
191
          shape->values[style->bindings[MS_STYLE_BINDING_OUTLINEWIDTH].index]);
12✔
192
    }
193
    if (style->bindings[MS_STYLE_BINDING_OPACITY].index != -1) {
20,186✔
194
      style->opacity = 100;
×
195
      bindIntegerAttribute(
×
196
          &style->opacity,
197
          shape->values[style->bindings[MS_STYLE_BINDING_OPACITY].index]);
×
198
    }
199
    if (style->bindings[MS_STYLE_BINDING_OFFSET_X].index != -1) {
20,186✔
200
      style->offsetx = 0;
×
201
      bindDoubleAttribute(
×
202
          &style->offsetx,
203
          shape->values[style->bindings[MS_STYLE_BINDING_OFFSET_X].index]);
×
204
    }
205
    if (style->bindings[MS_STYLE_BINDING_OFFSET_Y].index != -1) {
20,186✔
206
      style->offsety = 0;
×
207
      bindDoubleAttribute(
×
208
          &style->offsety,
209
          shape->values[style->bindings[MS_STYLE_BINDING_OFFSET_Y].index]);
×
210
    }
211
    if (style->bindings[MS_STYLE_BINDING_POLAROFFSET_PIXEL].index != -1) {
20,186✔
212
      style->polaroffsetpixel = 0;
1,146✔
213
      bindDoubleAttribute(
1,146✔
214
          &style->polaroffsetpixel,
215
          shape->values[style->bindings[MS_STYLE_BINDING_POLAROFFSET_PIXEL]
1,146✔
216
                            .index]);
1,146✔
217
    }
218
    if (style->bindings[MS_STYLE_BINDING_POLAROFFSET_ANGLE].index != -1) {
20,186✔
219
      style->polaroffsetangle = 0;
1,146✔
220
      bindDoubleAttribute(
1,146✔
221
          &style->polaroffsetangle,
222
          shape->values[style->bindings[MS_STYLE_BINDING_POLAROFFSET_ANGLE]
1,146✔
223
                            .index]);
1,146✔
224
    }
225
  }
226
  if (style->nexprbindings > 0) {
65,965✔
227
    applyOpacity = MS_TRUE;
228
    if (style->exprBindings[MS_STYLE_BINDING_OFFSET_X].type == MS_EXPRESSION) {
38✔
229
      style->offsetx = msEvalDoubleExpression(
2✔
230
          &(style->exprBindings[MS_STYLE_BINDING_OFFSET_X]), shape);
231
    }
232
    if (style->exprBindings[MS_STYLE_BINDING_OFFSET_Y].type == MS_EXPRESSION) {
38✔
233
      style->offsety = msEvalDoubleExpression(
2✔
234
          &(style->exprBindings[MS_STYLE_BINDING_OFFSET_Y]), shape);
235
    }
236
    if (style->exprBindings[MS_STYLE_BINDING_ANGLE].type == MS_EXPRESSION) {
38✔
237
      style->angle = msEvalDoubleExpression(
2✔
238
          &(style->exprBindings[MS_STYLE_BINDING_ANGLE]), shape);
239
    }
240
    if (style->exprBindings[MS_STYLE_BINDING_SIZE].type == MS_EXPRESSION) {
38✔
241
      style->size = msEvalDoubleExpression(
12✔
242
          &(style->exprBindings[MS_STYLE_BINDING_SIZE]), shape);
243
    }
244
    if (style->exprBindings[MS_STYLE_BINDING_WIDTH].type == MS_EXPRESSION) {
38✔
245
      style->width = msEvalDoubleExpression(
7✔
246
          &(style->exprBindings[MS_STYLE_BINDING_WIDTH]), shape);
247
    }
248
    if (style->exprBindings[MS_STYLE_BINDING_OPACITY].type == MS_EXPRESSION) {
38✔
249
      style->opacity =
8✔
250
          100 * msEvalDoubleExpression(
8✔
251
                    &(style->exprBindings[MS_STYLE_BINDING_OPACITY]), shape);
252
    }
253
    if (style->exprBindings[MS_STYLE_BINDING_OUTLINECOLOR].type ==
38✔
254
        MS_EXPRESSION) {
255
      char *txt = msEvalTextExpression(
2✔
256
          &(style->exprBindings[MS_STYLE_BINDING_OUTLINECOLOR]), shape);
257
      bindColorAttribute(&style->outlinecolor, txt);
2✔
258
      msFree(txt);
2✔
259
    }
260
    if (style->exprBindings[MS_STYLE_BINDING_COLOR].type == MS_EXPRESSION) {
38✔
261
      char *txt = msEvalTextExpression(
3✔
262
          &(style->exprBindings[MS_STYLE_BINDING_COLOR]), shape);
263
      bindColorAttribute(&style->color, txt);
3✔
264
      msFree(txt);
3✔
265
    }
266
  }
267

268
  if (applyOpacity == MS_TRUE && style->opacity < 100) {
65,965✔
269
    int alpha;
270
    alpha = MS_NINT(style->opacity * 2.55);
30✔
271
    style->color.alpha = alpha;
30✔
272
    style->outlinecolor.alpha = alpha;
30✔
273
    style->mincolor.alpha = alpha;
30✔
274
    style->maxcolor.alpha = alpha;
30✔
275
  }
276
}
65,965✔
277

278
static void bindLabel(layerObj *layer, shapeObj *shape, labelObj *label,
34,865✔
279
                      int drawmode) {
280
  int i;
281
  assert(MS_DRAW_LABELS(drawmode));
282

283
  /* check the label styleObj's (TODO: do we need to use querymapMode here? */
284
  for (i = 0; i < label->numstyles; i++) {
62,446✔
285
    /* force MS_DRAWMODE_FEATURES for label styles */
286
    bindStyle(layer, shape, label->styles[i], drawmode | MS_DRAWMODE_FEATURES);
27,581✔
287
  }
288

289
  if (label->numbindings > 0) {
34,865✔
290
    if (label->bindings[MS_LABEL_BINDING_ANGLE].index != -1) {
34✔
291
      label->angle = 0.0;
×
292
      bindDoubleAttribute(
×
293
          &label->angle,
294
          shape->values[label->bindings[MS_LABEL_BINDING_ANGLE].index]);
×
295
    }
296

297
    if (label->bindings[MS_LABEL_BINDING_SIZE].index != -1) {
34✔
298
      label->size = 1;
4✔
299
      bindIntegerAttribute(
4✔
300
          &label->size,
301
          shape->values[label->bindings[MS_LABEL_BINDING_SIZE].index]);
4✔
302
    }
303

304
    if (label->bindings[MS_LABEL_BINDING_COLOR].index != -1) {
34✔
305
      MS_INIT_COLOR(label->color, -1, -1, -1, 255);
4✔
306
      bindColorAttribute(
4✔
307
          &label->color,
308
          shape->values[label->bindings[MS_LABEL_BINDING_COLOR].index]);
4✔
309
    }
310

311
    if (label->bindings[MS_LABEL_BINDING_OUTLINECOLOR].index != -1) {
34✔
312
      MS_INIT_COLOR(label->outlinecolor, -1, -1, -1, 255);
×
313
      bindColorAttribute(
×
314
          &label->outlinecolor,
315
          shape->values[label->bindings[MS_LABEL_BINDING_OUTLINECOLOR].index]);
×
316
    }
317

318
    if (label->bindings[MS_LABEL_BINDING_FONT].index != -1) {
34✔
319
      msFree(label->font);
×
320
      label->font =
×
321
          msStrdup(shape->values[label->bindings[MS_LABEL_BINDING_FONT].index]);
×
322
    }
323

324
    if (label->bindings[MS_LABEL_BINDING_PRIORITY].index != -1) {
34✔
325
      label->priority = MS_DEFAULT_LABEL_PRIORITY;
10✔
326
      bindIntegerAttribute(
10✔
327
          &label->priority,
328
          shape->values[label->bindings[MS_LABEL_BINDING_PRIORITY].index]);
10✔
329
    }
330

331
    if (label->bindings[MS_LABEL_BINDING_SHADOWSIZEX].index != -1) {
34✔
332
      label->shadowsizex = 1;
×
333
      bindIntegerAttribute(
×
334
          &label->shadowsizex,
335
          shape->values[label->bindings[MS_LABEL_BINDING_SHADOWSIZEX].index]);
×
336
    }
337
    if (label->bindings[MS_LABEL_BINDING_SHADOWSIZEY].index != -1) {
34✔
338
      label->shadowsizey = 1;
×
339
      bindIntegerAttribute(
×
340
          &label->shadowsizey,
341
          shape->values[label->bindings[MS_LABEL_BINDING_SHADOWSIZEY].index]);
×
342
    }
343

344
    if (label->bindings[MS_LABEL_BINDING_OFFSET_X].index != -1) {
34✔
345
      label->offsetx = 0;
10✔
346
      bindIntegerAttribute(
10✔
347
          &label->offsetx,
348
          shape->values[label->bindings[MS_LABEL_BINDING_OFFSET_X].index]);
10✔
349
    }
350

351
    if (label->bindings[MS_LABEL_BINDING_OFFSET_Y].index != -1) {
34✔
352
      label->offsety = 0;
10✔
353
      bindIntegerAttribute(
10✔
354
          &label->offsety,
355
          shape->values[label->bindings[MS_LABEL_BINDING_OFFSET_Y].index]);
10✔
356
    }
357

358
    if (label->bindings[MS_LABEL_BINDING_ALIGN].index != -1) {
34✔
359
      int tmpAlign = 0;
10✔
360
      bindIntegerAttribute(
10✔
361
          &tmpAlign,
362
          shape->values[label->bindings[MS_LABEL_BINDING_ALIGN].index]);
10✔
363
      if (tmpAlign != 0) { /* is this test sufficient? */
10✔
364
        label->align = tmpAlign;
×
365
      } else { /* Integer binding failed, look for strings like cc, ul, lr,
366
                  etc... */
367
        if (strlen(
10✔
368
                shape->values[label->bindings[MS_LABEL_BINDING_ALIGN].index]) >=
10✔
369
            4) {
370
          char *va =
371
              shape->values[label->bindings[MS_LABEL_BINDING_ALIGN].index];
372
          if (!strncasecmp(va, "center", 5))
10✔
373
            label->align = MS_ALIGN_CENTER;
3✔
374
          else if (!strncasecmp(va, "left", 4))
7✔
375
            label->align = MS_ALIGN_LEFT;
4✔
376
          else if (!strncasecmp(va, "right", 5))
3✔
377
            label->align = MS_ALIGN_RIGHT;
3✔
378
        }
379
      }
380
    }
381

382
    if (label->bindings[MS_LABEL_BINDING_POSITION].index != -1) {
34✔
383
      int tmpPosition = 0;
×
384
      bindIntegerAttribute(
×
385
          &tmpPosition,
386
          shape->values[label->bindings[MS_LABEL_BINDING_POSITION].index]);
×
387
      if (tmpPosition != 0) { /* is this test sufficient? */
×
388
        label->position = tmpPosition;
×
389
      } else { /* Integer binding failed, look for strings like cc, ul, lr,
390
                  etc... */
391
        if (strlen(shape->values[label->bindings[MS_LABEL_BINDING_POSITION]
×
392
                                     .index]) == 2) {
×
393
          char *vp =
394
              shape->values[label->bindings[MS_LABEL_BINDING_POSITION].index];
395
          if (!strncasecmp(vp, "ul", 2))
×
396
            label->position = MS_UL;
×
397
          else if (!strncasecmp(vp, "lr", 2))
×
398
            label->position = MS_LR;
×
399
          else if (!strncasecmp(vp, "ur", 2))
×
400
            label->position = MS_UR;
×
401
          else if (!strncasecmp(vp, "ll", 2))
×
402
            label->position = MS_LL;
×
403
          else if (!strncasecmp(vp, "cr", 2))
×
404
            label->position = MS_CR;
×
405
          else if (!strncasecmp(vp, "cl", 2))
×
406
            label->position = MS_CL;
×
407
          else if (!strncasecmp(vp, "uc", 2))
×
408
            label->position = MS_UC;
×
409
          else if (!strncasecmp(vp, "lc", 2))
×
410
            label->position = MS_LC;
×
411
          else if (!strncasecmp(vp, "cc", 2))
×
412
            label->position = MS_CC;
×
413
        }
414
      }
415
    }
416
  }
417
  if (label->nexprbindings > 0) {
34,865✔
418
    if (label->exprBindings[MS_LABEL_BINDING_ANGLE].type == MS_EXPRESSION) {
17✔
419
      label->angle = msEvalDoubleExpression(
2✔
420
          &(label->exprBindings[MS_LABEL_BINDING_ANGLE]), shape);
421
    }
422
    if (label->exprBindings[MS_LABEL_BINDING_SIZE].type == MS_EXPRESSION) {
17✔
423
      label->size = msEvalDoubleExpression(
3✔
424
          &(label->exprBindings[MS_LABEL_BINDING_SIZE]), shape);
425
    }
426
    if (label->exprBindings[MS_LABEL_BINDING_COLOR].type == MS_EXPRESSION) {
17✔
427
      char *txt = msEvalTextExpression(
1✔
428
          &(label->exprBindings[MS_LABEL_BINDING_COLOR]), shape);
429
      bindColorAttribute(&label->color, txt);
1✔
430
      msFree(txt);
1✔
431
    }
432
    if (label->exprBindings[MS_LABEL_BINDING_OUTLINECOLOR].type ==
17✔
433
        MS_EXPRESSION) {
434
      char *txt = msEvalTextExpression(
1✔
435
          &(label->exprBindings[MS_LABEL_BINDING_OUTLINECOLOR]), shape);
436
      bindColorAttribute(&label->outlinecolor, txt);
1✔
437
      msFree(txt);
1✔
438
    }
439
    if (label->exprBindings[MS_LABEL_BINDING_PRIORITY].type == MS_EXPRESSION) {
17✔
440
      label->priority = msEvalDoubleExpression(
10✔
441
          &(label->exprBindings[MS_LABEL_BINDING_PRIORITY]), shape);
442
    }
443
  }
444
}
34,865✔
445

446
/*
447
** Function to bind various layer properties to shape attributes.
448
*/
449
int msBindLayerToShape(layerObj *layer, shapeObj *shape, int drawmode) {
30,471✔
450
  int i, j;
451

452
  if (!layer || !shape)
30,471✔
453
    return MS_FAILURE;
454

455
  for (i = 0; i < layer->numclasses; i++) {
67,246✔
456
    /* check the styleObj's */
457
    if (MS_DRAW_FEATURES(drawmode)) {
36,775✔
458
      for (j = 0; j < layer->class[i] -> numstyles; j++) {
75,149✔
459
        bindStyle(layer, shape, layer->class[i] -> styles[j], drawmode);
38,384✔
460
      }
461
    }
462

463
    /* check the labelObj's */
464
    if (MS_DRAW_LABELS(drawmode)) {
36,775✔
465
      for (j = 0; j < layer->class[i] -> numlabels; j++) {
40,510✔
466
        bindLabel(layer, shape, layer->class[i] -> labels[j], drawmode);
34,865✔
467
      }
468
    }
469
  } /* next classObj */
470

471
  return MS_SUCCESS;
472
}
473

474
/*
475
 * Used to get red, green, blue integers separately based upon the color index
476
 */
477
int getRgbColor(mapObj *map, int i, int *r, int *g, int *b) {
×
478
  /* check index range */
479
  int status = 1;
480
  *r = *g = *b = -1;
×
481
  if ((i > 0) && (i <= map->palette.numcolors)) {
×
482
    *r = map->palette.colors[i - 1].red;
×
483
    *g = map->palette.colors[i - 1].green;
×
484
    *b = map->palette.colors[i - 1].blue;
×
485
    status = 0;
486
  }
487
  return status;
×
488
}
489

490
static int searchContextForTag(mapObj *map, char **ltags, char *tag,
12,011✔
491
                               char *context, int requires) {
492
  int i;
493

494
  if (!context)
12,011✔
495
    return MS_FAILURE;
496

497
  /*  printf("\tin searchContextForTag, searching %s for %s\n", context, tag);
498
   */
499

500
  if (strstr(context, tag) != NULL)
15✔
501
    return MS_SUCCESS; /* found the tag */
502

503
  /* check referenced layers for the tag too */
504
  for (i = 0; i < map->numlayers; i++) {
75✔
505
    if (strstr(context, ltags[i]) != NULL) { /* need to check this layer */
60✔
506
      if (requires == MS_TRUE) {
5✔
507
        if (searchContextForTag(map, ltags, tag, GET_LAYER(map, i)->requires,
5✔
508
                                MS_TRUE) == MS_SUCCESS)
509
          return MS_SUCCESS;
510
      } else {
511
        if (searchContextForTag(map, ltags, tag,
×
512
                                GET_LAYER(map, i)->labelrequires,
×
513
                                MS_FALSE) == MS_SUCCESS)
514
          return MS_SUCCESS;
515
      }
516
    }
517
  }
518

519
  return MS_FAILURE;
520
}
521

522
/*
523
** Function to take a look at all layers with REQUIRES/LABELREQUIRES set to make
524
*sure there are no
525
** recursive context requirements set (e.g. layer1 requires layer2 and layer2
526
*requires layer1). This
527
** is bug 1059.
528
*/
529
int msValidateContexts(mapObj *map) {
1,300✔
530
  int i;
531
  char **ltags;
532
  int status = MS_SUCCESS;
533

534
  ltags = (char **)msSmallMalloc(map->numlayers * sizeof(char *));
1,300✔
535
  for (i = 0; i < map->numlayers; i++) {
7,303✔
536
    if (GET_LAYER(map, i)->name == NULL) {
6,003✔
537
      ltags[i] = msStrdup("[NULL]");
95✔
538
    } else {
539
      ltags[i] = (char *)msSmallMalloc(
11,816✔
540
          sizeof(char) * strlen(GET_LAYER(map, i)->name) + 3);
5,908✔
541
      sprintf(ltags[i], "[%s]", GET_LAYER(map, i)->name);
5,908✔
542
    }
543
  }
544

545
  /* check each layer's REQUIRES and LABELREQUIRES parameters */
546
  for (i = 0; i < map->numlayers; i++) {
7,303✔
547
    /* printf("working on layer %s, looking for references to %s\n",
548
     * GET_LAYER(map, i)->name, ltags[i]); */
549
    if (searchContextForTag(map, ltags, ltags[i], GET_LAYER(map, i)->requires,
6,003✔
550
                            MS_TRUE) == MS_SUCCESS) {
551
      msSetError(MS_PARSEERR,
×
552
                 "Recursion error found for REQUIRES parameter for layer %s.",
553
                 "msValidateContexts", GET_LAYER(map, i)->name);
554
      status = MS_FAILURE;
555
      break;
×
556
    }
557
    if (searchContextForTag(map, ltags, ltags[i],
6,003✔
558
                            GET_LAYER(map, i)->labelrequires,
559
                            MS_FALSE) == MS_SUCCESS) {
560
      msSetError(
×
561
          MS_PARSEERR,
562
          "Recursion error found for LABELREQUIRES parameter for layer %s.",
563
          "msValidateContexts", GET_LAYER(map, i)->name);
564
      status = MS_FAILURE;
565
      break;
×
566
    }
567
    /* printf("done layer %s\n", GET_LAYER(map, i)->name); */
568
  }
569

570
  /* clean up */
571
  msFreeCharArray(ltags, map->numlayers);
1,300✔
572

573
  return status;
1,300✔
574
}
575

576
int msEvalContext(mapObj *map, layerObj *layer, char *context) {
6,631✔
577
  int i, status;
578
  char *tag = NULL;
579
  tokenListNodeObjPtr token = NULL;
580

581
  expressionObj e;
582
  parseObj p;
583

584
  if (!context)
6,631✔
585
    return (MS_TRUE);
586

587
  /* initialize a temporary expression (e) */
588
  msInitExpression(&e);
5✔
589

590
  e.string = msStrdup(context);
5✔
591
  e.type = MS_EXPRESSION; /* todo */
5✔
592

593
  for (i = 0; i < map->numlayers; i++) { /* step through all the layers */
25✔
594
    if (layer->index == i)
20✔
595
      continue; /* skip the layer in question */
5✔
596
    if (GET_LAYER(map, i)->name == NULL)
15✔
597
      continue; /* Layer without name cannot be used in contexts */
×
598

599
    tag = (char *)msSmallMalloc(sizeof(char) * strlen(GET_LAYER(map, i)->name) +
15✔
600
                                3);
601
    sprintf(tag, "[%s]", GET_LAYER(map, i)->name);
15✔
602

603
    if (strstr(e.string, tag)) {
15✔
604
      if (msLayerIsVisible(map, (GET_LAYER(map, i))))
3✔
605
        e.string = msReplaceSubstring(e.string, tag, "1");
1✔
606
      else
607
        e.string = msReplaceSubstring(e.string, tag, "0");
2✔
608
    }
609

610
    free(tag);
15✔
611
  }
612

613
  msTokenizeExpression(&e, NULL, NULL);
5✔
614

615
  /*
616
   * We'll check for binding tokens in the token list. Since there is no shape
617
   * to bind to, the parser will crash if any are present.
618
   * This is one way to catch references to non-existent layers.
619
   */
620
  for (token = e.tokens; token; token = token->next) {
11✔
621
    if (token->token == MS_TOKEN_BINDING_DOUBLE ||
8✔
622
        token->token == MS_TOKEN_BINDING_STRING ||
6✔
623
        token->token == MS_TOKEN_BINDING_TIME) {
624
      msSetError(MS_PARSEERR,
2✔
625
                 "A non-existent layer is referenced in a LAYER REQUIRES or "
626
                 "LABELREQUIRES expression: %s",
627
                 "msEvalContext()", e.string);
628
      msFreeExpression(&e);
2✔
629
      return MS_FALSE;
2✔
630
    }
631
  }
632

633
  p.shape = NULL;
3✔
634
  p.expr = &e;
3✔
635
  p.expr->curtoken = p.expr->tokens; /* reset */
3✔
636
  p.type = MS_PARSE_TYPE_BOOLEAN;
3✔
637

638
  status = yyparse(&p);
3✔
639

640
  msFreeExpression(&e);
3✔
641

642
  if (status != 0) {
3✔
643
    msSetError(MS_PARSEERR, "Failed to parse context", "msEvalContext");
×
644
    return MS_FALSE; /* error in parse */
×
645
  }
646

647
  return p.result.intval;
3✔
648
}
649

650
/* msEvalExpression()
651
 *
652
 * Evaluates a mapserver expression for a given set of attribute values and
653
 * returns the result of the expression (MS_TRUE or MS_FALSE)
654
 * May also return MS_FALSE in case of parsing errors or invalid expressions
655
 * (check the error stack if you care)
656
 *
657
 */
658
int msEvalExpression(layerObj *layer, shapeObj *shape,
151,607✔
659
                     expressionObj *expression, int itemindex) {
660
  if (MS_STRING_IS_NULL_OR_EMPTY(expression->string))
151,607✔
661
    return MS_TRUE; /* NULL or empty expressions are ALWAYS true */
662
  if (expression->native_string != NULL)
68,027✔
663
    return MS_TRUE; /* expressions that are evaluated natively are ALWAYS true
664
                     */
665

666
  switch (expression->type) {
66,121✔
667
  case (MS_STRING):
5,137✔
668
    if (itemindex == -1) {
5,137✔
669
      msSetError(MS_MISCERR,
×
670
                 "Cannot evaluate expression, no item index defined.",
671
                 "msEvalExpression()");
672
      return MS_FALSE;
×
673
    }
674
    if (itemindex >= layer->numitems || itemindex >= shape->numvalues) {
5,137✔
675
      msSetError(MS_MISCERR, "Invalid item index.", "msEvalExpression()");
×
676
      return MS_FALSE;
×
677
    }
678
    if (expression->flags & MS_EXP_INSENSITIVE) {
5,137✔
679
      if (strcasecmp(expression->string, shape->values[itemindex]) == 0)
117✔
680
        return MS_TRUE; /* got a match */
681
    } else {
682
      if (strcmp(expression->string, shape->values[itemindex]) == 0)
5,020✔
683
        return MS_TRUE; /* got a match */
684
    }
685
    break;
686
  case (MS_LIST):
22✔
687
    if (itemindex == -1) {
22✔
688
      msSetError(MS_MISCERR,
×
689
                 "Cannot evaluate expression, no item index defined.",
690
                 "msEvalExpression()");
691
      return MS_FALSE;
×
692
    }
693
    if (itemindex >= layer->numitems || itemindex >= shape->numvalues) {
22✔
694
      msSetError(MS_MISCERR, "Invalid item index.", "msEvalExpression()");
×
695
      return MS_FALSE;
×
696
    }
697
    {
698
      char *start, *end;
699
      int value_len = strlen(shape->values[itemindex]);
22✔
700
      start = expression->string;
701
      while ((end = strchr(start, ',')) != NULL) {
103✔
702
        if (value_len == end - start &&
83✔
703
            !strncmp(start, shape->values[itemindex], end - start))
9✔
704
          return MS_TRUE;
705
        start = end + 1;
81✔
706
      }
707
      if (!strcmp(start, shape->values[itemindex]))
20✔
708
        return MS_TRUE;
709
    }
710
    break;
711
  case (MS_EXPRESSION): {
59,933✔
712
    int status;
713
    parseObj p;
714

715
    p.shape = shape;
59,933✔
716
    p.expr = expression;
59,933✔
717
    p.expr->curtoken = p.expr->tokens; /* reset */
59,933✔
718
    p.type = MS_PARSE_TYPE_BOOLEAN;
59,933✔
719

720
    status = yyparse(&p);
59,933✔
721

722
    if (status != 0) {
59,933✔
723
      msSetError(MS_PARSEERR, "Failed to parse expression: %s",
28✔
724
                 "msEvalExpression", expression->string);
725
      return MS_FALSE;
28✔
726
    }
727

728
    return p.result.intval;
59,905✔
729
  }
730
  case (MS_REGEX):
1,029✔
731
    if (itemindex == -1) {
1,029✔
732
      msSetError(MS_MISCERR,
×
733
                 "Cannot evaluate expression, no item index defined.",
734
                 "msEvalExpression()");
735
      return MS_FALSE;
×
736
    }
737
    if (itemindex >= layer->numitems || itemindex >= shape->numvalues) {
1,029✔
738
      msSetError(MS_MISCERR, "Invalid item index.", "msEvalExpression()");
×
739
      return MS_FALSE;
×
740
    }
741

742
    if (MS_STRING_IS_NULL_OR_EMPTY(shape->values[itemindex]) == MS_TRUE)
1,029✔
743
      return MS_FALSE;
744

745
    if (!expression->compiled) {
1,029✔
746
      if (expression->flags & MS_EXP_INSENSITIVE) {
31✔
747
        if (ms_regcomp(&(expression->regex), expression->string,
1✔
748
                       MS_REG_EXTENDED | MS_REG_NOSUB | MS_REG_ICASE) !=
749
            0) { /* compile the expression */
750
          msSetError(MS_REGEXERR, "Invalid regular expression.",
×
751
                     "msEvalExpression()");
752
          return MS_FALSE;
×
753
        }
754
      } else {
755
        if (ms_regcomp(&(expression->regex), expression->string,
30✔
756
                       MS_REG_EXTENDED | MS_REG_NOSUB) !=
757
            0) { /* compile the expression */
758
          msSetError(MS_REGEXERR, "Invalid regular expression.",
×
759
                     "msEvalExpression()");
760
          return MS_FALSE;
×
761
        }
762
      }
763
      expression->compiled = MS_TRUE;
31✔
764
    }
765

766
    if (ms_regexec(&(expression->regex), shape->values[itemindex], 0, NULL,
1,029✔
767
                   0) == 0)
768
      return MS_TRUE; /* got a match */
769
    break;
770
  }
771

772
  return MS_FALSE;
773
}
774

775
int *msAllocateValidClassGroups(layerObj *lp, int *nclasses) {
144✔
776
  int *classgroup = NULL;
777
  int nvalidclass = 0, i = 0;
778

779
  if (!lp || !lp->classgroup || lp->numclasses <= 0 || !nclasses)
144✔
780
    return NULL;
781

782
  classgroup = (int *)msSmallMalloc(sizeof(int) * lp->numclasses);
144✔
783
  nvalidclass = 0;
784
  for (i = 0; i < lp->numclasses; i++) {
440✔
785
    if (lp->class[i] -> group && strcasecmp(lp->class[i] -> group,
296✔
786
                                            lp -> classgroup) == 0) {
296✔
787
      classgroup[nvalidclass] = i;
148✔
788
      nvalidclass++;
148✔
789
    }
790
  }
791
  if (nvalidclass > 0) {
144✔
792
    classgroup = (int *)msSmallRealloc(classgroup, sizeof(int) * nvalidclass);
144✔
793
    *nclasses = nvalidclass;
144✔
794
    return classgroup;
144✔
795
  }
796

797
  if (classgroup)
×
798
    msFree(classgroup);
×
799

800
  return NULL;
801
}
802

803
int msShapeGetClass(layerObj *layer, mapObj *map, shapeObj *shape,
7,248✔
804
                    int *classgroup, int numclasses) {
805
  return msShapeGetNextClass(-1, layer, map, shape, classgroup, numclasses);
7,248✔
806
}
807

808
int msShapeGetNextClass(int currentclass, layerObj *layer, mapObj *map,
70,948✔
809
                        shapeObj *shape, int *classgroup, int numclasses) {
810
  int i, iclass;
811

812
  if (currentclass < 0)
70,948✔
813
    currentclass = -1;
814

815
  if (layer->numclasses > 0) {
70,948✔
816
    if (classgroup == NULL || numclasses <= 0)
67,246✔
817
      numclasses = layer->numclasses;
818

819
    for (i = currentclass + 1; i < numclasses; i++) {
74,157✔
820
      if (classgroup)
41,767✔
821
        iclass = classgroup[i];
2,709✔
822
      else
823
        iclass = i;
824

825
      if (iclass < 0 || iclass >= layer->numclasses)
41,767✔
826
        continue; /* this should never happen but just in case */
×
827

828
      if (!msScaleInBounds(map->scaledenom,
41,767✔
829
                           layer->class[iclass] -> minscaledenom,
830
                           layer -> class[iclass] -> maxscaledenom)) {
41,767✔
831
        continue;
651✔
832
      }
833

834
      /* verify the minfeaturesize */
835
      if ((shape->type == MS_SHAPE_LINE || shape->type == MS_SHAPE_POLYGON) &&
41,116✔
836
          (layer->class[iclass] -> minfeaturesize > 0)) {
20,846✔
837
        double minfeaturesize =
838
            Pix2LayerGeoref(map, layer, layer->class[iclass] -> minfeaturesize);
×
839
        if (msShapeCheckSize(shape, minfeaturesize) == MS_FALSE)
×
840
          continue; /* skip this one, next class */
×
841
      }
842

843
      if (layer->class[iclass] -> status != MS_DELETE &&
82,232✔
844
                                      msEvalExpression(
41,116✔
845
                                          layer, shape,
846
                                          &(layer->class[iclass] -> expression),
847
                                          layer->classitemindex) == MS_TRUE) {
848
        if (layer->class[iclass] -> isfallback && currentclass != -1) {
34,856✔
849
          // Class is not applicable if it is flagged as fallback (<ElseFilter/>
850
          // tag in SLD) but other classes have been applied before.
851
          return -1;
852
        } else {
853
          return (iclass);
854
        }
855
      }
856
    }
857
  }
858

859
  return (-1); /* no match */
860
}
861

862
static char *msEvalTextExpressionInternal(expressionObj *expr, shapeObj *shape,
4,353✔
863
                                          int bJSonEscape) {
864
  char *result = NULL;
865

866
  if (!expr->string)
4,353✔
867
    return result; /* nothing to do */
868

869
  switch (expr->type) {
4,337✔
870
  case (MS_STRING): {
3,641✔
871
    char *target = NULL;
872
    char *pszEscaped;
873
    tokenListNodeObjPtr node = NULL;
874
    tokenListNodeObjPtr nextNode = NULL;
875

876
    result = msStrdup(expr->string);
3,641✔
877

878
    node = expr->tokens;
3,641✔
879
    if (node) {
3,641✔
880
      while (node != NULL) {
8,522✔
881
        nextNode = node->next;
5,086✔
882
        if (node->token == MS_TOKEN_BINDING_DOUBLE ||
5,086✔
883
            node->token == MS_TOKEN_BINDING_INTEGER ||
884
            node->token == MS_TOKEN_BINDING_STRING ||
5,086✔
885
            node->token == MS_TOKEN_BINDING_TIME) {
886
          target =
887
              (char *)msSmallMalloc(strlen(node->tokenval.bindval.item) + 3);
3,441✔
888
          sprintf(target, "[%s]", node->tokenval.bindval.item);
3,441✔
889
          if (bJSonEscape)
3,441✔
890
            pszEscaped =
891
                msEscapeJSonString(shape->values[node->tokenval.bindval.index]);
5✔
892
          else
893
            pszEscaped = msStrdup(shape->values[node->tokenval.bindval.index]);
3,436✔
894
          result = msReplaceSubstring(result, target, pszEscaped);
3,441✔
895
          msFree(pszEscaped);
3,441✔
896
          msFree(target);
3,441✔
897
        }
898
        node = nextNode;
899
      }
900
    }
901
    if (!strlen(result)) {
3,641✔
902
      msFree(result);
3✔
903
      result = NULL;
904
    }
905
  } break;
906
  case (MS_EXPRESSION): {
696✔
907
    int status;
908
    parseObj p;
909

910
    p.shape = shape;
696✔
911
    p.expr = expr;
696✔
912
    p.expr->curtoken = p.expr->tokens; /* reset */
696✔
913
    p.type = MS_PARSE_TYPE_STRING;
696✔
914

915
    status = yyparse(&p);
696✔
916

917
    if (status != 0) {
696✔
918
      msSetError(MS_PARSEERR, "Failed to process text expression: %s",
×
919
                 "msEvalTextExpression", expr->string);
920
      return NULL;
×
921
    }
922

923
    result = p.result.strval;
696✔
924
    break;
696✔
925
  }
926
  default:
927
    break;
928
  }
929
  if (result && !strlen(result)) {
4,334✔
930
    msFree(result);
24✔
931
    result = NULL;
932
  }
933
  return result;
934
}
935

936
char *msEvalTextExpressionJSonEscape(expressionObj *expr, shapeObj *shape) {
21✔
937
  return msEvalTextExpressionInternal(expr, shape, MS_TRUE);
21✔
938
}
939

940
char *msEvalTextExpression(expressionObj *expr, shapeObj *shape) {
4,332✔
941
  return msEvalTextExpressionInternal(expr, shape, MS_FALSE);
4,332✔
942
}
943

944
double msEvalDoubleExpression(expressionObj *expression, shapeObj *shape) {
48✔
945
  double value;
946
  int status;
947
  parseObj p;
948
  p.shape = shape;
48✔
949
  p.expr = expression;
48✔
950
  p.expr->curtoken = p.expr->tokens; /* reset */
48✔
951
  p.type = MS_PARSE_TYPE_STRING;
48✔
952
  status = yyparse(&p);
48✔
953
  if (status != 0) {
48✔
954
    msSetError(MS_PARSEERR, "Failed to parse expression: %s", "bindStyle",
×
955
               expression->string);
956
    value = 0.0;
957
  } else {
958
    value = atof(p.result.strval);
48✔
959
    msFree(p.result.strval);
48✔
960
  }
961
  return value;
48✔
962
}
963

964
char *msShapeGetLabelAnnotation(layerObj *layer, shapeObj *shape,
9,528✔
965
                                labelObj *lbl) {
966
  assert(shape && lbl);
967
  if (lbl->text.string) {
9,528✔
968
    return msEvalTextExpression(&(lbl->text), shape);
3,568✔
969
  } else if (layer->class[shape->classindex] -> text.string) {
5,960✔
970
    return msEvalTextExpression(&(layer->class[shape->classindex] -> text),
757✔
971
                                shape);
972
  } else {
973
    if (shape->values && layer->labelitemindex >= 0 &&
5,203✔
974
        shape->values[layer->labelitemindex] &&
338✔
975
        strlen(shape->values[layer->labelitemindex]))
976
      return msStrdup(shape->values[layer->labelitemindex]);
338✔
977
    else if (shape->text)
4,865✔
978
      return msStrdup(
1,480✔
979
          shape->text); /* last resort but common with iniline features */
980
  }
981
  return NULL;
982
}
983

984
int msGetLabelStatus(mapObj *map, layerObj *layer, shapeObj *shape,
34,340✔
985
                     labelObj *lbl) {
986
  assert(layer && lbl);
987
  if (!msScaleInBounds(map->scaledenom, lbl->minscaledenom, lbl->maxscaledenom))
34,340✔
988
    return MS_OFF;
989
  if (msEvalExpression(layer, shape, &(lbl->expression),
34,340✔
990
                       layer->labelitemindex) != MS_TRUE)
991
    return MS_OFF;
992
  /* TODO: check for minfeaturesize here ? */
993
  return MS_ON;
994
}
995

996
/* Check if the shape is enough big to be drawn with the
997
   layer::minfeaturesize setting. The minfeaturesize parameter should be
998
   the value in geo ref (not in pixel) and should have been multiplied by
999
   the resolution factor.
1000
 */
1001
int msShapeCheckSize(shapeObj *shape, double minfeaturesize) {
×
1002
  double dx = (shape->bounds.maxx - shape->bounds.minx);
×
1003
  double dy = (shape->bounds.maxy - shape->bounds.miny);
×
1004

1005
  if (pow(minfeaturesize, 2.0) > (pow(dx, 2.0) + pow(dy, 2.0)))
×
1006
    return MS_FALSE;
×
1007

1008
  return MS_TRUE;
1009
}
1010

1011
/*
1012
** Adjusts an image size in one direction to fit an extent.
1013
*/
1014
int msAdjustImage(rectObj rect, int *width, int *height) {
×
1015
  if (*width == -1 && *height == -1) {
×
1016
    msSetError(MS_MISCERR, "Cannot calculate both image height and width.",
×
1017
               "msAdjustImage()");
1018
    return (-1);
×
1019
  }
1020

1021
  if (*width > 0)
×
1022
    *height =
×
1023
        MS_NINT((rect.maxy - rect.miny) / ((rect.maxx - rect.minx) / (*width)));
×
1024
  else
1025
    *width = MS_NINT((rect.maxx - rect.minx) /
×
1026
                     ((rect.maxy - rect.miny) / (*height)));
1027

1028
  return (0);
1029
}
1030

1031
/*
1032
** Make sure extent fits image window to be created. Returns cellsize of output
1033
*image.
1034
*/
1035
double msAdjustExtent(rectObj *rect, int width, int height) {
2,232✔
1036
  double cellsize, ox, oy;
1037

1038
  if (width == 1 || height == 1)
2,232✔
1039
    return 0;
1040

1041
  cellsize = MS_MAX(MS_CELLSIZE(rect->minx, rect->maxx, width),
2,232✔
1042
                    MS_CELLSIZE(rect->miny, rect->maxy, height));
1043

1044
  if (cellsize <= 0) /* avoid division by zero errors */
2,232✔
1045
    return (0);
1046

1047
  ox = MS_MAX(((width - 1) - (rect->maxx - rect->minx) / cellsize) / 2,
2,109✔
1048
              0); /* these were width-1 and height-1 */
1049
  oy = MS_MAX(((height - 1) - (rect->maxy - rect->miny) / cellsize) / 2, 0);
2,109✔
1050

1051
  rect->minx = rect->minx - ox * cellsize;
2,109✔
1052
  rect->miny = rect->miny - oy * cellsize;
2,109✔
1053
  rect->maxx = rect->maxx + ox * cellsize;
2,109✔
1054
  rect->maxy = rect->maxy + oy * cellsize;
2,109✔
1055

1056
  return (cellsize);
2,109✔
1057
}
1058

1059
/*
1060
** Rect must always contain a portion of bounds. If not, rect is
1061
** shifted to overlap by overlay percent. The dimensions of rect do
1062
** not change but placement relative to bounds can.
1063
*/
1064
int msConstrainExtent(rectObj *bounds, rectObj *rect, double overlay) {
×
1065
  /* check left edge, and if necessary the right edge of bounds */
1066
  if (rect->maxx <= bounds->minx) {
×
1067
    const double offset = overlay * (rect->maxx - rect->minx);
×
1068
    rect->minx += offset; /* shift right */
×
1069
    rect->maxx += offset;
×
1070
  } else if (rect->minx >= bounds->maxx) {
×
1071
    const double offset = overlay * (rect->maxx - rect->minx);
×
1072
    rect->minx -= offset; /* shift left */
×
1073
    rect->maxx -= offset;
×
1074
  }
1075

1076
  /* check top edge, and if necessary the bottom edge of bounds */
1077
  if (rect->maxy <= bounds->miny) {
×
1078
    const double offset = overlay * (rect->maxy - rect->miny);
×
1079
    rect->miny -= offset; /* shift down */
×
1080
    rect->maxy -= offset;
×
1081
  } else if (rect->miny >= bounds->maxy) {
×
1082
    const double offset = overlay * (rect->maxy - rect->miny);
×
1083
    rect->miny += offset; /* shift up */
×
1084
    rect->maxy += offset;
×
1085
  }
1086

1087
  return (MS_SUCCESS);
×
1088
}
1089

1090
/*
1091
** Generic function to save an image to a file.
1092
**
1093
** Note that map may be NULL. If it is set, then it is used for two things:
1094
** - Deal with relative imagepaths (compute absolute path relative to map path)
1095
** - Extract the georeferenced extents and coordinate system
1096
**   of the map for writing out with the image when appropriate
1097
**   (primarily this means via msSaveImageGDAL() to something like GeoTIFF).
1098
**
1099
** The filename is NULL when the image is supposed to be written to stdout.
1100
*/
1101

1102
int msSaveImage(mapObj *map, imageObj *img, const char *filename) {
1,331✔
1103
  int nReturnVal = MS_FAILURE;
1104
  char szPath[MS_MAXPATHLEN];
1105
  struct mstimeval starttime = {0}, endtime = {0};
1,331✔
1106

1107
  if (map && map->debug >= MS_DEBUGLEVEL_TUNING) {
1,331✔
1108
    msGettimeofday(&starttime, NULL);
4✔
1109
  }
1110

1111
  if (img) {
1,331✔
1112
    if (MS_DRIVER_GDAL(img->format)) {
1,331✔
1113
      if (map != NULL && filename != NULL)
211✔
1114
        nReturnVal = msSaveImageGDAL(
189✔
1115
            map, img, msBuildPath(szPath, map->mappath, filename));
189✔
1116
      else
1117
        nReturnVal = msSaveImageGDAL(map, img, filename);
22✔
1118
    } else
1119

1120
        if (MS_RENDERER_PLUGIN(img->format)) {
1,120✔
1121
      rendererVTableObj *renderer = img->format->vtable;
1,120✔
1122
      FILE *stream = NULL;
1123
      if (filename) {
1,120✔
1124
        if (map)
601✔
1125
          stream = fopen(msBuildPath(szPath, map->mappath, filename), "wb");
567✔
1126
        else
1127
          stream = fopen(filename, "wb");
34✔
1128

1129
        if (!stream) {
601✔
1130
          msSetError(MS_IOERR, "Failed to create output file (%s).",
×
1131
                     "msSaveImage()", (map ? szPath : filename));
1132
          return MS_FAILURE;
×
1133
        }
1134

1135
      } else {
1136
        if (msIO_needBinaryStdout() == MS_FAILURE)
519✔
1137
          return MS_FAILURE;
1138
        stream = stdout;
519✔
1139
      }
1140

1141
      if (renderer->supports_pixel_buffer) {
1,120✔
1142
        rasterBufferObj data;
1143
        if (renderer->getRasterBufferHandle(img, &data) != MS_SUCCESS) {
953✔
1144
          if (stream != stdout)
×
1145
            fclose(stream);
×
1146
          return MS_FAILURE;
×
1147
        }
1148

1149
        nReturnVal = msSaveRasterBuffer(map, &data, stream, img->format);
953✔
1150
      } else {
1151
        nReturnVal = renderer->saveImage(img, map, stream, img->format);
167✔
1152
      }
1153
      if (stream != stdout)
1,120✔
1154
        fclose(stream);
601✔
1155

1156
    } else if (MS_DRIVER_IMAGEMAP(img->format))
×
1157
      nReturnVal = msSaveImageIM(img, filename, img->format);
×
1158
    else
1159
      msSetError(MS_MISCERR, "Unknown image type", "msSaveImage()");
×
1160
  }
1161

1162
  if (map && map->debug >= MS_DEBUGLEVEL_TUNING) {
1,331✔
1163
    msGettimeofday(&endtime, NULL);
4✔
1164
    msDebug("msSaveImage(%s) total time: %.3fs\n",
4✔
1165
            (filename ? filename : "stdout"),
1166
            (endtime.tv_sec + endtime.tv_usec / 1.0e6) -
4✔
1167
                (starttime.tv_sec + starttime.tv_usec / 1.0e6));
4✔
1168
  }
1169

1170
  return nReturnVal;
1171
}
1172

1173
/*
1174
** Generic function to save an image to a byte array.
1175
** - the return value is the pointer to the byte array
1176
** - size_ptr contains the number of bytes returned
1177
** - format: the desired output format
1178
**
1179
** The caller is responsible to free the returned array
1180
** The function returns NULL if the output format is not supported.
1181
*/
1182

1183
unsigned char *msSaveImageBuffer(imageObj *image, int *size_ptr,
15✔
1184
                                 outputFormatObj *format) {
1185
  *size_ptr = 0;
15✔
1186
  if (MS_RENDERER_PLUGIN(image->format)) {
15✔
1187
    rasterBufferObj data;
1188
    rendererVTableObj *renderer = image->format->vtable;
15✔
1189
    if (renderer->supports_pixel_buffer) {
15✔
1190
      bufferObj buffer;
1191
      msBufferInit(&buffer);
15✔
1192
      const int status = renderer->getRasterBufferHandle(image, &data);
15✔
1193
      if (MS_UNLIKELY(status == MS_FAILURE)) {
15✔
1194
        return NULL;
1195
      }
1196
      msSaveRasterBufferToBuffer(&data, &buffer, format);
15✔
1197
      *size_ptr = buffer.size;
15✔
1198
      return buffer.data;
15✔
1199
      /* don't free the bufferObj as we don't own the bytes anymore */
1200
    } else {
1201
      /* check if the renderer supports native buffer output */
1202
      if (renderer->saveImageBuffer)
×
1203
        return renderer->saveImageBuffer(image, size_ptr, format);
×
1204

1205
      msSetError(MS_MISCERR, "Unsupported image type", "msSaveImageBuffer()");
×
1206
      return NULL;
×
1207
    }
1208
  }
1209
  msSetError(MS_MISCERR, "Unsupported image type", "msSaveImage()");
×
1210
  return NULL;
×
1211
}
1212

1213
/**
1214
 * Generic function to free the imageObj
1215
 */
1216
void msFreeImage(imageObj *image) {
2,056✔
1217
  if (image) {
2,056✔
1218
    if (MS_RENDERER_PLUGIN(image->format)) {
2,056✔
1219
      rendererVTableObj *renderer = image->format->vtable;
1,747✔
1220
      tileCacheObj *next, *cur = image->tilecache;
1,747✔
1221
      while (cur) {
1,784✔
1222
        msFreeImage(cur->image);
37✔
1223
        next = cur->next;
37✔
1224
        free(cur);
37✔
1225
        cur = next;
1226
      }
1227
      image->ntiles = 0;
1,747✔
1228
      renderer->freeImage(image);
1,747✔
1229
    } else if (MS_RENDERER_IMAGEMAP(image->format))
309✔
1230
      msFreeImageIM(image);
×
1231
    else if (MS_RENDERER_RAWDATA(image->format))
309✔
1232
      msFree(image->img.raw_16bit);
309✔
1233
    else
1234
      msSetError(MS_MISCERR, "Unknown image type", "msFreeImage()");
×
1235

1236
    if (image->imagepath)
2,056✔
1237
      free(image->imagepath);
1,741✔
1238
    if (image->imageurl)
2,056✔
1239
      free(image->imageurl);
1,741✔
1240

1241
    if (--image->format->refcount < 1)
2,056✔
1242
      msFreeOutputFormat(image->format);
517✔
1243

1244
    image->imagepath = NULL;
1245
    image->imageurl = NULL;
1246

1247
    msFree(image->img_mask);
2,056✔
1248
    image->img_mask = NULL;
1249

1250
    msFree(image);
2,056✔
1251
  }
1252
}
2,056✔
1253

1254
/*
1255
** Return an array containing all the layer's index given a group name.
1256
** If nothing is found, NULL is returned. The nCount is initialized
1257
** to the number of elements in the returned array.
1258
** Note : the caller of the function should free the array.
1259
*/
1260
int *msGetLayersIndexByGroup(mapObj *map, char *groupname, int *pnCount) {
1✔
1261
  int i;
1262
  int iLayer = 0;
1263
  int *aiIndex;
1264

1265
  if (!groupname || !map || !pnCount) {
1✔
1266
    return NULL;
1267
  }
1268

1269
  aiIndex = (int *)msSmallMalloc(sizeof(int) * map->numlayers);
1✔
1270

1271
  for (i = 0; i < map->numlayers; i++) {
3✔
1272
    if (!GET_LAYER(map, i)->group) /* skip it */
2✔
1273
      continue;
2✔
1274
    if (strcmp(groupname, GET_LAYER(map, i)->group) == 0) {
×
1275
      aiIndex[iLayer] = i;
×
1276
      iLayer++;
×
1277
    }
1278
  }
1279

1280
  if (iLayer == 0) {
1✔
1281
    free(aiIndex);
1✔
1282
    aiIndex = NULL;
1283
    *pnCount = 0;
1✔
1284
  } else {
1285
    aiIndex = (int *)msSmallRealloc(aiIndex, sizeof(int) * iLayer);
×
1286
    *pnCount = iLayer;
×
1287
  }
1288

1289
  return aiIndex;
1290
}
1291

1292
/* ==================================================================== */
1293
/*      Measured shape utility functions.                               */
1294
/* ==================================================================== */
1295

1296
/************************************************************************/
1297
/*        pointObj *msGetPointUsingMeasure(shapeObj *shape, double m)   */
1298
/*                                                                      */
1299
/*      Using a measured value get the XY location it corresponds        */
1300
/*      to.                                                             */
1301
/*                                                                      */
1302
/************************************************************************/
1303
pointObj *msGetPointUsingMeasure(shapeObj *shape, double m) {
×
1304
  pointObj *point = NULL;
1305
  int bFound = 0;
1306
  double dfFirstPointX = 0;
1307
  double dfFirstPointY = 0;
1308
  double dfFirstPointM = 0;
1309
  double dfSecondPointX = 0;
1310
  double dfSecondPointY = 0;
1311
  double dfSecondPointM = 0;
1312

1313
  if (shape && shape->numlines > 0) {
×
1314
    /* -------------------------------------------------------------------- */
1315
    /*      check fir the first value (min) and the last value(max) to      */
1316
    /*      see if the m is contained between these min and max.            */
1317
    /* -------------------------------------------------------------------- */
1318
    const lineObj *lineFirst = &(shape->line[0]);
×
1319
    const double dfMin = lineFirst->point[0].m;
×
1320
    const lineObj *lineLast = &(shape->line[shape->numlines - 1]);
×
1321
    const double dfMax = lineLast->point[lineLast->numpoints - 1].m;
×
1322

1323
    if (m >= dfMin && m <= dfMax) {
×
1324
      for (int i = 0; i < shape->numlines; i++) {
×
1325
        const lineObj *line = &(shape->line[i]);
×
1326

1327
        for (int j = 0; j < line->numpoints; j++) {
×
1328
          const double dfCurrentM = line->point[j].m;
×
1329
          if (dfCurrentM > m) {
×
1330
            bFound = 1;
1331

1332
            dfSecondPointX = line->point[j].x;
×
1333
            dfSecondPointY = line->point[j].y;
×
1334
            dfSecondPointM = line->point[j].m;
1335

1336
            /* --------------------------------------------------------------------
1337
             */
1338
            /*      get the previous node xy values. */
1339
            /* --------------------------------------------------------------------
1340
             */
1341
            if (j > 0) { /* not the first point of the line */
×
1342
              dfFirstPointX = line->point[j - 1].x;
×
1343
              dfFirstPointY = line->point[j - 1].y;
×
1344
              dfFirstPointM = line->point[j - 1].m;
×
1345
            } else { /* get last point of previous line */
1346
              dfFirstPointX = shape->line[i - 1].point[0].x;
×
1347
              dfFirstPointY = shape->line[i - 1].point[0].y;
×
1348
              dfFirstPointM = shape->line[i - 1].point[0].m;
×
1349
            }
1350
            break;
1351
          }
1352
        }
1353
      }
1354
    }
1355

1356
    if (!bFound)
×
1357
      return NULL;
1358

1359
    /* -------------------------------------------------------------------- */
1360
    /*      extrapolate the m value to get t he xy coordinate.              */
1361
    /* -------------------------------------------------------------------- */
1362

1363
    double dfFactor = 0;
1364
    if (dfFirstPointM != dfSecondPointM)
×
1365
      dfFactor = (m - dfFirstPointM) / (dfSecondPointM - dfFirstPointM);
×
1366

1367
    point = (pointObj *)msSmallMalloc(sizeof(pointObj));
×
1368

1369
    point->x = dfFirstPointX + (dfFactor * (dfSecondPointX - dfFirstPointX));
×
1370
    point->y = dfFirstPointY + (dfFactor * (dfSecondPointY - dfFirstPointY));
×
1371
    point->m = dfFirstPointM + (dfFactor * (dfSecondPointM - dfFirstPointM));
×
1372

1373
    return point;
×
1374
  }
1375

1376
  return NULL;
1377
}
1378

1379
/************************************************************************/
1380
/*       IntersectionPointLinepointObj *p, pointObj *a, pointObj *b)    */
1381
/*                                                                      */
1382
/*      Returns a point object corresponding to the intersection of     */
1383
/*      point p and a line formed of 2 points : a and b.                */
1384
/*                                                                      */
1385
/*      Alorith base on :                                               */
1386
/*      http://www.faqs.org/faqs/graphics/algorithms-faq/               */
1387
/*                                                                      */
1388
/*      Subject 1.02:How do I find the distance from a point to a line? */
1389
/*                                                                      */
1390
/*          Let the point be C (Cx,Cy) and the line be AB (Ax,Ay) to (Bx,By).*/
1391
/*          Let P be the point of perpendicular projection of C on AB.  The
1392
 * parameter*/
1393
/*          r, which indicates P's position along AB, is computed by the dot
1394
 * product */
1395
/*          of AC and AB divided by the square of the length of AB:     */
1396
/*                                                                      */
1397
/*          (1)     AC dot AB                                           */
1398
/*              r = ---------                                           */
1399
/*                  ||AB||^2                                            */
1400
/*                                                                      */
1401
/*          r has the following meaning:                                */
1402
/*                                                                      */
1403
/*              r=0      P = A                                          */
1404
/*              r=1      P = B                                          */
1405
/*              r<0      P is on the backward extension of AB           */
1406
/*              r>1      P is on the forward extension of AB            */
1407
/*              0<r<1    P is interior to AB                            */
1408
/*                                                                      */
1409
/*          The length of a line segment in d dimensions, AB is computed by:*/
1410
/*                                                                      */
1411
/*              L = sqrt( (Bx-Ax)^2 + (By-Ay)^2 + ... + (Bd-Ad)^2)      */
1412
/*                                                                      */
1413
/*          so in 2D:                                                   */
1414
/*                                                                      */
1415
/*              L = sqrt( (Bx-Ax)^2 + (By-Ay)^2 )                       */
1416
/*                                                                      */
1417
/*          and the dot product of two vectors in d dimensions, U dot V is
1418
 * computed:*/
1419
/*                                                                      */
1420
/*              D = (Ux * Vx) + (Uy * Vy) + ... + (Ud * Vd)             */
1421
/*                                                                      */
1422
/*          so in 2D:                                                   */
1423
/*                                                                      */
1424
/*              D = (Ux * Vx) + (Uy * Vy)                               */
1425
/*                                                                      */
1426
/*          So (1) expands to:                                          */
1427
/*                                                                      */
1428
/*                  (Cx-Ax)(Bx-Ax) + (Cy-Ay)(By-Ay)                     */
1429
/*              r = -------------------------------                     */
1430
/*                                L^2                                   */
1431
/*                                                                      */
1432
/*          The point P can then be found:                              */
1433
/*                                                                      */
1434
/*              Px = Ax + r(Bx-Ax)                                      */
1435
/*              Py = Ay + r(By-Ay)                                      */
1436
/*                                                                      */
1437
/*          And the distance from A to P = r*L.                         */
1438
/*                                                                      */
1439
/*          Use another parameter s to indicate the location along PC, with the
1440
 */
1441
/*          following meaning:                                          */
1442
/*                 s<0      C is left of AB                             */
1443
/*                 s>0      C is right of AB                            */
1444
/*                 s=0      C is on AB                                  */
1445
/*                                                                      */
1446
/*          Compute s as follows:                                       */
1447
/*                                                                      */
1448
/*                  (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay)                       */
1449
/*              s = -----------------------------                       */
1450
/*                              L^2                                     */
1451
/*                                                                      */
1452
/*                                                                      */
1453
/*          Then the distance from C to P = |s|*L.                      */
1454
/*                                                                      */
1455
/************************************************************************/
1456
pointObj *msIntersectionPointLine(pointObj *p, pointObj *a, pointObj *b) {
×
1457
  pointObj *result = NULL;
1458

1459
  if (p && a && b) {
×
1460
    const double L =
1461
        sqrt(((b->x - a->x) * (b->x - a->x)) + ((b->y - a->y) * (b->y - a->y)));
×
1462

1463
    double r = 0;
1464
    if (L != 0)
×
1465
      r = ((p->x - a->x) * (b->x - a->x) + (p->y - a->y) * (b->y - a->y)) /
×
1466
          (L * L);
×
1467

1468
    result = (pointObj *)msSmallMalloc(sizeof(pointObj));
×
1469
    /* -------------------------------------------------------------------- */
1470
    /*      We want to make sure that the point returned is on the line     */
1471
    /*                                                                      */
1472
    /*              r=0      P = A                                          */
1473
    /*              r=1      P = B                                          */
1474
    /*              r<0      P is on the backward extension of AB           */
1475
    /*              r>1      P is on the forward extension of AB            */
1476
    /*                    0<r<1    P is interior to AB                      */
1477
    /* -------------------------------------------------------------------- */
1478
    if (r < 0) {
×
1479
      result->x = a->x;
×
1480
      result->y = a->y;
×
1481
    } else if (r > 1) {
×
1482
      result->x = b->x;
×
1483
      result->y = b->y;
×
1484
    } else {
1485
      result->x = a->x + r * (b->x - a->x);
×
1486
      result->y = a->y + r * (b->y - a->y);
×
1487
    }
1488
    result->m = 0;
×
1489
  }
1490

1491
  return result;
×
1492
}
1493

1494
/************************************************************************/
1495
/*         pointObj *msGetMeasureUsingPoint(shapeObj *shape, pointObj   */
1496
/*      *point)                                                         */
1497
/*                                                                      */
1498
/*      Calculate the intersection point betwwen the point and the      */
1499
/*      shape and return the Measured value at the intersection.        */
1500
/************************************************************************/
1501
pointObj *msGetMeasureUsingPoint(shapeObj *shape, pointObj *point) {
×
1502
  pointObj oFirst = {0, 0, 0, 0};
×
1503
  pointObj oSecond = {0, 0, 0, 0};
×
1504

1505
  if (shape && point) {
×
1506
    double dfMinDist = HUGE_VAL;
1507
    for (int i = 0; i < shape->numlines; i++) {
×
1508
      lineObj line = shape->line[i];
×
1509
      /* -------------------------------------------------------------------- */
1510
      /*      for each line (2 consecutive lines) get the distance between    */
1511
      /*      the line and the point and determine which line segment is      */
1512
      /*      the closeset to the point.                                      */
1513
      /* -------------------------------------------------------------------- */
1514
      for (int j = 0; j < line.numpoints - 1; j++) {
×
1515
        const double dfDist =
1516
            msDistancePointToSegment(point, &line.point[j], &line.point[j + 1]);
×
1517
        if (dfDist < dfMinDist) {
×
1518
          oFirst.x = line.point[j].x;
×
1519
          oFirst.y = line.point[j].y;
×
1520
          oFirst.m = line.point[j].m;
×
1521

1522
          oSecond.x = line.point[j + 1].x;
×
1523
          oSecond.y = line.point[j + 1].y;
×
1524
          oSecond.m = line.point[j + 1].m;
×
1525

1526
          dfMinDist = dfDist;
1527
        }
1528
      }
1529
    }
1530
    /* -------------------------------------------------------------------- */
1531
    /*      once we have the nearest segment, look for the x,y location     */
1532
    /*      which is the nearest intersection between the line and the      */
1533
    /*      point.                                                          */
1534
    /* -------------------------------------------------------------------- */
1535
    pointObj *poIntersectionPt =
1536
        msIntersectionPointLine(point, &oFirst, &oSecond);
×
1537
    if (poIntersectionPt) {
×
1538
      const double dfDistTotal =
1539
          sqrt(((oSecond.x - oFirst.x) * (oSecond.x - oFirst.x)) +
×
1540
               ((oSecond.y - oFirst.y) * (oSecond.y - oFirst.y)));
×
1541

1542
      const double dfDistToIntersection =
1543
          sqrt(((poIntersectionPt->x - oFirst.x) *
×
1544
                (poIntersectionPt->x - oFirst.x)) +
1545
               ((poIntersectionPt->y - oFirst.y) *
×
1546
                (poIntersectionPt->y - oFirst.y)));
1547

1548
      const double dfFactor = dfDistToIntersection / dfDistTotal;
×
1549

1550
      poIntersectionPt->m = oFirst.m + (oSecond.m - oFirst.m) * dfFactor;
×
1551
      return poIntersectionPt;
×
1552
    }
1553
  }
1554
  return NULL;
1555
}
1556

1557
/* ==================================================================== */
1558
/*   End   Measured shape utility functions.                            */
1559
/* ==================================================================== */
1560

1561
char **msGetAllGroupNames(mapObj *map, int *numTok) {
×
1562
  char **papszGroups = NULL;
1563

1564
  assert(map);
1565
  *numTok = 0;
×
1566

1567
  if (!map->layerorder) {
×
1568
    map->layerorder = (int *)msSmallMalloc(map->numlayers * sizeof(int));
×
1569

1570
    /*
1571
     * Initiate to default order
1572
     */
1573
    for (int i = 0; i < map->numlayers; i++)
×
1574
      map->layerorder[i] = i;
×
1575
  }
1576

1577
  if (map->numlayers > 0) {
×
1578
    const int nCount = map->numlayers;
1579
    papszGroups = (char **)msSmallMalloc(sizeof(char *) * nCount);
×
1580

1581
    for (int i = 0; i < nCount; i++)
×
1582
      papszGroups[i] = NULL;
×
1583

1584
    for (int i = 0; i < nCount; i++) {
×
1585
      layerObj *lp;
1586
      lp = (GET_LAYER(map, map->layerorder[i]));
×
1587

1588
      int bFound = 0;
1589
      if (lp->group && lp->status != MS_DELETE) {
×
1590
        for (int j = 0; j < *numTok; j++) {
×
1591
          if (papszGroups[j] && strcmp(lp->group, papszGroups[j]) == 0) {
×
1592
            bFound = 1;
1593
            break;
1594
          }
1595
        }
1596
        if (!bFound) {
×
1597
          /* New group... add to the list of groups found */
1598
          papszGroups[(*numTok)] = msStrdup(lp->group);
×
1599
          (*numTok)++;
×
1600
        }
1601
      }
1602
    }
1603
  }
1604

1605
  return papszGroups;
×
1606
}
1607

1608
/************************************************************************/
1609
/*                         msForceTmpFileBase()                         */
1610
/************************************************************************/
1611

1612
static int tmpCount = 0;
1613
static char *ForcedTmpBase = NULL;
1614

1615
void msForceTmpFileBase(const char *new_base) {
2,784✔
1616
  /* -------------------------------------------------------------------- */
1617
  /*      Clear previous setting, if any.                                 */
1618
  /* -------------------------------------------------------------------- */
1619
  if (ForcedTmpBase != NULL) {
2,784✔
1620
    free(ForcedTmpBase);
×
1621
    ForcedTmpBase = NULL;
×
1622
  }
1623

1624
  tmpCount = -1;
2,784✔
1625

1626
  if (new_base == NULL)
2,784✔
1627
    return;
1628

1629
  /* -------------------------------------------------------------------- */
1630
  /*      Record new base.                                                */
1631
  /* -------------------------------------------------------------------- */
1632
  ForcedTmpBase = msStrdup(new_base);
×
1633
  tmpCount = 0;
×
1634
}
1635

1636
/**********************************************************************
1637
 *                          msTmpFile()
1638
 *
1639
 * Generate a Unique temporary file.
1640
 *
1641
 * Returns char* which must be freed by caller.
1642
 **********************************************************************/
1643
char *msTmpFile(mapObj *map, const char *mappath, const char *tmppath,
256✔
1644
                const char *ext) {
1645
  char szPath[MS_MAXPATHLEN];
1646
  const char *fullFname;
1647
  char *tmpFileName; /* big enough for time + pid + ext */
1648
  char *tmpBase = NULL;
1649

1650
  tmpBase = msTmpPath(map, mappath, tmppath);
256✔
1651
  tmpFileName = msTmpFilename(ext);
256✔
1652

1653
  fullFname = msBuildPath(szPath, tmpBase, tmpFileName);
256✔
1654

1655
  free(tmpFileName);
256✔
1656
  free(tmpBase);
256✔
1657

1658
  if (fullFname)
256✔
1659
    return msStrdup(fullFname);
256✔
1660

1661
  return NULL;
1662
}
1663

1664
/**********************************************************************
1665
 *                          msTmpPath()
1666
 *
1667
 * Return the temporary path based on the platform.
1668
 *
1669
 * Returns char* which must be freed by caller.
1670
 **********************************************************************/
1671
char *msTmpPath(mapObj *map, const char *mappath, const char *tmppath) {
266✔
1672
  char szPath[MS_MAXPATHLEN];
1673
  const char *fullPath;
1674
  const char *tmpBase;
1675
  const char *ms_temppath;
1676
#ifdef _WIN32
1677
  DWORD dwRetVal = 0;
1678
  TCHAR lpTempPathBuffer[MAX_PATH];
1679
#endif
1680

1681
  if (ForcedTmpBase != NULL)
266✔
1682
    tmpBase = ForcedTmpBase;
1683
  else if (tmppath != NULL)
266✔
1684
    tmpBase = tmppath;
1685
  else if ((ms_temppath = CPLGetConfigOption("MS_TEMPPATH", NULL)) != NULL)
38✔
1686
    tmpBase = ms_temppath;
1687
  else if (map && map->web.temppath)
38✔
1688
    tmpBase = map->web.temppath;
1689
  else { /* default paths */
1690
#ifndef _WIN32
1691
    tmpBase = "/tmp/";
1692
#else
1693
    dwRetVal = GetTempPath(MAX_PATH,          /* length of the buffer */
1694
                           lpTempPathBuffer); /* buffer for path */
1695
    if (dwRetVal > MAX_PATH || (dwRetVal == 0)) {
1696
      tmpBase = "C:\\";
1697
    } else {
1698
      tmpBase = (char *)lpTempPathBuffer;
1699
    }
1700
#endif
1701
  }
1702

1703
  fullPath = msBuildPath(szPath, mappath, tmpBase);
266✔
1704
  return msStrdup(fullPath);
266✔
1705
}
1706

1707
/**********************************************************************
1708
 *                          msTmpFilename()
1709
 *
1710
 * Generate a Unique temporary filename.
1711
 *
1712
 * Returns char* which must be freed by caller.
1713
 **********************************************************************/
1714
char *msTmpFilename(const char *ext) {
257✔
1715
  char *tmpFname;
1716
  int tmpFnameBufsize;
1717
  char *fullFname;
1718
  char tmpId[128]; /* big enough for time + pid + ext */
1719

1720
  snprintf(tmpId, sizeof(tmpId), "%lx_%x", (long)time(NULL), (int)getpid());
257✔
1721

1722
  if (ext == NULL)
257✔
1723
    ext = "";
1724
  tmpFnameBufsize = strlen(tmpId) + 10 + strlen(ext) + 1;
257✔
1725
  tmpFname = (char *)msSmallMalloc(tmpFnameBufsize);
257✔
1726

1727
  msAcquireLock(TLOCK_TMPFILE);
257✔
1728
  snprintf(tmpFname, tmpFnameBufsize, "%s_%x.%s", tmpId, tmpCount++, ext);
257✔
1729
  msReleaseLock(TLOCK_TMPFILE);
257✔
1730

1731
  fullFname = msStrdup(tmpFname);
257✔
1732
  free(tmpFname);
257✔
1733

1734
  return fullFname;
257✔
1735
}
1736

1737
/**
1738
 *  Generic function to Initialize an image object.
1739
 */
1740
imageObj *msImageCreate(int width, int height, outputFormatObj *format,
845✔
1741
                        char *imagepath, char *imageurl, double resolution,
1742
                        double defresolution, colorObj *bg) {
1743
  imageObj *image = NULL;
1744
  if (MS_RENDERER_PLUGIN(format)) {
845✔
1745

1746
    image = format->vtable->createImage(width, height, format, bg);
536✔
1747
    if (image == NULL) {
536✔
1748
      msSetError(MS_MEMERR, "Unable to create new image object.",
×
1749
                 "msImageCreate()");
1750
      return NULL;
×
1751
    }
1752

1753
    image->format = format;
536✔
1754
    format->refcount++;
536✔
1755

1756
    image->width = width;
536✔
1757
    image->height = height;
536✔
1758
    image->imagepath = NULL;
536✔
1759
    image->imageurl = NULL;
536✔
1760
    image->tilecache = NULL;
536✔
1761
    image->ntiles = 0;
536✔
1762
    image->resolution = resolution;
536✔
1763
    image->resolutionfactor = resolution / defresolution;
536✔
1764

1765
    if (imagepath)
536✔
1766
      image->imagepath = msStrdup(imagepath);
351✔
1767
    if (imageurl)
536✔
1768
      image->imageurl = msStrdup(imageurl);
351✔
1769
  } else if (MS_RENDERER_RAWDATA(format)) {
309✔
1770
    if (format->imagemode != MS_IMAGEMODE_INT16 &&
309✔
1771
        format->imagemode != MS_IMAGEMODE_FLOAT32 &&
309✔
1772
        format->imagemode != MS_IMAGEMODE_FLOAT64 &&
212✔
1773
        format->imagemode != MS_IMAGEMODE_BYTE) {
1774
      msSetError(MS_IMGERR,
×
1775
                 "Attempt to use illegal imagemode with rawdata renderer.",
1776
                 "msImageCreate()");
1777
      return NULL;
×
1778
    }
1779

1780
    image = (imageObj *)calloc(1, sizeof(imageObj));
309✔
1781
    if (image == NULL) {
309✔
1782
      msSetError(MS_MEMERR, "Unable to create new image object.",
×
1783
                 "msImageCreate()");
1784
      return NULL;
×
1785
    }
1786

1787
    if (format->imagemode == MS_IMAGEMODE_INT16)
309✔
1788
      image->img.raw_16bit = (short *)msSmallCalloc(
38✔
1789
          sizeof(short), ((size_t)width) * height * format->bands);
38✔
1790
    else if (format->imagemode == MS_IMAGEMODE_FLOAT32)
271✔
1791
      image->img.raw_float = (float *)msSmallCalloc(
55✔
1792
          sizeof(float), ((size_t)width) * height * format->bands);
55✔
1793
    else if (format->imagemode == MS_IMAGEMODE_FLOAT64)
216✔
1794
      image->img.raw_double = (double *)msSmallCalloc(
4✔
1795
          sizeof(double), ((size_t)width) * height * format->bands);
4✔
1796
    else if (format->imagemode == MS_IMAGEMODE_BYTE)
1797
      image->img.raw_byte = (unsigned char *)msSmallCalloc(
212✔
1798
          sizeof(unsigned char), ((size_t)width) * height * format->bands);
212✔
1799

1800
    if (image->img.raw_16bit == NULL) {
309✔
1801
      msFree(image);
×
1802
      msSetError(MS_IMGERR,
×
1803
                 "Attempt to allocate raw image failed, out of memory.",
1804
                 "msImageCreate()");
1805
      return NULL;
×
1806
    }
1807

1808
    image->img_mask = msAllocBitArray(width * height);
309✔
1809

1810
    image->format = format;
309✔
1811
    format->refcount++;
309✔
1812

1813
    image->width = width;
309✔
1814
    image->height = height;
309✔
1815
    image->imagepath = NULL;
309✔
1816
    image->imageurl = NULL;
309✔
1817
    image->resolution = resolution;
309✔
1818
    image->resolutionfactor = resolution / defresolution;
309✔
1819

1820
    if (imagepath)
309✔
1821
      image->imagepath = msStrdup(imagepath);
179✔
1822
    if (imageurl)
309✔
1823
      image->imageurl = msStrdup(imageurl);
179✔
1824

1825
    /* initialize to requested nullvalue if there is one */
1826
    const char *nullvalue =
1827
        msGetOutputFormatOption(image->format, "NULLVALUE", NULL);
309✔
1828
    if (nullvalue != NULL) {
309✔
1829
      int i = image->width * image->height * format->bands;
34✔
1830
      if (atof(nullvalue) == 0.0)
34✔
1831
        /* nothing to do */;
1832
      else if (format->imagemode == MS_IMAGEMODE_INT16) {
34✔
1833
        short nv = atoi(nullvalue);
31✔
1834
        for (; i > 0;)
31,941✔
1835
          image->img.raw_16bit[--i] = nv;
31,910✔
1836
      } else if (format->imagemode == MS_IMAGEMODE_FLOAT32) {
3✔
1837
        float nv = atof(nullvalue);
3✔
1838
        for (; i > 0;)
135✔
1839
          image->img.raw_float[--i] = nv;
132✔
1840
      } else if (format->imagemode == MS_IMAGEMODE_FLOAT64) {
×
1841
        double nv = atof(nullvalue);
1842
        for (; i > 0;)
×
1843
          image->img.raw_double[--i] = nv;
×
1844
      } else if (format->imagemode == MS_IMAGEMODE_BYTE) {
×
1845
        unsigned char nv = (unsigned char)atoi(nullvalue);
×
1846

1847
        memset(image->img.raw_byte, nv, i);
×
1848
      }
1849
    }
1850
  } else if (MS_RENDERER_IMAGEMAP(format)) {
×
1851
    image = msImageCreateIM(width, height, format, imagepath, imageurl,
×
1852
                            resolution, defresolution);
1853
  } else {
1854
    msSetError(MS_MISCERR,
×
1855
               "Unsupported renderer requested, unable to initialize image.",
1856
               "msImageCreate()");
1857
    return NULL;
×
1858
  }
1859

1860
  if (!image) {
219✔
1861
    msSetError(MS_IMGERR, "Unable to initialize image.", "msImageCreate()");
×
1862
    return NULL;
×
1863
  }
1864
  image->refpt.x = image->refpt.y = 0;
845✔
1865
  return image;
845✔
1866
}
1867

1868
/**
1869
 * Generic function to transform a point.
1870
 *
1871
 */
1872
void msTransformPoint(pointObj *point, rectObj *extent, double cellsize,
23,398✔
1873
                      imageObj *image) {
1874
  double invcellsize;
1875
  /*We should probably have a function defined at all the renders*/
1876
  if (image != NULL && MS_RENDERER_PLUGIN(image->format)) {
23,398✔
1877
    if (image->format->renderer == MS_RENDER_WITH_KML) {
23,398✔
1878
      return;
1879
    }
1880
  }
1881
  invcellsize = 1.0 / cellsize;
23,371✔
1882
  point->x = MS_MAP2IMAGE_X_IC_DBL(point->x, extent->minx, invcellsize);
23,371✔
1883
  point->y = MS_MAP2IMAGE_Y_IC_DBL(point->y, extent->maxy, invcellsize);
23,371✔
1884
}
1885

1886
/*
1887
** Helper functions supplied as part of bug #2868 solution. Consider moving
1888
*these to
1889
** mapprimitive.c for more general use.
1890
*/
1891

1892
/* vector difference */
1893
static pointObj point_diff(const pointObj a, const pointObj b) {
1894
  pointObj retv;
1895
  retv.x = a.x - b.x;
24✔
1896
  retv.y = a.y - b.y;
24✔
1897
  retv.z = a.z - b.z;
24✔
1898
  retv.m = a.m - b.m;
24✔
1899
  return retv;
24✔
1900
}
1901

1902
/* vector sum */
1903
static pointObj point_sum(const pointObj a, const pointObj b) {
1904
  pointObj retv;
1905
  retv.x = a.x + b.x;
28✔
1906
  retv.y = a.y + b.y;
28✔
1907
  retv.z = a.z + b.z;
28✔
1908
  retv.m = a.m + b.m;
28✔
1909
  return retv;
24✔
1910
}
1911

1912
/* vector multiply */
1913
static pointObj point_mul(const pointObj a, double b) {
1914
  pointObj retv;
1915
  retv.x = a.x * b;
28✔
1916
  retv.y = a.y * b;
28✔
1917
  retv.z = a.z * b;
28✔
1918
  retv.m = a.m * b;
4✔
1919
  return retv;
1920
}
1921

1922
/* vector ??? */
1923
static double point_abs2(const pointObj a) {
1924
  return a.x * a.x + a.y * a.y + a.z * a.z + a.m * a.m;
24✔
1925
}
1926

1927
/* vector normal */
1928
static pointObj point_norm(const pointObj a) {
24✔
1929
  double lenmul;
1930
  pointObj retv;
1931
  int norm_vector;
1932

1933
  norm_vector = a.x == 0 && a.y == 0;
24✔
1934
  norm_vector = norm_vector && a.z == 0 && a.m == 0;
×
1935

1936
  if (norm_vector)
1937
    return a;
×
1938

1939
  lenmul =
24✔
1940
      1.0 / sqrt(point_abs2(a)); /* this seems to be the costly operation */
24✔
1941

1942
  retv.x = a.x * lenmul;
24✔
1943
  retv.y = a.y * lenmul;
24✔
1944
  retv.z = a.z * lenmul;
24✔
1945
  retv.m = a.m * lenmul;
24✔
1946

1947
  return retv;
24✔
1948
}
1949

1950
/* rotate a vector 90 degrees */
1951
static pointObj point_rotz90(const pointObj a) {
1952
  double nx = -1.0 * a.y, ny = a.x;
24✔
1953
  pointObj retv = a;
1954
  retv.x = nx;
1955
  retv.y = ny;
1956
  return retv;
1957
}
1958

1959
/* vector cross product (warning: z and m dimensions are ignored!) */
1960
static double point_cross(const pointObj a, const pointObj b) {
1961
  return a.x * b.y - a.y * b.x;
20✔
1962
}
1963

1964
shapeObj *msOffsetCurve(shapeObj *p, double offset) {
79✔
1965
  shapeObj *ret;
1966
  int i, j, first, idx, ok = 0;
1967
#if defined USE_GEOS && (GEOS_VERSION_MAJOR > 3 ||                             \
1968
                         (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 3))
1969
  ret = msGEOSOffsetCurve(p, offset);
79✔
1970
  /* GEOS curve offsetting can fail sometimes, we continue with our own
1971
   implementation if that is the case.*/
1972
  if (ret)
79✔
1973
    return ret;
1974
  /* clear error raised by geos in this case */
1975
  msResetErrorList();
4✔
1976
#endif
1977
  /*
1978
  ** For offset corner point calculation 1/sin() is used
1979
  ** to avoid 1/0 division (and long spikes) we define a
1980
  ** limit for sin().
1981
  */
1982
#define CURVE_SIN_LIMIT 0.3
1983
  ret = (shapeObj *)msSmallMalloc(sizeof(shapeObj));
4✔
1984
  msInitShape(ret);
4✔
1985
  ret->numlines = p->numlines;
4✔
1986
  ret->line = (lineObj *)msSmallMalloc(sizeof(lineObj) * ret->numlines);
4✔
1987
  for (i = 0; i < ret->numlines; i++) {
12✔
1988
    ret->line[i].numpoints = p->line[i].numpoints;
8✔
1989
    ret->line[i].point =
8✔
1990
        (pointObj *)msSmallMalloc(sizeof(pointObj) * ret->line[i].numpoints);
8✔
1991
  }
1992
  for (i = 0; i < p->numlines; i++) {
12✔
1993
    pointObj old_diffdir, old_offdir;
1994
    if (p->line[i].numpoints < 2) {
8✔
1995
      ret->line[i].numpoints = 0;
4✔
1996
      continue; /* skip degenerate points */
1997
    }
1998
    ok = 1;
1999
    /* initialize old_offdir and old_diffdir, as gcc isn't smart enough to see
2000
     * that it is not an error to do so, and prints a warning */
2001
    old_offdir.x = old_offdir.y = old_diffdir.x = old_diffdir.y = 0;
2002

2003
    idx = 0;
2004
    first = 1;
2005

2006
    /* saved metrics of the last processed point */
2007
    pointObj old_pt = p->line[i].point[0];
4✔
2008
    for (j = 1; j < p->line[i].numpoints; j++) {
28✔
2009
      const pointObj pt = p->line[i].point[j]; /* place of the point */
24✔
2010
      const pointObj diffdir =
2011
          point_norm(point_diff(pt, old_pt)); /* direction of the line */
24✔
2012
      const pointObj offdir =
2013
          point_rotz90(diffdir); /* direction where the distance between the
2014
                                    line and the offset is measured */
2015
      pointObj offpt; /* this will be the corner point of the offset line */
2016

2017
      /* offset line points computation */
2018
      if (first == 1) { /* first point */
24✔
2019
        first = 0;
2020
        offpt = point_sum(old_pt, point_mul(offdir, offset));
2021
      } else { /* middle points */
2022
        /* curve is the angle of the last and the current line's direction
2023
         * (supplementary angle of the shape's inner angle) */
2024
        double sin_curve = point_cross(diffdir, old_diffdir);
2025
        double cos_curve = point_cross(old_offdir, diffdir);
2026
        if ((-1.0) * CURVE_SIN_LIMIT < sin_curve &&
20✔
2027
            sin_curve < CURVE_SIN_LIMIT) {
2028
          /* do not calculate 1/sin, instead use a corner point approximation:
2029
           * average of the last and current offset direction and length */
2030

2031
          /*
2032
          ** TODO: fair for obtuse inner angles, however, positive and negative
2033
          ** acute inner angles would need special handling - similar to LINECAP
2034
          ** to avoid drawing of long spikes
2035
          */
2036
          offpt = point_sum(
×
2037
              old_pt, point_mul(point_sum(offdir, old_offdir), 0.5 * offset));
2038
        } else {
2039
          double base_shift = -1.0 * (1.0 + cos_curve) / sin_curve;
20✔
2040
          offpt = point_sum(
2041
              old_pt,
2042
              point_mul(point_sum(point_mul(diffdir, base_shift), offdir),
2043
                        offset));
2044
        }
2045
      }
2046
      ret->line[i].point[idx] = offpt;
24✔
2047
      idx++;
24✔
2048
      old_pt = pt;
2049
      old_diffdir = diffdir;
2050
      old_offdir = offdir;
2051
    }
2052

2053
    /* last point */
2054
    if (first == 0) {
4✔
2055
      pointObj offpt = point_sum(old_pt, point_mul(old_offdir, offset));
2056
      ret->line[i].point[idx] = offpt;
4✔
2057
      idx++;
4✔
2058
    }
2059

2060
    if (idx != p->line[i].numpoints) {
4✔
2061
      /* printf("shouldn't happen :(\n"); */
2062
      ret->line[i].numpoints = idx;
×
2063
      ret->line =
×
2064
          msSmallRealloc(ret->line, ret->line[i].numpoints * sizeof(pointObj));
×
2065
    }
2066
  }
2067
  if (!ok)
4✔
2068
    ret->numlines = 0; /* all lines where degenerate */
×
2069
  return ret;
2070
}
2071

2072
shapeObj *msOffsetPolyline(shapeObj *p, double offsetx, double offsety) {
208✔
2073
  int i, j;
2074
  shapeObj *ret;
2075
  if (offsety == MS_STYLE_SINGLE_SIDED_OFFSET) { /* complex calculations */
208✔
2076
    return msOffsetCurve(p, offsetx);
55✔
2077
  } else if (offsety == MS_STYLE_DOUBLE_SIDED_OFFSET) {
153✔
2078
    shapeObj *tmp1;
2079
    ret = msOffsetCurve(p, offsetx / 2.0);
12✔
2080
    tmp1 = msOffsetCurve(p, -offsetx / 2.0);
12✔
2081
    for (i = 0; i < tmp1->numlines; i++) {
24✔
2082
      msAddLineDirectly(ret, tmp1->line + i);
12✔
2083
    }
2084
    msFreeShape(tmp1);
12✔
2085
    free(tmp1);
12✔
2086
    return ret;
12✔
2087
  }
2088

2089
  ret = (shapeObj *)msSmallMalloc(sizeof(shapeObj));
141✔
2090
  msInitShape(ret);
141✔
2091
  ret->numlines = p->numlines;
141✔
2092
  ret->line = (lineObj *)msSmallMalloc(sizeof(lineObj) * ret->numlines);
141✔
2093
  for (i = 0; i < ret->numlines; i++) {
290✔
2094
    ret->line[i].numpoints = p->line[i].numpoints;
149✔
2095
    ret->line[i].point =
149✔
2096
        (pointObj *)msSmallMalloc(sizeof(pointObj) * ret->line[i].numpoints);
149✔
2097
  }
2098

2099
  for (i = 0; i < p->numlines; i++) {
290✔
2100
    for (j = 0; j < p->line[i].numpoints; j++) {
1,656✔
2101
      ret->line[i].point[j].x = p->line[i].point[j].x + offsetx;
1,507✔
2102
      ret->line[i].point[j].y = p->line[i].point[j].y + offsety;
1,507✔
2103
    }
2104
  }
2105

2106
  return ret;
2107
}
2108

2109
/*
2110
-------------------------------------------------------------------------------
2111
 msSetup()
2112

2113
 Contributed by Jerry Pisk in bug 1203.  Heads off potential race condition
2114
 in initializing GD font cache with multiple threads.  Should be called from
2115
 mapscript module initialization code.
2116
-------------------------------------------------------------------------------
2117
*/
2118

2119
int msSetup() {
2,795✔
2120
#ifdef _WIN32
2121
  const char *maxfiles = CPLGetConfigOption("MS_MAX_OPEN_FILES", NULL);
2122
  if (maxfiles) {
2123
    int res = _getmaxstdio();
2124
    if (res < 2048) {
2125
      res = _setmaxstdio(atoi(maxfiles));
2126
      assert(res != -1);
2127
    }
2128
  }
2129
#endif
2130

2131
#ifdef USE_THREAD
2132
  msThreadInit();
2,795✔
2133
#endif
2134

2135
  if (gdal_destroyed) {
2,795✔
NEW
2136
    msAcquireLock(TLOCK_GDAL);
×
NEW
2137
    GDALAllRegister();
×
NEW
2138
    gdal_destroyed = 0;
×
NEW
2139
    msReleaseLock(TLOCK_GDAL);
×
2140
  }
2141

2142
  /* Use PROJ_DATA/PROJ_LIB env vars if set */
2143
  msProjDataInitFromEnv();
2,795✔
2144

2145
  /* Use MS_ERRORFILE and MS_DEBUGLEVEL env vars if set */
2146
  if (msDebugInitFromEnv() != MS_SUCCESS)
2,795✔
2147
    return MS_FAILURE;
2148

2149
#ifdef USE_GEOS
2150
  msGEOSSetup();
2,795✔
2151
#endif
2152

2153
#ifdef USE_RSVG
2154
#if !GLIB_CHECK_VERSION(2, 35, 0)
2155
  g_type_init();
2156
#endif
2157
#endif
2158

2159
  msFontCacheSetup();
2,795✔
2160

2161
  return MS_SUCCESS;
2,795✔
2162
}
2163

2164
/* This is intended to be a function to cleanup anything that "hangs around"
2165
   when all maps are destroyed, like Registered GDAL drivers, and so forth. */
2166
#ifndef NDEBUG
2167
#if defined(USE_LIBXML2)
2168
#include "maplibxml2.h"
2169
#endif
2170
#endif
2171
void msCleanup() {
2,784✔
2172
  msForceTmpFileBase(NULL);
2,784✔
2173
  msConnPoolFinalCleanup();
2,784✔
2174
  /* Lexer string parsing variable */
2175
  if (msyystring_buffer != NULL) {
2,784✔
2176
    msFree(msyystring_buffer);
2,780✔
2177
    msyystring_buffer = NULL;
2,780✔
2178
  }
2179
  msyylex_destroy();
2,784✔
2180

2181
  msOGRCleanup();
2,784✔
2182
  msGDALCleanup();
2,784✔
2183

2184
  /* Release both GDAL and OGR resources */
2185
  msAcquireLock(TLOCK_GDAL);
2,784✔
2186
  /* Cleanup some GDAL global resources in particular */
2187
  GDALDestroy();
2,784✔
2188
  gdal_destroyed = 1;
2,784✔
2189
  msReleaseLock(TLOCK_GDAL);
2,784✔
2190

2191
  msSetPROJ_DATA(NULL, NULL);
2,784✔
2192
  msProjectionContextPoolCleanup();
2,784✔
2193

2194
#if defined(USE_CURL)
2195
  msHTTPCleanup();
2,784✔
2196
#endif
2197

2198
#ifdef USE_GEOS
2199
  msGEOSCleanup();
2,784✔
2200
#endif
2201

2202
/* make valgrind happy on debug code */
2203
#ifndef NDEBUG
2204
#ifdef USE_CAIRO
2205
  msCairoCleanup();
2206
#endif
2207
#if defined(USE_LIBXML2)
2208
  xmlCleanupParser();
2209
#endif
2210
#endif
2211

2212
  msFontCacheCleanup();
2,784✔
2213

2214
  msTimeCleanup();
2,784✔
2215

2216
  msIO_Cleanup();
2,784✔
2217

2218
  msResetErrorList();
2,784✔
2219

2220
  /* Close/cleanup log/debug output. Keep this at the very end. */
2221
  msDebugCleanup();
2,784✔
2222

2223
  /* Clean up the vtable factory */
2224
  msPluginFreeVirtualTableFactory();
2,784✔
2225
}
2,784✔
2226

2227
/************************************************************************/
2228
/*                            msAlphaBlend()                            */
2229
/*                                                                      */
2230
/*      Function to overlay/blend an RGBA value into an existing        */
2231
/*      RGBA value using the Porter-Duff "over" operator.               */
2232
/*      Primarily intended for use with rasterBufferObj                 */
2233
/*      raster rendering.  The "src" is the overlay value, and "dst"    */
2234
/*      is the existing value being overlaid. dst is expected to be     */
2235
/*      premultiplied, but the source should not be.                    */
2236
/*                                                                      */
2237
/*      NOTE: alpha_dst may be NULL.                                    */
2238
/************************************************************************/
2239

2240
void msAlphaBlend(unsigned char red_src, unsigned char green_src,
314,744✔
2241
                  unsigned char blue_src, unsigned char alpha_src,
2242
                  unsigned char *red_dst, unsigned char *green_dst,
2243
                  unsigned char *blue_dst, unsigned char *alpha_dst) {
2244
  /* -------------------------------------------------------------------- */
2245
  /*      Simple cases we want to handle fast.                            */
2246
  /* -------------------------------------------------------------------- */
2247
  if (alpha_src == 0)
314,744✔
2248
    return;
2249

2250
  if (alpha_src == 255) {
314,744✔
2251
    *red_dst = red_src;
151,548✔
2252
    *green_dst = green_src;
151,548✔
2253
    *blue_dst = blue_src;
151,548✔
2254
    if (alpha_dst)
151,548✔
2255
      *alpha_dst = 255;
216✔
2256
    return;
151,548✔
2257
  }
2258

2259
  /* -------------------------------------------------------------------- */
2260
  /*      Premultiple alpha for source values now.                        */
2261
  /* -------------------------------------------------------------------- */
2262
  red_src = red_src * alpha_src / 255;
163,196✔
2263
  green_src = green_src * alpha_src / 255;
163,196✔
2264
  blue_src = blue_src * alpha_src / 255;
163,196✔
2265

2266
  /* -------------------------------------------------------------------- */
2267
  /*      Another pretty fast case if there is nothing in the             */
2268
  /*      destination to mix with.                                        */
2269
  /* -------------------------------------------------------------------- */
2270
  if (alpha_dst && *alpha_dst == 0) {
163,196✔
2271
    *red_dst = red_src;
96,608✔
2272
    *green_dst = green_src;
96,608✔
2273
    *blue_dst = blue_src;
96,608✔
2274
    *alpha_dst = alpha_src;
96,608✔
2275
    return;
96,608✔
2276
  }
2277

2278
  /* -------------------------------------------------------------------- */
2279
  /*      Cases with actual blending.                                     */
2280
  /* -------------------------------------------------------------------- */
2281
  if (!alpha_dst || *alpha_dst == 255) {
19,845✔
2282
    int weight_dst = 256 - alpha_src;
62,619✔
2283

2284
    *red_dst = (256 * red_src + *red_dst * weight_dst) >> 8;
62,619✔
2285
    *green_dst = (256 * green_src + *green_dst * weight_dst) >> 8;
62,619✔
2286
    *blue_dst = (256 * blue_src + *blue_dst * weight_dst) >> 8;
62,619✔
2287
  } else {
2288
    int weight_dst = (256 - alpha_src);
3,969✔
2289

2290
    *red_dst = (256 * red_src + *red_dst * weight_dst) >> 8;
3,969✔
2291
    *green_dst = (256 * green_src + *green_dst * weight_dst) >> 8;
3,969✔
2292
    *blue_dst = (256 * blue_src + *blue_dst * weight_dst) >> 8;
3,969✔
2293

2294
    *alpha_dst = (256 * alpha_src + *alpha_dst * weight_dst) >> 8;
3,969✔
2295
  }
2296
}
2297

2298
/************************************************************************/
2299
/*                           msAlphaBlendPM()                           */
2300
/*                                                                      */
2301
/*      Same as msAlphaBlend() except that the source RGBA is           */
2302
/*      assumed to already be premultiplied.                            */
2303
/************************************************************************/
2304

2305
void msAlphaBlendPM(unsigned char red_src, unsigned char green_src,
75,020✔
2306
                    unsigned char blue_src, unsigned char alpha_src,
2307
                    unsigned char *red_dst, unsigned char *green_dst,
2308
                    unsigned char *blue_dst, unsigned char *alpha_dst) {
2309
  /* -------------------------------------------------------------------- */
2310
  /*      Simple cases we want to handle fast.                            */
2311
  /* -------------------------------------------------------------------- */
2312
  if (alpha_src == 0)
75,020✔
2313
    return;
2314

2315
  if (alpha_src == 255) {
74,990✔
2316
    *red_dst = red_src;
19,981✔
2317
    *green_dst = green_src;
19,981✔
2318
    *blue_dst = blue_src;
19,981✔
2319
    if (alpha_dst)
19,981✔
2320
      *alpha_dst = 255;
19,981✔
2321
    return;
19,981✔
2322
  }
2323

2324
  /* -------------------------------------------------------------------- */
2325
  /*      Another pretty fast case if there is nothing in the             */
2326
  /*      destination to mix with.                                        */
2327
  /* -------------------------------------------------------------------- */
2328
  if (alpha_dst && *alpha_dst == 0) {
55,009✔
2329
    *red_dst = red_src;
15,214✔
2330
    *green_dst = green_src;
15,214✔
2331
    *blue_dst = blue_src;
15,214✔
2332
    *alpha_dst = alpha_src;
15,214✔
2333
    return;
15,214✔
2334
  }
2335

2336
  /* -------------------------------------------------------------------- */
2337
  /*      Cases with actual blending.                                     */
2338
  /* -------------------------------------------------------------------- */
2339
  if (!alpha_dst || *alpha_dst == 255) {
×
2340
    int weight_dst = 255 - alpha_src;
39,795✔
2341

2342
    *red_dst = (alpha_src * red_src + *red_dst * weight_dst) >> 8;
39,795✔
2343
    *green_dst = (alpha_src * green_src + *green_dst * weight_dst) >> 8;
39,795✔
2344
    *blue_dst = (alpha_src * blue_src + *blue_dst * weight_dst) >> 8;
39,795✔
2345
  } else {
2346
    int weight_dst = (255 - alpha_src);
×
2347

2348
    *red_dst = (alpha_src * red_src + *red_dst * weight_dst) >> 8;
×
2349
    *green_dst = (alpha_src * green_src + *green_dst * weight_dst) >> 8;
×
2350
    *blue_dst = (alpha_src * blue_src + *blue_dst * weight_dst) >> 8;
×
2351

2352
    *alpha_dst = (255 * alpha_src + *alpha_dst * weight_dst) >> 8;
×
2353
  }
2354
}
2355

2356
void msRGBtoHSL(colorObj *rgb, double *h, double *s, double *l) {
2,586✔
2357
  double r = rgb->red / 255.0, g = rgb->green / 255.0, b = rgb->blue / 255.0;
2,586✔
2358
  double maxv = MS_MAX(MS_MAX(r, g), b), minv = MS_MIN(MS_MIN(r, g), b);
7,758✔
2359
  double d = maxv - minv;
2,586✔
2360

2361
  *h = 0, *s = 0;
2,586✔
2362
  *l = (maxv + minv) / 2;
2,586✔
2363

2364
  if (maxv != minv) {
2,586✔
2365
    *s = *l > 0.5 ? d / (2 - maxv - minv) : d / (maxv + minv);
2,586✔
2366
    if (maxv == r) {
2,586✔
2367
      *h = (g - b) / d + (g < b ? 6 : 0);
2,226✔
2368
    } else if (maxv == g) {
1,473✔
2369
      *h = (b - r) / d + 2;
283✔
2370
    } else if (maxv == b) {
1,190✔
2371
      *h = (r - g) / d + 4;
1,190✔
2372
    }
2373
    *h /= 6;
2,586✔
2374
  }
2375
}
2,586✔
2376

2377
static double hue_to_rgb(double p, double q, double t) {
3,879✔
2378
  if (t < 0)
3,879✔
2379
    t += 1;
669✔
2380
  if (t > 1)
3,879✔
2381
    t -= 1;
×
2382
  if (t < 0.1666666666666666)
3,879✔
2383
    return p + (q - p) * 6 * t;
585✔
2384
  if (t < 0.5)
3,294✔
2385
    return q;
2386
  if (t < 0.6666666666666666)
2,001✔
2387
    return p + (q - p) * (0.666666666666 - t) * 6;
584✔
2388
  return p;
2389
}
2390

2391
void msHSLtoRGB(double h, double s, double l, colorObj *rgb) {
1,293✔
2392
  double r, g, b;
2393

2394
  if (s == 0) {
1,293✔
2395
    r = g = b = l;
2396
  } else {
2397

2398
    double q = l < 0.5 ? l * (1 + s) : l + s - l * s;
1,293✔
2399
    double p = 2 * l - q;
1,293✔
2400
    r = hue_to_rgb(p, q, h + 0.33333333333333333);
1,293✔
2401
    g = hue_to_rgb(p, q, h);
1,293✔
2402
    b = hue_to_rgb(p, q, h - 0.33333333333333333);
1,293✔
2403
  }
2404
  rgb->red = r * 255;
1,293✔
2405
  rgb->green = g * 255;
1,293✔
2406
  rgb->blue = b * 255;
1,293✔
2407
}
1,293✔
2408

2409
/*
2410
 RFC 24: check if the parent pointer is NULL and raise an error otherwise
2411
*/
2412
int msCheckParentPointer(void *p, const char *objname) {
4,599✔
2413
  if (p == NULL) {
4,599✔
2414
    if (objname != NULL) {
×
2415
      msSetError(MS_NULLPARENTERR, "The %s parent object is null",
×
2416
                 "msCheckParentPointer()", objname);
2417
    } else {
2418
      msSetError(MS_NULLPARENTERR, "The parent object is null",
×
2419
                 "msCheckParentPointer()");
2420
    }
2421
    return MS_FAILURE;
×
2422
  }
2423
  return MS_SUCCESS;
2424
}
2425

2426
void msBufferInit(bufferObj *buffer) {
207✔
2427
  buffer->data = NULL;
207✔
2428
  buffer->size = 0;
207✔
2429
  buffer->available = 0;
207✔
2430
  buffer->_next_allocation_size = MS_DEFAULT_BUFFER_ALLOC;
207✔
2431
}
207✔
2432

2433
void msBufferResize(bufferObj *buffer, size_t target_size) {
240✔
2434
  while (buffer->available <= target_size) {
493✔
2435
    buffer->data = msSmallRealloc(
506✔
2436
        buffer->data, buffer->available + buffer->_next_allocation_size);
253✔
2437
    buffer->available += buffer->_next_allocation_size;
253✔
2438
    buffer->_next_allocation_size *= 2;
253✔
2439
  }
2440
}
240✔
2441

2442
void msBufferAppend(bufferObj *buffer, void *data, size_t length) {
13,190✔
2443
  if (buffer->available < buffer->size + length) {
13,190✔
2444
    msBufferResize(buffer, buffer->size + length);
238✔
2445
  }
2446
  memcpy(&(buffer->data[buffer->size]), data, length);
13,190✔
2447
  buffer->size += length;
13,190✔
2448
}
13,190✔
2449

2450
void msBufferFree(bufferObj *buffer) {
192✔
2451
  if (buffer->available > 0)
192✔
2452
    free(buffer->data);
192✔
2453
}
192✔
2454

2455
void msFreeRasterBuffer(rasterBufferObj *b) {
347✔
2456
  switch (b->type) {
347✔
2457
  case MS_BUFFER_BYTE_RGBA:
347✔
2458
    msFree(b->data.rgba.pixels);
347✔
2459
    b->data.rgba.pixels = NULL;
347✔
2460
    break;
347✔
2461
  case MS_BUFFER_BYTE_PALETTE:
×
2462
    msFree(b->data.palette.pixels);
×
2463
    msFree(b->data.palette.palette);
×
2464
    b->data.palette.pixels = NULL;
×
2465
    b->data.palette.palette = NULL;
×
2466
    break;
×
2467
  }
2468
}
347✔
2469

2470
/*
2471
** Issue #3043: Layer extent comparison short circuit.
2472
**
2473
** msExtentsOverlap()
2474
**
2475
** Returns MS_TRUE if map extent and layer extent overlap,
2476
** MS_FALSE if they are disjoint, and MS_UNKNOWN if there is
2477
** not enough info to calculate a deterministic answer.
2478
**
2479
*/
2480
int msExtentsOverlap(mapObj *map, layerObj *layer) {
4,702✔
2481
  rectObj map_extent;
2482
  rectObj layer_extent;
2483

2484
  /* No extent info? Nothing we can do, return MS_UNKNOWN. */
2485
  if ((map->extent.minx == -1) && (map->extent.miny == -1) &&
4,702✔
2486
      (map->extent.maxx == -1) && (map->extent.maxy == -1))
2✔
2487
    return MS_UNKNOWN;
2488
  if ((layer->extent.minx == -1) && (layer->extent.miny == -1) &&
4,702✔
2489
      (layer->extent.maxx == -1) && (layer->extent.maxy == -1))
4,588✔
2490
    return MS_UNKNOWN;
2491

2492
  /* No map projection? Let someone else sort this out. */
2493
  if (!(map->projection.numargs > 0))
114✔
2494
    return MS_UNKNOWN;
2495

2496
  /* No layer projection? Perform naive comparison, because they are
2497
  ** in the same projection. */
2498
  if (!(layer->projection.numargs > 0))
101✔
2499
    return msRectOverlap(&(map->extent), &(layer->extent));
×
2500

2501
  /* In the case where map and layer projections are identical, and the */
2502
  /* bounding boxes don't cross the dateline, do simple rectangle comparison */
2503
  if (map->extent.minx < map->extent.maxx &&
101✔
2504
      layer->extent.minx < layer->extent.maxx &&
202✔
2505
      !msProjectionsDiffer(&(map->projection), &(layer->projection))) {
101✔
2506
    return msRectOverlap(&(map->extent), &(layer->extent));
13✔
2507
  }
2508

2509
  /* We need to transform our rectangles for comparison,
2510
  ** so we will work with copies and leave the originals intact. */
2511
  MS_COPYRECT(&map_extent, &(map->extent));
88✔
2512
  MS_COPYRECT(&layer_extent, &(layer->extent));
88✔
2513

2514
  /* Transform map extents into geographics for comparison. */
2515
  if (msProjectRect(&(map->projection), &(map->latlon), &map_extent))
88✔
2516
    return MS_UNKNOWN;
2517

2518
  /* Transform layer extents into geographics for comparison. */
2519
  if (msProjectRect(&(layer->projection), &(map->latlon), &layer_extent))
88✔
2520
    return MS_UNKNOWN;
2521

2522
  /* Simple case? Return simple answer. */
2523
  if (map_extent.minx < map_extent.maxx &&
88✔
2524
      layer_extent.minx < layer_extent.maxx)
88✔
2525
    return msRectOverlap(&(map_extent), &(layer_extent));
88✔
2526

2527
  /* Uh oh, one of the rects crosses the dateline!
2528
  ** Let someone else handle it. */
2529
  return MS_UNKNOWN;
2530
}
2531

2532
/************************************************************************/
2533
/*                             msSmallMalloc()                          */
2534
/************************************************************************/
2535

2536
/* Safe version of malloc(). This function is taken from gdal/cpl. */
2537

2538
void *msSmallMalloc(size_t nSize) {
432,438✔
2539
  void *pReturn;
2540

2541
  if (MS_UNLIKELY(nSize == 0))
432,438✔
2542
    return NULL;
2543

2544
  pReturn = malloc(nSize);
432,192✔
2545
  if (MS_UNLIKELY(pReturn == NULL)) {
432,192✔
2546
    msIO_fprintf(stderr,
×
2547
                 "msSmallMalloc(): Out of memory allocating %ld bytes.\n",
2548
                 (long)nSize);
2549
    exit(1);
×
2550
  }
2551

2552
  return pReturn;
2553
}
2554

2555
/************************************************************************/
2556
/*                             msSmallRealloc()                         */
2557
/************************************************************************/
2558

2559
/* Safe version of realloc(). This function is taken from gdal/cpl. */
2560

2561
void *msSmallRealloc(void *pData, size_t nNewSize) {
24,421✔
2562
  void *pReturn;
2563

2564
  if (MS_UNLIKELY(nNewSize == 0))
24,421✔
2565
    return NULL;
2566

2567
  pReturn = realloc(pData, nNewSize);
24,421✔
2568

2569
  if (MS_UNLIKELY(pReturn == NULL)) {
24,421✔
2570
    msIO_fprintf(stderr,
×
2571
                 "msSmallRealloc(): Out of memory allocating %ld bytes.\n",
2572
                 (long)nNewSize);
2573
    exit(1);
×
2574
  }
2575

2576
  return pReturn;
2577
}
2578

2579
/************************************************************************/
2580
/*                             msSmallCalloc()                         */
2581
/************************************************************************/
2582

2583
/* Safe version of calloc(). This function is taken from gdal/cpl. */
2584

2585
void *msSmallCalloc(size_t nCount, size_t nSize) {
27,435✔
2586
  void *pReturn;
2587

2588
  if (MS_UNLIKELY(nSize * nCount == 0))
27,435✔
2589
    return NULL;
2590

2591
  pReturn = calloc(nCount, nSize);
25,119✔
2592
  if (MS_UNLIKELY(pReturn == NULL)) {
25,119✔
2593
    msIO_fprintf(stderr,
×
2594
                 "msSmallCalloc(): Out of memory allocating %ld bytes.\n",
2595
                 (long)(nCount * nSize));
2596
    exit(1);
×
2597
  }
2598

2599
  return pReturn;
2600
}
2601

2602
/*
2603
** msBuildOnlineResource()
2604
**
2605
** Try to build the online resource (mapserv URL) for this service.
2606
** "http://$(SERVER_NAME):$(SERVER_PORT)$(SCRIPT_NAME)$(PATH_INFO)?"
2607
** (+append the map=... param if it was explicitly passed in QUERY_STRING)
2608
**
2609
** Returns a newly allocated string that should be freed by the caller or
2610
** NULL in case of error.
2611
*/
2612
char *msBuildOnlineResource(const mapObj *map, const cgiRequestObj *req) {
61✔
2613
  (void)map; // unused parameter
2614

2615
  const char *pathinfo = getenv("PATH_INFO");
61✔
2616
  if (!pathinfo) {
61✔
2617
    pathinfo = "";
2618
  }
2619

2620
  /* If map=.. was explicitly set then we'll include it in onlineresource
2621
   */
2622

2623
  const char *mapparam = NULL;
2624
  if (req->type == MS_GET_REQUEST) {
61✔
2625
    int i;
2626
    for (i = 0; i < req->NumParams; i++) {
125✔
2627
      if (strcasecmp(req->ParamNames[i], "map") == 0) {
65✔
2628
        mapparam = req->ParamValues[i];
1✔
2629
        break;
1✔
2630
      }
2631
    }
2632
  }
2633

2634
  // check if MS_ONLINERESOURCE has been set in the
2635
  // CONFIG file
2636
  const char *config_online_resource =
2637
      CPLGetConfigOption("MS_ONLINERESOURCE", NULL);
61✔
2638

2639
  if (config_online_resource) {
61✔
2640

2641
    // append the PATH_INFO to the MS_ONLINERESOURCE value and return it
2642
    const char *path = pathinfo;
2643
    size_t config_len = strlen(config_online_resource);
18✔
2644

2645
    /* avoid double slash */
2646
    if (config_len > 0 && config_online_resource[config_len - 1] == '/' &&
18✔
2647
        pathinfo[0] == '/') {
12✔
2648
      path = pathinfo + 1;
11✔
2649
    }
2650

2651
    size_t path_len = strlen(path);
18✔
2652
    size_t map_len = 0;
2653

2654
    if (mapparam) {
18✔
2655
      map_len = strlen(mapparam) + 5; /* "map=" + "&" */
1✔
2656
    }
2657

2658
    // add 2 to buffer_size for '?' and '\0'
2659
    size_t buffer_size = config_len + path_len + map_len + 2;
18✔
2660

2661
    char *result = (char *)msSmallMalloc(buffer_size);
18✔
2662
    if (!result) {
18✔
2663
      msSetError(MS_MEMERR, "Memory allocation failed",
×
2664
                 "msBuildOnlineResource()");
2665
      return NULL;
×
2666
    }
2667

2668
    if (mapparam) {
18✔
2669
      snprintf(result, buffer_size, "%s%s?map=%s&", config_online_resource,
2670
               path, mapparam);
2671
    } else {
2672
      snprintf(result, buffer_size, "%s%s?", config_online_resource, path);
2673
    }
2674

2675
    return result;
18✔
2676
  }
2677

2678
  char *online_resource = NULL;
2679
  const char *value, *protocol = "http";
2680
  char **hostname_array = NULL;
2681
  int mapparam_len = 0, hostname_array_len = 0;
43✔
2682
  const char *hostname = getenv("HTTP_X_FORWARDED_HOST");
43✔
2683
  if (!hostname)
43✔
2684
    hostname = getenv("SERVER_NAME");
43✔
2685
  else {
2686
    if (strchr(hostname, ',')) {
×
2687
      hostname_array = msStringSplit(hostname, ',', &hostname_array_len);
×
2688
      hostname = hostname_array[0];
×
2689
    }
2690
  }
2691

2692
  const char *port = getenv("HTTP_X_FORWARDED_PORT");
43✔
2693
  if (!port)
43✔
2694
    port = getenv("SERVER_PORT");
43✔
2695

2696
  const char *script = getenv("SCRIPT_NAME");
43✔
2697

2698
  /* HTTPS is set by Apache to "on" in an HTTPS server ... if not set */
2699
  /* then check SERVER_PORT: 443 is the default https port. */
2700
  if (((value = getenv("HTTPS")) && strcasecmp(value, "on") == 0) ||
86✔
2701
      ((value = getenv("SERVER_PORT")) && atoi(value) == 443)) {
43✔
2702
    protocol = "https";
2703
  }
2704
  if ((value = getenv("HTTP_X_FORWARDED_PROTO"))) {
43✔
2705
    protocol = value;
2706
  }
2707

2708
  if (hostname && port && script) {
43✔
2709
    size_t buffer_size;
2710
    buffer_size = strlen(hostname) + strlen(port) + strlen(pathinfo) +
×
2711
                  strlen(script) + mapparam_len +
×
2712
                  11; /* 11 comes from
2713
                         https://[host]:[port][scriptname][pathinfo]?[map]\0,
2714
                         i.e. "https://:?\0" */
2715
    online_resource = (char *)msSmallMalloc(buffer_size);
×
2716
    if ((atoi(port) == 80 && strcmp(protocol, "http") == 0) ||
×
2717
        (atoi(port) == 443 && strcmp(protocol, "https") == 0))
×
2718
      snprintf(online_resource, buffer_size, "%s://%s%s%s?", protocol, hostname,
2719
               script, pathinfo);
2720
    else
2721
      snprintf(online_resource, buffer_size, "%s://%s:%s%s%s?", protocol,
2722
               hostname, port, script, pathinfo);
2723

2724
    if (mapparam) {
×
2725
      int baselen;
2726
      baselen = strlen(online_resource);
×
2727
      snprintf(online_resource + baselen, buffer_size - baselen, "map=%s&",
×
2728
               mapparam);
2729
    }
2730
  } else {
2731
    msSetError(MS_CGIERR, "Impossible to establish server URL.",
43✔
2732
               "msBuildOnlineResource()");
2733
    return NULL;
43✔
2734
  }
2735
  if (hostname_array) {
×
2736
    msFreeCharArray(hostname_array, hostname_array_len);
×
2737
  }
2738

2739
  return online_resource;
2740
}
2741

2742
/************************************************************************/
2743
/*                             msIntegerInArray()                        */
2744
/************************************************************************/
2745

2746
/* Check if a integer is in a array */
2747
int msIntegerInArray(const int value, int *array, int numelements) {
6,962✔
2748
  int i;
2749
  for (i = 0; i < numelements; ++i) {
28,494✔
2750
    if (value == array[i])
28,418✔
2751
      return MS_TRUE;
2752
  }
2753
  return MS_FALSE;
2754
}
2755

2756
/************************************************************************
2757
 *                            msMapSetProjections                       *
2758
 *                                                                      *
2759
 *   Ensure that all the layers in the map file have a projection       *
2760
 *   by copying the map-level projection to all layers than have no     *
2761
 *   projection.                                                        *
2762
 ************************************************************************/
2763

2764
int msMapSetLayerProjections(mapObj *map) {
278✔
2765

2766
  char *mapProjStr = NULL;
2767
  int i;
2768

2769
  if (map->projection.numargs <= 0) {
278✔
2770
    msSetError(MS_WMSERR,
×
2771
               "Cannot set new SRS on a map that doesn't "
2772
               "have any projection set. Please make sure your mapfile "
2773
               "has a PROJECTION defined at the top level.",
2774
               "msTileSetProjectionst()");
2775
    return (MS_FAILURE);
×
2776
  }
2777

2778
  for (i = 0; i < map->numlayers; i++) {
1,374✔
2779
    layerObj *lp = GET_LAYER(map, i);
1,096✔
2780
    /* This layer is turned on and needs a projection? */
2781
    if (lp->projection.numargs <= 0 && lp->status != MS_OFF &&
1,096✔
2782
        lp->transform == MS_TRUE) {
306✔
2783

2784
      /* Fetch main map projection string only now that we need it */
2785
      if (mapProjStr == NULL)
306✔
2786
        mapProjStr = msGetProjectionString(&(map->projection));
141✔
2787

2788
      /* Set the projection to the map file projection */
2789
      if (msLoadProjectionString(&(lp->projection), mapProjStr) != 0) {
306✔
2790
        msSetError(MS_CGIERR, "Unable to set projection on layer.",
×
2791
                   "msMapSetLayerProjections()");
2792
        return (MS_FAILURE);
×
2793
      }
2794
      lp->project = MS_TRUE;
306✔
2795
      if (lp->connection &&
306✔
2796
          IS_THIRDPARTY_LAYER_CONNECTIONTYPE(lp->connectiontype)) {
43✔
2797
        char **reflayers;
2798
        int numreflayers, j;
2799
        reflayers = msStringSplit(lp->connection, ',', &numreflayers);
1✔
2800
        for (j = 0; j < numreflayers; j++) {
2✔
2801
          int *lidx, nlidx;
2802
          /* first check layers referenced by group name */
2803
          lidx = msGetLayersIndexByGroup(map, reflayers[i], &nlidx);
1✔
2804
          if (lidx) {
1✔
2805
            int k;
2806
            for (k = 0; k < nlidx; k++) {
×
2807
              layerObj *glp = GET_LAYER(map, lidx[k]);
×
2808
              if (glp->projection.numargs <= 0 && glp->transform == MS_TRUE) {
×
2809

2810
                /* Set the projection to the map file projection */
2811
                if (msLoadProjectionString(&(glp->projection), mapProjStr) !=
×
2812
                    0) {
2813
                  msSetError(MS_CGIERR, "Unable to set projection on layer.",
×
2814
                             "msMapSetLayerProjections()");
2815
                  return (MS_FAILURE);
×
2816
                }
2817
                glp->project = MS_TRUE;
×
2818
              }
2819
            }
2820
            free(lidx);
×
2821
          } else {
2822
            /* group name did not match, check by layer name */
2823
            int layer_idx = msGetLayerIndex(map, lp->connection);
1✔
2824
            layerObj *glp = GET_LAYER(map, layer_idx);
1✔
2825
            if (glp->projection.numargs <= 0 && glp->transform == MS_TRUE) {
1✔
2826

2827
              /* Set the projection to the map file projection */
2828
              if (msLoadProjectionString(&(glp->projection), mapProjStr) != 0) {
1✔
2829
                msSetError(MS_CGIERR, "Unable to set projection on layer.",
×
2830
                           "msMapSetLayerProjections()");
2831
                return (MS_FAILURE);
×
2832
              }
2833
              glp->project = MS_TRUE;
1✔
2834
            }
2835
          }
2836
        }
2837
        msFreeCharArray(reflayers, numreflayers);
1✔
2838
      }
2839
    }
2840
  }
2841
  msFree(mapProjStr);
278✔
2842
  return (MS_SUCCESS);
278✔
2843
}
2844

2845
/************************************************************************
2846
 *                    msMapSetLanguageSpecificConnection                *
2847
 *                                                                      *
2848
 *   Override DATA and CONNECTION of each layer with their specific     *
2849
 *  variant for the specified language.                                 *
2850
 ************************************************************************/
2851

2852
void msMapSetLanguageSpecificConnection(mapObj *map,
67✔
2853
                                        const char *validated_language) {
2854
  int i;
2855
  for (i = 0; i < map->numlayers; i++) {
702✔
2856
    layerObj *layer = GET_LAYER(map, i);
635✔
2857
    if (layer->data)
635✔
2858
      layer->data =
524✔
2859
          msCaseReplaceSubstring(layer->data, "%language%", validated_language);
524✔
2860
    if (layer->connection)
635✔
2861
      layer->connection = msCaseReplaceSubstring(
×
2862
          layer->connection, "%language%", validated_language);
2863
  }
2864
}
67✔
2865

2866
/* Generalize a shape based of the tolerance.
2867
   Ref: http://trac.osgeo.org/gdal/ticket/966 */
2868
shapeObj *msGeneralize(shapeObj *shape, double tolerance) {
12✔
2869
  lineObj newLine = {0, NULL};
12✔
2870
  const double sqTolerance = tolerance * tolerance;
12✔
2871

2872
  shapeObj *newShape = (shapeObj *)msSmallMalloc(sizeof(shapeObj));
12✔
2873
  msInitShape(newShape);
12✔
2874
  msCopyShape(shape, newShape);
12✔
2875

2876
  if (shape->numlines < 1)
12✔
2877
    return newShape;
2878

2879
  /* Clean shape */
2880
  for (int i = 0; i < newShape->numlines; i++)
24✔
2881
    free(newShape->line[i].point);
12✔
2882
  newShape->numlines = 0;
12✔
2883
  if (newShape->line)
12✔
2884
    free(newShape->line);
12✔
2885

2886
  msAddLine(newShape, &newLine);
12✔
2887

2888
  if (shape->line[0].numpoints == 0) {
12✔
2889
    return newShape;
2890
  }
2891

2892
  msAddPointToLine(&newShape->line[0], &shape->line[0].point[0]);
12✔
2893
  double dX0 = shape->line[0].point[0].x;
12✔
2894
  double dY0 = shape->line[0].point[0].y;
12✔
2895

2896
  for (int i = 1; i < shape->line[0].numpoints; i++) {
80✔
2897
    double dX1 = shape->line[0].point[i].x;
68✔
2898
    double dY1 = shape->line[0].point[i].y;
68✔
2899

2900
    const double dX = dX1 - dX0;
68✔
2901
    const double dY = dY1 - dY0;
68✔
2902
    const double dSqDist = dX * dX + dY * dY;
68✔
2903
    if (i == shape->line[0].numpoints - 1 || dSqDist >= sqTolerance) {
68✔
2904
      pointObj p;
2905
      p.x = dX1;
44✔
2906
      p.y = dY1;
44✔
2907

2908
      /* Keep this point (always keep the last point) */
2909
      msAddPointToLine(&newShape->line[0], &p);
44✔
2910
      dX0 = dX1;
2911
      dY0 = dY1;
2912
    }
2913
  }
2914

2915
  return newShape;
2916
}
2917

2918
void msSetLayerOpacity(layerObj *layer, int opacity) {
1✔
2919
  if (!layer->compositer) {
1✔
2920
    layer->compositer = msSmallMalloc(sizeof(LayerCompositer));
1✔
2921
    initLayerCompositer(layer->compositer);
1✔
2922
  }
2923
  layer->compositer->opacity = opacity;
1✔
2924
}
1✔
2925

2926
void msReplaceFreeableStr(char **ppszStr, char *pszNewStr) {
27,000✔
2927
  msFree(*ppszStr);
27,000✔
2928
  *ppszStr = pszNewStr;
27,000✔
2929
}
27,000✔
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