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

MapServer / MapServer / 20718230642

05 Jan 2026 02:18PM UTC coverage: 41.754% (+0.03%) from 41.725%
20718230642

push

github

web-flow
WMS: rework building of the layer tree to make it faster, and understandable! (#7410)

* WMS: rework building of the layer tree to make it faster, and understandable!

Avoid quadratic performance in the number of layers.

On a 103 MB .map file with 6130 layers and 3 level of nesting, WMS
GetCapabilities response generation goes from 56 seconds to 5 seconds.

* msRenameLayer(): avoid potential overflow

* mapows.cpp: minimal conversion to C++

* msOWSMakeAllLayersUnique(): avoid quadratic performance in number of layers

* loadMap(): optimize CRS creation when they are all the same

360 of 422 new or added lines in 4 files covered. (85.31%)

76 existing lines in 4 files now uncovered.

62888 of 150616 relevant lines covered (41.75%)

25229.23 hits per line

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

68.37
/src/mapfile.c
1
/******************************************************************************
2
 * $id: mapfile.c 7854 2008-08-14 19:22:48Z dmorissette $
3
 *
4
 * Project:  MapServer
5
 * Purpose:  High level Map file parsing code.
6
 * Author:   Steve Lime and the MapServer team.
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 1996-2005 Regents of the University of Minnesota.
10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a
12
 * copy of this software and associated documentation files (the "Software"),
13
 * to deal in the Software without restriction, including without limitation
14
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15
 * and/or sell copies of the Software, and to permit persons to whom the
16
 * Software is furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included in
19
 * all copies of this Software or works derived from this Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27
 * DEALINGS IN THE SOFTWARE.
28
 ****************************************************************************/
29

30
#ifndef _GNU_SOURCE
31
#define _GNU_SOURCE
32
#endif
33

34
#include <stdarg.h>
35
#include <stdbool.h>
36
#include <assert.h>
37
#include <ctype.h>
38
#include <float.h>
39

40
#include "mapserver.h"
41
#include "mapfile.h"
42
#include "mapthread.h"
43
#include "maptime.h"
44
#include "mapogcsld.h"
45

46
#include "cpl_conv.h"
47
#include "cpl_port.h"
48
#include "cpl_string.h"
49

50
extern int msyylex(void);
51
extern void msyyrestart(FILE *);
52
extern int msyylex_destroy(void);
53
extern void msyycleanup_includes();
54

55
extern double msyynumber;
56
extern int msyylineno;
57
extern FILE *msyyin;
58

59
extern int msyysource;
60
extern int msyystate;
61
extern const char *msyystring;
62
extern char *msyybasepath;
63
extern int msyyreturncomments;
64
extern char *msyystring_buffer;
65
extern int msyystring_icase;
66

67
extern int loadSymbol(symbolObj *s, char *symbolpath); /* in mapsymbol.c */
68
extern void writeSymbol(symbolObj *s, FILE *stream);   /* in mapsymbol.c */
69
static int loadGrid(layerObj *pLayer);
70
static int loadStyle(styleObj *style);
71
static void writeStyle(FILE *stream, int indent, styleObj *style);
72
static int resolveSymbolNames(mapObj *map);
73
static int loadExpression(expressionObj *exp);
74
static void writeExpression(FILE *stream, int indent, const char *name,
75
                            expressionObj *exp);
76

77
/*
78
** Symbol to string static arrays needed for writing map files.
79
** Must be kept in sync with enumerations and defines found in mapserver.h.
80
*/
81
/* static char *msUnits[9]={"INCHES", "FEET", "MILES", "METERS", "KILOMETERS",
82
 * "DD", "PIXELS", "PERCENTAGES", "NAUTICALMILES"}; */
83
/* static char *msLayerTypes[10]={"POINT", "LINE", "POLYGON", "RASTER",
84
 * "ANNOTATION", "QUERY", "CIRCLE", "TILEINDEX","CHART"}; */
85
char *msPositionsText[MS_POSITIONS_LENGTH] = {
86
    "UL", "LR", "UR",   "LL", "CR",    "CL", "UC",
87
    "LC", "CC", "AUTO", "XY", "FOLLOW"}; /* msLabelPositions[] also used in
88
                                            mapsymbols.c (not static) */
89
/* static char *msBitmapFontSizes[5]={"TINY", "SMALL", "MEDIUM", "LARGE",
90
 * "GIANT"}; */
91
/* static char *msQueryMapStyles[4]={"NORMAL", "HILITE", "SELECTED",
92
 * "INVERTED"}; */
93
/* static char *msStatus[4]={"OFF", "ON", "DEFAULT", "EMBED"}; */
94
/* static char *msOnOff[2]={"OFF", "ON"}; */
95
/* static char *msTrueFalse[2]={"FALSE", "TRUE"}; */
96
/* static char *msYesNo[2]={"NO", "YES"}; */
97
/* static char *msJoinType[2]={"ONE-TO-ONE", "ONE-TO-MANY"}; */
98
/* static char *msAlignValue[3]={"LEFT","CENTER","RIGHT"}; */
99

100
/*
101
** Validates a string (value) against a series of patterns. We support up to
102
*four to allow cascading from classObj to
103
** layerObj to webObj plus a legacy pattern like TEMPLATEPATTERN.
104
*/
105
int msValidateParameter(const char *value, const char *pattern1,
26✔
106
                        const char *pattern2, const char *pattern3,
107
                        const char *pattern4) {
108
  if (msEvalRegex(pattern1, value) == MS_TRUE)
26✔
109
    return MS_SUCCESS;
110
  if (msEvalRegex(pattern2, value) == MS_TRUE)
3✔
111
    return MS_SUCCESS;
112
  if (msEvalRegex(pattern3, value) == MS_TRUE)
3✔
113
    return MS_SUCCESS;
114
  if (msEvalRegex(pattern4, value) == MS_TRUE)
3✔
115
    return MS_SUCCESS;
116

117
  return (MS_FAILURE);
118
}
119

120
int msIsValidRegex(const char *e) {
1,871✔
121
  ms_regex_t re;
122
  int errcode = ms_regcomp(&re, e, MS_REG_EXTENDED | MS_REG_NOSUB);
1,871✔
123
  if (errcode != 0) {
1,871✔
124
    char szErrbuf[256];
125
    ms_regerror(errcode, &re, szErrbuf, sizeof(szErrbuf));
×
126
    msSetError(MS_REGEXERR, "Failed to compile expression (%s): %s.",
×
127
               "msIsValidRegex()", e, szErrbuf);
128
    return (MS_FALSE);
129
  }
130
  ms_regfree(&re);
1,871✔
131
  return MS_TRUE;
1,871✔
132
}
133

134
int msEvalRegex(const char *e, const char *s) {
7,267✔
135
  ms_regex_t re;
136

137
  if (!e || !s)
7,267✔
138
    return (MS_FALSE);
139

140
  int errcode = ms_regcomp(&re, e, MS_REG_EXTENDED | MS_REG_NOSUB);
7,257✔
141
  if (errcode != 0) {
7,257✔
142
    char szErrbuf[256];
143
    ms_regerror(errcode, &re, szErrbuf, sizeof(szErrbuf));
1✔
144
    msSetError(MS_REGEXERR, "Failed to compile expression (%s): %s.",
1✔
145
               "msEvalRegex()", e, szErrbuf);
146
    return (MS_FALSE);
147
  }
148

149
  if (ms_regexec(&re, s, 0, NULL, 0) != 0) { /* no match */
7,256✔
150
    ms_regfree(&re);
1,884✔
151
    return (MS_FALSE);
1,884✔
152
  }
153
  ms_regfree(&re);
5,372✔
154

155
  return (MS_TRUE);
5,372✔
156
}
157

158
int msCaseEvalRegex(const char *e, const char *s) {
×
159
  ms_regex_t re;
160

161
  if (!e || !s)
×
162
    return (MS_FALSE);
163

164
  if (ms_regcomp(&re, e, MS_REG_EXTENDED | MS_REG_ICASE | MS_REG_NOSUB) != 0) {
×
165
    msSetError(MS_REGEXERR, "Failed to compile expression (%s).",
×
166
               "msEvalRegex()", e);
167
    return (MS_FALSE);
×
168
  }
169

170
  if (ms_regexec(&re, s, 0, NULL, 0) != 0) { /* no match */
×
171
    ms_regfree(&re);
×
172
    return (MS_FALSE);
×
173
  }
174
  ms_regfree(&re);
×
175

176
  return (MS_TRUE);
×
177
}
178

179
#ifdef USE_MSFREE
180
void msFree(void *p) {
181
  if (p)
182
    free(p);
183
}
184
#endif
185

186
/*
187
** Free memory allocated for a character array
188
*/
189
void msFreeCharArray(char **array, int num_items) {
144,208✔
190
  int i;
191
  if (!array)
144,208✔
192
    return;
193
  for (i = 0; i < num_items; i++)
566,588✔
194
    msFree(array[i]);
438,984✔
195
  msFree(array);
127,604✔
196
}
197

198
/*
199
** Checks symbol from lexer against variable length list of
200
** legal symbols.
201
*/
202
int getSymbol(int n, ...) {
120,816✔
203
  int symbol;
204
  va_list argp;
205
  int i = 0;
206

207
  symbol = msyylex();
120,816✔
208

209
  va_start(argp, n);
120,816✔
210
  while (i < n) { /* check each symbol in the list */
197,563✔
211
    if (symbol == va_arg(argp, int)) {
197,563✔
212
      va_end(argp);
120,816✔
213
      return (symbol);
120,816✔
214
    }
215
    i++;
76,747✔
216
  }
217

218
  va_end(argp);
×
219

220
  msSetError(MS_SYMERR, "Parsing error near (%s):(line %d)", "getSymbol()",
×
221
             msyystring_buffer, msyylineno);
222
  return (-1);
×
223
}
224

225
/*
226
** Same as getSymbol, except no error message is set on failure
227
*/
228
int getSymbol2(int n, ...) {
2,791✔
229
  int symbol;
230
  va_list argp;
231
  int i = 0;
232

233
  symbol = msyylex();
2,791✔
234

235
  va_start(argp, n);
2,791✔
236
  while (i < n) { /* check each symbol in the list */
2,883✔
237
    if (symbol == va_arg(argp, int)) {
2,866✔
238
      va_end(argp);
2,774✔
239
      return (symbol);
2,774✔
240
    }
241
    i++;
92✔
242
  }
243

244
  va_end(argp);
17✔
245
  return (-1);
17✔
246
}
247

248
/*
249
** Get a string or symbol as a string.   Operates like getString(), but also
250
** supports symbols.
251
*/
252
static char *getToken(void) {
253
  msyylex();
757✔
254
  return msStrdup(msyystring_buffer);
3,556✔
255
}
256

257
/*
258
** Load a string from the map file. A "string" is defined in lexer.l.
259
*/
260
int getString(char **s) {
173,152✔
261
  /* if (*s)
262
    msSetError(MS_SYMERR, "Duplicate item (%s):(line %d)", "getString()",
263
  msyystring_buffer, msyylineno); return(MS_FAILURE); } else */
264
  if (msyylex() == MS_STRING) {
173,152✔
265
    if (*s)
173,152✔
266
      free(*s); /* avoid leak */
3,161✔
267
    *s = msStrdup(msyystring_buffer);
173,152✔
268
    return (MS_SUCCESS);
173,152✔
269
  }
270

271
  msSetError(MS_SYMERR, "Parsing error near (%s):(line %d)", "getString()",
×
272
             msyystring_buffer, msyylineno);
273
  return (MS_FAILURE);
×
274
}
275

276
int msCheckNumber(double number, int num_check_type, double value1,
152,325✔
277
                  double value2) {
278
  if (num_check_type == MS_NUM_CHECK_NONE) {
152,325✔
279
    return MS_SUCCESS;
280
  } else if (num_check_type == MS_NUM_CHECK_RANGE && number >= value1 &&
77,882✔
281
             number <= value2) {
282
    return MS_SUCCESS;
283
  } else if (num_check_type == MS_NUM_CHECK_GT && number > value1) {
17,258✔
284
    return MS_SUCCESS;
285
  } else if (num_check_type == MS_NUM_CHECK_GTE && number >= value1) {
8,544✔
286
    return MS_SUCCESS;
8,544✔
287
  }
288

289
  return MS_FAILURE;
290
}
291

292
/*
293
** Load a floating point number from the map file. (see lexer.l)
294
*/
295
int getDouble(double *d, int num_check_type, double value1, double value2) {
74,731✔
296
  if (msyylex() == MS_NUMBER) {
74,731✔
297
    if (msCheckNumber(msyynumber, num_check_type, value1, value2) ==
74,731✔
298
        MS_SUCCESS) {
299
      *d = msyynumber;
74,731✔
300
      return (0);
74,731✔
301
    }
302
  }
303

304
  msSetError(MS_SYMERR, "Parsing error near (%s):(line %d)", "getDouble()",
×
305
             msyystring_buffer, msyylineno);
306
  return (-1);
×
307
}
308

309
/*
310
** Load a integer from the map file. (see lexer.l)
311
*/
312
int getInteger(int *i, int num_check_type, int value1, int value2) {
61,102✔
313
  if (msyylex() == MS_NUMBER) {
61,102✔
314
    if (msCheckNumber(msyynumber, num_check_type, value1, value2) ==
61,102✔
315
        MS_SUCCESS) {
316
      *i = (int)msyynumber;
61,102✔
317
      return (0);
61,102✔
318
    }
319
  }
320

321
  msSetError(MS_SYMERR, "Parsing error near (%s):(line %d)", "getInteger()",
×
322
             msyystring_buffer, msyylineno);
323
  return (-1);
×
324
}
325

326
int getCharacter(char *c) {
257✔
327
  if (msyylex() == MS_STRING) {
257✔
328
    *c = msyystring_buffer[0];
257✔
329
    return (0);
257✔
330
  }
331

332
  msSetError(MS_SYMERR, "Parsing error near (%s):(line %d)", "getCharacter()",
×
333
             msyystring_buffer, msyylineno);
334
  return (-1);
×
335
}
336

337
/*
338
** Try to load as an integer, then try as a named symbol.
339
** Part of work on bug 490.
340
*/
341
int getIntegerOrSymbol(int *i, int n, ...) {
×
342
  int symbol;
343
  va_list argp;
344
  int j = 0;
345

346
  symbol = msyylex();
×
347

348
  if (symbol == MS_NUMBER) {
×
349
    *i = (int)msyynumber;
×
350
    return MS_SUCCESS; /* success */
×
351
  }
352

353
  va_start(argp, n);
×
354
  while (j < n) { /* check each symbol in the list */
×
355
    if (symbol == va_arg(argp, int)) {
×
356
      va_end(argp);
×
357
      *i = symbol;
×
358
      return MS_SUCCESS;
×
359
    }
360
    j++;
×
361
  }
362
  va_end(argp);
×
363

364
  msSetError(MS_SYMERR, "Parsing error near (%s):(line %d)",
×
365
             "getIntegerOrSymbol()", msyystring_buffer, msyylineno);
366
  return (-1);
×
367
}
368

369
/*
370
** msBuildPluginLibraryPath
371
**
372
** This function builds a path to be used dynamically to load plugin library.
373
*/
374
int msBuildPluginLibraryPath(char **dest, const char *lib_str, mapObj *map) {
×
375
  char szLibPath[MS_MAXPATHLEN] = {'\0'};
×
376
  char szLibPathExt[MS_MAXPATHLEN] = {'\0'};
×
377
  const char *plugin_dir = NULL;
378

379
  if (map)
×
380
    plugin_dir = msLookupHashTable(&(map->configoptions), "MS_PLUGIN_DIR");
×
381

382
    /* do nothing on windows, filename without .dll will be loaded by default*/
383
#if !defined(_WIN32)
384
  if (lib_str) {
×
385
    size_t len = strlen(lib_str);
×
386
    if (3 < len && strcmp(lib_str + len - 3, ".so")) {
×
387
      strlcpy(szLibPathExt, lib_str, MS_MAXPATHLEN);
388
      strlcat(szLibPathExt, ".so", MS_MAXPATHLEN);
389
      lib_str = szLibPathExt;
390
    }
391
  }
392
#endif /* !defined(_WIN32) */
393
  if (NULL == msBuildPath(szLibPath, plugin_dir, lib_str)) {
×
394
    return MS_FAILURE;
395
  }
396
  msFree(*dest);
×
397
  *dest = msStrdup(szLibPath);
×
398

399
  return MS_SUCCESS;
×
400
}
401

402
/*
403
** Returns the index of specified symbol or -1 if not found.
404
**
405
** If try_addimage_if_notfound==MS_TRUE then msAddImageSymbol() will be called
406
** to try to allocate the symbol as an image symbol.
407
*/
408
int msGetSymbolIndex(symbolSetObj *symbols, const char *name,
3,259✔
409
                     int try_addimage_if_notfound) {
410
  int i;
411

412
  if (!symbols || !name)
3,259✔
413
    return (-1);
414

415
  /* symbol 0 has no name */
416
  for (i = 1; i < symbols->numsymbols; i++) {
9,979✔
417
    if (symbols->symbol[i]->name)
9,679✔
418
      if (strcasecmp(symbols->symbol[i]->name, name) == 0)
9,034✔
419
        return (i);
2,959✔
420
  }
421

422
  if (try_addimage_if_notfound)
300✔
423
    return (
424
        msAddImageSymbol(symbols, name)); /* make sure it's not a filename */
46✔
425

426
  return (-1);
427
}
428

429
/*
430
** Return the index number for a given layer based on its name.
431
*/
432
int msGetLayerIndex(mapObj *map, const char *name) {
1,140✔
433
  int i;
434

435
  if (!name)
1,140✔
436
    return (-1);
437

438
  for (i = 0; i < map->numlayers; i++) {
3,889✔
439
    if (!GET_LAYER(map, i)->name) /* skip it */
3,445✔
440
      continue;
1✔
441
    if (strcmp(name, GET_LAYER(map, i)->name) == 0)
3,444✔
442
      return (i);
674✔
443
  }
444
  return (-1);
445
}
446

447
int loadColor(colorObj *color, attributeBindingObj *binding) {
24,546✔
448
  int symbol;
449
  char hex[2];
450

451
  /*
452
  ** Note that negative color values can be used to suppress or change behavior.
453
  *For example, referenceObj uses
454
  ** a negative color component to suppress rectangle fills.
455
  */
456

457
  if (binding) {
24,546✔
458
    if ((symbol = getSymbol(3, MS_NUMBER, MS_BINDING, MS_STRING)) == -1)
20,489✔
459
      return MS_FAILURE;
460
  } else {
461
    if ((symbol = getSymbol(2, MS_NUMBER, MS_STRING)) == -1)
4,057✔
462
      return MS_FAILURE;
463
  }
464

465
  color->alpha = 255;
24,546✔
466
  if (symbol == MS_NUMBER) {
24,546✔
467
    if (msyynumber >= -255 && msyynumber <= 255) {
24,127✔
468
      color->red = (int)msyynumber;
24,127✔
469
    } else {
470
      return MS_FAILURE;
471
    }
472
    if (getInteger(&(color->green), MS_NUM_CHECK_RANGE, -255, 255) == -1)
24,127✔
473
      return MS_FAILURE;
474
    if (getInteger(&(color->blue), MS_NUM_CHECK_RANGE, -255, 255) == -1)
24,127✔
475
      return MS_FAILURE;
476
  } else if (symbol == MS_STRING) {
419✔
477
    int len = strlen(msyystring_buffer);
396✔
478
    if (msyystring_buffer[0] == '#' &&
396✔
479
        (len == 7 || len == 9)) { /* got a hex color w/optional alpha */
396✔
480
      hex[0] = msyystring_buffer[1];
396✔
481
      hex[1] = msyystring_buffer[2];
396✔
482
      color->red = msHexToInt(hex);
396✔
483
      hex[0] = msyystring_buffer[3];
396✔
484
      hex[1] = msyystring_buffer[4];
396✔
485
      color->green = msHexToInt(hex);
396✔
486
      hex[0] = msyystring_buffer[5];
396✔
487
      hex[1] = msyystring_buffer[6];
396✔
488
      color->blue = msHexToInt(hex);
396✔
489
      if (len == 9) {
396✔
490
        hex[0] = msyystring_buffer[7];
41✔
491
        hex[1] = msyystring_buffer[8];
41✔
492
        color->alpha = msHexToInt(hex);
41✔
493
      }
494
    } else {
495
      /* TODO: consider named colors here */
496
      msSetError(MS_SYMERR, "Invalid hex color (%s):(line %d)", "loadColor()",
×
497
                 msyystring_buffer, msyylineno);
498
      return MS_FAILURE;
×
499
    }
500
  } else {
501
    assert(binding);
502
    msFree(binding->item);
23✔
503
    binding->item = msStrdup(msyystring_buffer);
23✔
504
    binding->index = -1;
23✔
505
  }
506

507
  return MS_SUCCESS;
508
}
509

510
#if ALPHACOLOR_ENABLED
511
int loadColorWithAlpha(colorObj *color) {
512
  char hex[2];
513

514
  /*
515
  ** Note that negative color values can be used to suppress or change behavior.
516
  *For example, referenceObj uses
517
  ** a negative color component to suppress rectangle fills.
518
  */
519

520
  if (getInteger(&(color->red), MS_NUM_CHECK_RANGE, -255, 255) == -1) {
521
    if (msyystring_buffer[0] == '#' &&
522
        strlen(msyystring_buffer) == 7) { /* got a hex color */
523
      hex[0] = msyystring_buffer[1];
524
      hex[1] = msyystring_buffer[2];
525
      color->red = msHexToInt(hex);
526
      hex[0] = msyystring_buffer[3];
527
      hex[1] = msyystring_buffer[4];
528
      color->green = msHexToInt(hex);
529
      hex[0] = msyystring_buffer[5];
530
      hex[1] = msyystring_buffer[6];
531
      color->blue = msHexToInt(hex);
532
      color->alpha = 0;
533

534
      return (MS_SUCCESS);
535
    } else if (msyystring_buffer[0] == '#' &&
536
               strlen(msyystring_buffer) ==
537
                   9) { /* got a hex color with alpha */
538
      hex[0] = msyystring_buffer[1];
539
      hex[1] = msyystring_buffer[2];
540
      color->red = msHexToInt(hex);
541
      hex[0] = msyystring_buffer[3];
542
      hex[1] = msyystring_buffer[4];
543
      color->green = msHexToInt(hex);
544
      hex[0] = msyystring_buffer[5];
545
      hex[1] = msyystring_buffer[6];
546
      color->blue = msHexToInt(hex);
547
      hex[0] = msyystring_buffer[7];
548
      hex[1] = msyystring_buffer[8];
549
      color->alpha = msHexToInt(hex);
550
      return (MS_SUCCESS);
551
    }
552
    return (MS_FAILURE);
553
  }
554
  if (getInteger(&(color->green), MS_NUM_CHECK_RANGE, -255, 255) == -1)
555
    return (MS_FAILURE);
556
  if (getInteger(&(color->blue), MS_NUM_CHECK_RANGE, -255, 255) == -1)
557
    return (MS_FAILURE);
558
  if (getInteger(&(color->alpha), MS_NUM_CHECK_RANGE, 0, 255) == -1)
559
    return (MS_FAILURE);
560

561
  return (MS_SUCCESS);
562
}
563
#endif
564

565
/*
566
** Helper functions for writing mapfiles.
567
*/
568
static void writeLineFeed(FILE *stream) { msIO_fprintf(stream, "\n"); }
85✔
569

570
static void writeIndent(FILE *stream, int indent) {
571
  const char *str = "  "; /* change this string to define the indent */
572
  int i;
573
  for (i = 0; i < indent; i++)
967✔
574
    msIO_fprintf(stream, "%s", str);
680✔
575
}
576

577
static void writeBlockBegin(FILE *stream, int indent, const char *name) {
58✔
578
  writeIndent(stream, indent);
579
  msIO_fprintf(stream, "%s\n", name);
58✔
580
}
58✔
581

582
static void writeBlockEnd(FILE *stream, int indent, const char *name) {
58✔
583
  writeIndent(stream, indent);
584
  msIO_fprintf(stream, "END # %s\n", name);
58✔
585
}
58✔
586

587
static void writeKeyword(FILE *stream, int indent, const char *name, int value,
128✔
588
                         int size, ...) {
589
  va_list argp;
590
  int i, j = 0;
591
  const char *s;
592

593
  va_start(argp, size);
128✔
594
  while (j < size) { /* check each value/keyword mapping in the list, values
537✔
595
                        with no match are ignored */
596
    i = va_arg(argp, int);
448✔
597
    s = va_arg(argp, const char *);
448✔
598
    if (value == i) {
448✔
599
      writeIndent(stream, ++indent);
600
      msIO_fprintf(stream, "%s %s\n", name, s);
39✔
601
      va_end(argp);
39✔
602
      return;
39✔
603
    }
604
    j++;
409✔
605
  }
606
  va_end(argp);
89✔
607
}
608

609
static void writeDimension(FILE *stream, int indent, const char *name, double x,
17✔
610
                           double y, char *bind_x, char *bind_y) {
611
  writeIndent(stream, ++indent);
612
  if (bind_x)
17✔
613
    msIO_fprintf(stream, "%s [%s] ", name, bind_x);
×
614
  else
615
    msIO_fprintf(stream, "%s %.15g ", name, x);
17✔
616
  if (bind_y)
17✔
617
    msIO_fprintf(stream, "[%s]\n", bind_y);
×
618
  else
619
    msIO_fprintf(stream, "%.15g\n", y);
17✔
620
}
17✔
621

622
static void writeDoubleRange(FILE *stream, int indent, const char *name,
×
623
                             double x, double y) {
624
  writeIndent(stream, ++indent);
625
  msIO_fprintf(stream, "%s %f %f\n", name, x, y);
×
626
}
×
627

628
static void writeExtent(FILE *stream, int indent, const char *name,
8✔
629
                        rectObj extent) {
630
  if (!MS_VALID_EXTENT(extent))
8✔
631
    return;
632
  writeIndent(stream, ++indent);
633
  msIO_fprintf(stream, "%s %.15g %.15g %.15g %.15g\n", name, extent.minx,
2✔
634
               extent.miny, extent.maxx, extent.maxy);
635
}
636

637
static void writeNumber(FILE *stream, int indent, const char *name,
370✔
638
                        double defaultNumber, double number) {
639
  if (number == defaultNumber)
370✔
640
    return; /* don't output default */
641
  writeIndent(stream, ++indent);
642
  msIO_fprintf(stream, "%s %.15g\n", name, number);
25✔
643
}
644

645
static void writeCharacter(FILE *stream, int indent, const char *name,
6✔
646
                           const char defaultCharacter, char character) {
647
  if (defaultCharacter == character)
6✔
648
    return;
649
  writeIndent(stream, ++indent);
650
  msIO_fprintf(stream, "%s '%c'\n", name, character);
×
651
}
652

653
static void writeStringElement(FILE *stream, const char *string) {
85✔
654
  char *string_to_free = NULL;
655
  const char *string_escaped;
656

657
  if (strchr(string, '\\')) {
85✔
658
    string_to_free = msStrdup(string);
×
659
    string_to_free = msReplaceSubstring(string_to_free, "\\", "\\\\");
×
660
    string_escaped = string_to_free;
661
  } else {
662
    string_escaped = string;
663
  }
664
  if ((strchr(string_escaped, '\'') == NULL) &&
85✔
665
      (strchr(string_escaped, '\"') == NULL))
85✔
666
    msIO_fprintf(stream, "\"%s\"", string_escaped);
85✔
667
  else if ((strchr(string_escaped, '\"') != NULL) &&
×
668
           (strchr(string_escaped, '\'') == NULL))
669
    msIO_fprintf(stream, "'%s'", string_escaped);
×
670
  else if ((strchr(string_escaped, '\'') != NULL) &&
×
671
           (strchr(string_escaped, '\"') == NULL))
672
    msIO_fprintf(stream, "\"%s\"", string_escaped);
×
673
  else {
674
    char *string_tmp = msStringEscape(string_escaped);
×
675
    msIO_fprintf(stream, "\"%s\"", string_tmp);
×
676
    if (string_escaped != string_tmp)
×
677
      free(string_tmp);
×
678
  }
679
  if (string_to_free)
85✔
680
    free(string_to_free);
×
681
}
85✔
682

683
static void writeString(FILE *stream, int indent, const char *name,
253✔
684
                        const char *defaultString, const char *string) {
685
  if (!string)
253✔
686
    return;
687
  if (defaultString && strcmp(string, defaultString) == 0)
58✔
688
    return;
689
  writeIndent(stream, ++indent);
690
  if (name)
53✔
691
    msIO_fprintf(stream, "%s ", name);
45✔
692
  writeStringElement(stream, string);
53✔
693
  writeLineFeed(stream);
694
}
695

696
static void writeNumberOrString(FILE *stream, int indent, const char *name,
11✔
697
                                double defaultNumber, double number,
698
                                char *string) {
699
  if (string)
11✔
700
    writeString(stream, indent, name, NULL, string);
2✔
701
  else
702
    writeNumber(stream, indent, name, defaultNumber, number);
9✔
703
}
11✔
704

705
static void writeNumberOrKeyword(FILE *stream, int indent, const char *name,
23✔
706
                                 double defaultNumber, double number, int value,
707
                                 int size, ...) {
708
  va_list argp;
709
  int i, j = 0;
710
  const char *s;
711

712
  va_start(argp, size);
23✔
713
  while (j < size) { /* check each value/keyword mapping in the list */
52✔
714
    i = va_arg(argp, int);
29✔
715
    s = va_arg(argp, const char *);
29✔
716
    if (value == i) {
29✔
717
      writeIndent(stream, ++indent);
718
      msIO_fprintf(stream, "%s %s\n", name, s);
×
719
      va_end(argp);
×
720
      return;
×
721
    }
722
    j++;
29✔
723
  }
724
  va_end(argp);
23✔
725

726
  writeNumber(stream, indent, name, defaultNumber, number);
23✔
727
}
728

729
static void writeNameValuePair(FILE *stream, int indent, const char *name,
14✔
730
                               const char *value) {
731
  if (!name || !value)
14✔
732
    return;
733
  writeIndent(stream, ++indent);
734

735
  writeStringElement(stream, (char *)name);
14✔
736
  msIO_fprintf(stream, "\t");
14✔
737
  writeStringElement(stream, (char *)value);
14✔
738
  writeLineFeed(stream);
739
}
740

741
static void writeAttributeBinding(FILE *stream, int indent, const char *name,
×
742
                                  attributeBindingObj *binding) {
743
  if (!binding || !binding->item)
×
744
    return;
745
  writeIndent(stream, ++indent);
746
  msIO_fprintf(stream, "%s [%s]\n", name, binding->item);
×
747
}
748

749
static void writeColor(FILE *stream, int indent, const char *name,
55✔
750
                       colorObj *defaultColor, colorObj *color) {
751
  if (!defaultColor && !MS_VALID_COLOR(*color))
55✔
752
    return;
753
  else if (defaultColor && MS_COMPARE_COLOR(*defaultColor, *color))
10✔
754
    return; /* if defaultColor has the same value than the color, return.*/
755

756
  writeIndent(stream, ++indent);
757
#if ALPHACOLOR_ENABLED
758
  msIO_fprintf(stream, "%s %d %d %d\n", name, color->red, color->green,
759
               color->blue, color->alpha);
760
#else
761
  if (color->alpha != 255) {
13✔
762
    char buffer[9];
763
    sprintf(buffer, "%02x", color->red);
×
764
    sprintf(buffer + 2, "%02x", color->green);
×
765
    sprintf(buffer + 4, "%02x", color->blue);
×
766
    sprintf(buffer + 6, "%02x", color->alpha);
×
767
    *(buffer + 8) = 0;
×
768
    msIO_fprintf(stream, "%s \"#%s\"\n", name, buffer);
×
769
  } else {
770
    msIO_fprintf(stream, "%s %d %d %d\n", name, color->red, color->green,
13✔
771
                 color->blue);
772
  }
773
#endif
774
}
775

776
/* todo: deal with alpha's... */
777
static void writeColorRange(FILE *stream, int indent, const char *name,
×
778
                            colorObj *mincolor, colorObj *maxcolor) {
779
  if (!MS_VALID_COLOR(*mincolor) || !MS_VALID_COLOR(*maxcolor))
×
780
    return;
781
  writeIndent(stream, ++indent);
782
  msIO_fprintf(stream, "%s %d %d %d  %d %d %d\n", name, mincolor->red,
×
783
               mincolor->green, mincolor->blue, maxcolor->red, maxcolor->green,
784
               maxcolor->blue);
785
}
786

787
/*
788
** Initialize, load and free a single join
789
*/
790
void initJoin(joinObj *join) {
×
791
  join->numitems = 0;
×
792

793
  join->name = NULL; /* unique join name, used for variable substitution */
×
794

795
  join->items = NULL;  /* array to hold item names for the joined table */
×
796
  join->values = NULL; /* arrays of strings to holds one record worth of data */
×
797

798
  join->table = NULL;
×
799

800
  join->joininfo = NULL;
×
801

802
  join->from = NULL; /* join items */
×
803
  join->to = NULL;
×
804

805
  join->header = NULL;
×
806
  join->template = NULL; /* only html type templates are supported */
×
807
  join->footer = NULL;
×
808

809
  join->type = MS_JOIN_ONE_TO_ONE;
×
810

811
  join->connection = NULL;
×
812
  join->connectiontype = MS_DB_XBASE;
×
813
}
×
814

815
void freeJoin(joinObj *join) {
×
816
  msFree(join->name);
×
817
  msFree(join->table);
×
818
  msFree(join->from);
×
819
  msFree(join->to);
×
820

821
  msFree(join->header);
×
822
  msFree(join->template);
×
823
  msFree(join->footer);
×
824

825
  msFreeCharArray(join->items,
×
826
                  join->numitems); /* these may have been free'd elsewhere */
827
  msFreeCharArray(join->values, join->numitems);
×
828
  join->numitems = 0;
×
829

830
  msJoinClose(join);
×
831
  msFree(join->connection);
×
832
}
×
833

834
int loadJoin(joinObj *join) {
×
835
  int nTmp;
836
  initJoin(join);
×
837

838
  for (;;) {
839
    switch (msyylex()) {
×
840
    case (CONNECTION):
×
841
      if (getString(&join->connection) == MS_FAILURE)
×
842
        return (-1);
843
      break;
844
    case (CONNECTIONTYPE):
×
845
      if ((nTmp = getSymbol(5, MS_DB_XBASE, MS_DB_MYSQL, MS_DB_ORACLE,
×
846
                            MS_DB_POSTGRES, MS_DB_CSV)) == -1)
847
        return (-1);
848
      join->connectiontype = nTmp;
×
849
      break;
×
850
    case (EOF):
×
851
      msSetError(MS_EOFERR, NULL, "loadJoin()");
×
852
      return (-1);
×
853
    case (END):
×
854
      if ((join->from == NULL) || (join->to == NULL) || (join->table == NULL)) {
×
855
        msSetError(MS_EOFERR,
×
856
                   "Join must define table, name, from and to properties.",
857
                   "loadJoin()");
858
        return (-1);
×
859
      }
860
      if ((join->type == MS_MULTIPLE) &&
×
861
          ((join->template == NULL) || (join->name == NULL))) {
×
862
        msSetError(
×
863
            MS_EOFERR,
864
            "One-to-many joins must define template and name properties.",
865
            "loadJoin()");
866
        return (-1);
×
867
      }
868
      return (0);
869
    case (FOOTER):
×
870
      if (getString(&join->footer) == MS_FAILURE)
×
871
        return (-1);
872
      break;
873
    case (FROM):
×
874
      if (getString(&join->from) == MS_FAILURE)
×
875
        return (-1);
876
      break;
877
    case (HEADER):
×
878
      if (getString(&join->header) == MS_FAILURE)
×
879
        return (-1);
880
      break;
881
    case (JOIN):
882
      break; /* for string loads */
883
    case (NAME):
×
884
      if (getString(&join->name) == MS_FAILURE)
×
885
        return (-1);
886
      break;
887
    case (TABLE):
×
888
      if (getString(&join->table) == MS_FAILURE)
×
889
        return (-1);
890
      break;
891
    case (TEMPLATE):
×
892
      if (getString(&join->template) == MS_FAILURE)
×
893
        return (-1);
894
      break;
895
    case (TO):
×
896
      if (getString(&join->to) == MS_FAILURE)
×
897
        return (-1);
898
      break;
899
    case (TYPE):
×
900
      if ((nTmp = getSymbol(2, MS_JOIN_ONE_TO_ONE, MS_JOIN_ONE_TO_MANY)) == -1)
×
901
        return (-1);
902
      join->type = nTmp;
×
903
      break;
×
904
    default:
×
905
      msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)", "loadJoin()",
×
906
                 msyystring_buffer, msyylineno);
907
      return (-1);
×
908
    }
909
  } /* next token */
910
}
911

912
static void writeScaleToken(FILE *stream, int indent, scaleTokenObj *token) {
×
913
  int i;
914
  indent++;
×
915
  writeBlockBegin(stream, indent, "SCALETOKEN");
×
916
  writeString(stream, indent, "NAME", NULL, token->name);
×
917
  indent++;
×
918
  writeBlockBegin(stream, indent, "VALUES");
×
919
  for (i = 0; i < token->n_entries; i++) {
×
920
    char minscale[32];
921
    sprintf(minscale, "%g", token->tokens[i].minscale);
×
922
    writeNameValuePair(stream, indent, minscale, token->tokens[i].value);
×
923
  }
924
  writeBlockEnd(stream, indent, "VALUES");
×
925
  indent--;
926
  writeBlockEnd(stream, indent, "SCALETOKEN");
×
927
}
×
928

929
static void writeJoin(FILE *stream, int indent, joinObj *join) {
×
930
  indent++;
×
931
  writeBlockBegin(stream, indent, "JOIN");
×
932
  writeString(stream, indent, "FOOTER", NULL, join->footer);
×
933
  writeString(stream, indent, "FROM", NULL, join->from);
×
934
  writeString(stream, indent, "HEADER", NULL, join->header);
×
935
  writeString(stream, indent, "NAME", NULL, join->name);
×
936
  writeString(stream, indent, "TABLE", NULL, join->table);
×
937
  writeString(stream, indent, "TEMPLATE", NULL, join->template);
×
938
  writeString(stream, indent, "TO", NULL, join->to);
×
939
  writeKeyword(stream, indent, "CONNECTIONTYPE", join->connectiontype, 3,
×
940
               MS_DB_CSV, "CSV", MS_DB_POSTGRES, "POSTGRESQL", MS_DB_MYSQL,
941
               "MYSQL");
942
  writeKeyword(stream, indent, "TYPE", join->type, 1, MS_JOIN_ONE_TO_MANY,
×
943
               "ONE-TO-MANY");
944
  writeBlockEnd(stream, indent, "JOIN");
×
945
}
×
946

947
/* inserts a feature at the end of the list, can create a new list */
948
featureListNodeObjPtr insertFeatureList(featureListNodeObjPtr *list,
3,286✔
949
                                        shapeObj *shape) {
950
  featureListNodeObjPtr node;
951

952
  node = (featureListNodeObjPtr)msSmallMalloc(sizeof(featureListNodeObj));
3,286✔
953

954
  msInitShape(&(node->shape));
3,286✔
955
  if (msCopyShape(shape, &(node->shape)) == -1) {
3,286✔
956
    msFree(node);
×
957
    return (NULL);
×
958
  }
959

960
  /* AJS - alans@wunderground.com O(n^2) -> O(n) conversion, keep a pointer to
961
   * the end */
962

963
  /* set the tailifhead to NULL, since it is only set for the head of the list
964
   */
965
  node->tailifhead = NULL;
3,286✔
966
  node->next = NULL;
3,286✔
967

968
  /* if we are at the head of the list, we need to set the list to node, before
969
   * pointing tailifhead somewhere  */
970
  if (*list == NULL) {
3,286✔
971
    *list = node;
2,563✔
972
  } else {
973
    if ((*list)->tailifhead !=
723✔
974
        NULL) /* this should never be NULL, but just in case */
975
      (*list)->tailifhead->next =
723✔
976
          node; /* put the node at the end of the list */
977
  }
978

979
  /* repoint the head of the list to the end  - our new element
980
     this causes a loop if we are at the head, be careful not to
981
     walk in a loop */
982
  (*list)->tailifhead = node;
3,286✔
983

984
  return (node); /* a pointer to last object in the list */
3,286✔
985
}
986

987
void freeFeatureList(featureListNodeObjPtr list) {
2,546✔
988
  featureListNodeObjPtr listNext = NULL;
989
  while (list != NULL) {
5,815✔
990
    listNext = list->next;
3,269✔
991
    msFreeShape(&(list->shape));
3,269✔
992
    msFree(list);
3,269✔
993
    list = listNext;
994
  }
995
}
2,546✔
996

997
/* lineObj = multipointObj */
998
static int loadFeaturePoints(lineObj *points) {
2,910✔
999
  int ret = -1;
1000
  int buffer_size = 0;
1001

1002
  points->point = (pointObj *)malloc(sizeof(pointObj) * MS_FEATUREINITSIZE);
2,910✔
1003
  MS_CHECK_ALLOC(points->point, sizeof(pointObj) * MS_FEATUREINITSIZE,
2,910✔
1004
                 MS_FAILURE);
1005
  points->numpoints = 0;
2,910✔
1006
  buffer_size = MS_FEATUREINITSIZE;
1007

1008
  while (ret < 0) {
2,910✔
1009
    switch (msyylex()) {
7,414✔
1010
    case (EOF):
×
1011
      msSetError(MS_EOFERR, NULL, "loadFeaturePoints()");
×
1012
      ret = MS_FAILURE;
1013
      break;
1014
    case (END):
1015
      ret = MS_SUCCESS;
1016
      break;
1017
    case (MS_NUMBER):
4,504✔
1018
      if (points->numpoints == buffer_size) { /* just add it to the end */
4,504✔
1019
        pointObj *newPoints = (pointObj *)realloc(
48✔
1020
            points->point,
48✔
1021
            sizeof(pointObj) * (buffer_size + MS_FEATUREINCREMENT));
48✔
1022
        if (newPoints == NULL) {
48✔
1023
          msSetError(MS_MEMERR, "%s: %d: Out of memory allocating %u bytes.\n",
×
1024
                     __FUNCTION__, __FILE__, __LINE__,
1025
                     (unsigned int)(sizeof(pointObj) *
1026
                                    (buffer_size + MS_FEATUREINCREMENT)));
1027
          ret = MS_FAILURE;
1028
          break;
1029
        }
1030
        points->point = newPoints;
48✔
1031
        buffer_size += MS_FEATUREINCREMENT;
1032
      }
1033

1034
      points->point[points->numpoints].x = atof(msyystring_buffer);
4,504✔
1035
      if (getDouble(&(points->point[points->numpoints].y), MS_NUM_CHECK_NONE,
4,504✔
1036
                    -1, -1) == -1) {
1037
        ret = MS_FAILURE;
1038
        break;
1039
      }
1040
      points->numpoints++;
4,504✔
1041
      break;
4,504✔
1042
    default:
×
1043
      msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
1044
                 "loadFeaturePoints()", msyystring_buffer, msyylineno);
1045
      ret = MS_FAILURE;
1046
      break;
1047
    }
1048
  }
1049

1050
  if (ret == MS_FAILURE) {
2,910✔
1051
    msFree(points->point); /* clean up */
×
1052
    points->point = NULL;
×
1053
    points->numpoints = 0;
×
1054
  }
1055
  return ret;
1056
}
1057

1058
static int loadFeature(layerObj *player, int type) {
2,922✔
1059
  int status = MS_SUCCESS;
1060
  featureListNodeObjPtr *list = &(player->features);
2,922✔
1061
  multipointObj points = {0, NULL};
2,922✔
1062
  shapeObj *shape = NULL;
1063

1064
  shape = (shapeObj *)malloc(sizeof(shapeObj));
2,922✔
1065
  MS_CHECK_ALLOC(shape, sizeof(shapeObj), MS_FAILURE);
2,922✔
1066

1067
  msInitShape(shape);
2,922✔
1068
  shape->type = type;
2,922✔
1069

1070
  for (;;) {
1071
    switch (msyylex()) {
6,980✔
1072
    case (EOF):
×
1073
      msSetError(MS_EOFERR, NULL, "loadFeature()");
×
1074
      msFreeShape(shape); /* clean up */
×
1075
      msFree(shape);
×
1076
      return (MS_FAILURE);
×
1077
    case (END):
2,922✔
1078
      if (player->features != NULL && player->features->tailifhead != NULL)
2,922✔
1079
        shape->index = player->features->tailifhead->shape.index + 1;
478✔
1080
      else
1081
        shape->index = 0;
2,444✔
1082
      if (insertFeatureList(list, shape) == NULL)
2,922✔
1083
        status = MS_FAILURE;
1084

1085
      msFreeShape(shape); /* clean up */
2,922✔
1086
      msFree(shape);
2,922✔
1087

1088
      return (status);
2,922✔
1089
    case (FEATURE):
1090
      break; /* for string loads */
1091
    case (POINTS):
2,910✔
1092
      if (loadFeaturePoints(&points) == MS_FAILURE) {
2,910✔
1093
        msFreeShape(shape); /* clean up */
×
1094
        msFree(shape);
×
1095
        return (MS_FAILURE);
×
1096
      }
1097
      status = msAddLine(shape, &points);
2,910✔
1098

1099
      msFree(points.point); /* clean up */
2,910✔
1100
      points.numpoints = 0;
2,910✔
1101

1102
      if (status == MS_FAILURE) {
2,910✔
1103
        msFreeShape(shape); /* clean up */
×
1104
        msFree(shape);
×
1105
        return (MS_FAILURE);
×
1106
      }
1107
      break;
1108
    case (ITEMS): {
73✔
1109
      char *string = NULL;
73✔
1110
      if (getString(&string) == MS_FAILURE) {
73✔
1111
        msFreeShape(shape); /* clean up */
×
1112
        msFree(shape);
×
1113
        return (MS_FAILURE);
×
1114
      }
1115
      if (string) {
73✔
1116
        if (shape->values)
73✔
1117
          msFreeCharArray(shape->values, shape->numvalues);
×
1118
        shape->values = msStringSplitComplex(string, ";", &shape->numvalues,
73✔
1119
                                             MS_ALLOWEMPTYTOKENS);
1120
        msFree(string); /* clean up */
73✔
1121
      }
1122
      break;
73✔
1123
    }
1124
    case (TEXT):
1,059✔
1125
      if (getString(&shape->text) == MS_FAILURE) {
1,059✔
1126
        msFreeShape(shape); /* clean up */
×
1127
        msFree(shape);
×
1128
        return (MS_FAILURE);
×
1129
      }
1130
      break;
1131
    case (WKT): {
16✔
1132
      char *string = NULL;
16✔
1133

1134
      /* todo, what do we do with multiple WKT property occurrences? */
1135

1136
      msFreeShape(shape);
16✔
1137
      msFree(shape);
16✔
1138
      if (getString(&string) == MS_FAILURE)
16✔
1139
        return (MS_FAILURE);
×
1140

1141
      if ((shape = msShapeFromWKT(string)) == NULL)
16✔
1142
        status = MS_FAILURE;
1143

1144
      msFree(string); /* clean up */
16✔
1145

1146
      if (status == MS_FAILURE) {
16✔
1147
        msFreeShape(shape); /* clean up */
×
1148
        msFree(shape);
×
1149
        return (MS_FAILURE);
×
1150
      }
1151
      break;
16✔
1152
    }
1153
    default:
×
1154
      msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
1155
                 "loadfeature()", msyystring_buffer, msyylineno);
1156
      msFreeShape(shape); /* clean up */
×
1157
      msFree(shape);
×
1158
      return (MS_FAILURE);
×
1159
    }
1160
  } /* next token */
1161
}
1162

1163
static void writeFeature(FILE *stream, int indent, shapeObj *feature) {
3✔
1164
  int i, j;
1165

1166
  indent++;
3✔
1167
  writeBlockBegin(stream, indent, "FEATURE");
3✔
1168

1169
  indent++;
3✔
1170
  for (i = 0; i < feature->numlines; i++) {
6✔
1171
    writeBlockBegin(stream, indent, "POINTS");
3✔
1172
    for (j = 0; j < feature->line[i].numpoints; j++) {
6✔
1173
      writeIndent(stream, indent);
1174
      msIO_fprintf(stream, "%.15g %.15g\n", feature->line[i].point[j].x,
3✔
1175
                   feature->line[i].point[j].y);
3✔
1176
    }
1177
    writeBlockEnd(stream, indent, "POINTS");
3✔
1178
  }
1179
  indent--;
1180

1181
  if (feature->numvalues) {
3✔
1182
    writeIndent(stream, indent);
1183
    msIO_fprintf(stream, "ITEMS \"");
×
1184
    for (i = 0; i < feature->numvalues; i++) {
×
1185
      if (i == 0)
×
1186
        msIO_fprintf(stream, "%s", feature->values[i]);
×
1187
      else
1188
        msIO_fprintf(stream, ";%s", feature->values[i]);
×
1189
    }
1190
    msIO_fprintf(stream, "\"\n");
×
1191
  }
1192

1193
  writeString(stream, indent, "TEXT", NULL, feature->text);
3✔
1194
  writeBlockEnd(stream, indent, "FEATURE");
3✔
1195
}
3✔
1196

1197
void initGrid(graticuleObj *pGraticule) {
8✔
1198
  memset(pGraticule, 0, sizeof(graticuleObj));
1199
}
8✔
1200

1201
void freeGrid(graticuleObj *pGraticule) {
8✔
1202
  msFree(pGraticule->labelformat);
8✔
1203
  msFree(pGraticule->pboundingpoints);
8✔
1204
  msFree(pGraticule->pboundinglines);
8✔
1205
}
8✔
1206

1207
static int loadGrid(layerObj *pLayer) {
8✔
1208
  for (;;) {
1209
    switch (msyylex()) {
35✔
1210
    case (EOF):
×
1211
      msSetError(MS_EOFERR, NULL, "loadGrid()");
×
1212
      return (-1);
×
1213
    case (END):
1214
      return (0);
1215
    case (GRID):
1216
      break; /* for string loads */
1217
    case (LABELFORMAT):
7✔
1218
      if (getString(&(pLayer->grid->labelformat)) == MS_FAILURE) {
7✔
1219
        if (strcasecmp(msyystring_buffer, "DD") ==
×
1220
            0) /* DD triggers a symbol to be returned instead of a string so
1221
                  check for this special case */
1222
        {
1223
          msFree(pLayer->grid->labelformat);
×
1224
          pLayer->grid->labelformat = msStrdup("DD");
×
1225
        } else
1226
          return (-1);
1227
      }
1228
      break;
1229
    case (MINARCS):
×
1230
      if (getDouble(&(pLayer->grid->minarcs), MS_NUM_CHECK_GT, 0, -1) == -1)
×
1231
        return (-1);
1232
      break;
1233
    case (MAXARCS):
4✔
1234
      if (getDouble(&(pLayer->grid->maxarcs), MS_NUM_CHECK_GT, 0, -1) == -1)
4✔
1235
        return (-1);
1236
      break;
1237
    case (MININTERVAL):
4✔
1238
      if (getDouble(&(pLayer->grid->minincrement), MS_NUM_CHECK_GT, 0, -1) ==
4✔
1239
          -1)
1240
        return (-1);
1241
      break;
1242
    case (MAXINTERVAL):
8✔
1243
      if (getDouble(&(pLayer->grid->maxincrement), MS_NUM_CHECK_GT, 0, -1) ==
8✔
1244
          -1)
1245
        return (-1);
1246
      break;
1247
    case (MINSUBDIVIDE):
×
1248
      if (getDouble(&(pLayer->grid->minsubdivides), MS_NUM_CHECK_GT, 0, -1) ==
×
1249
          -1)
1250
        return (-1);
1251
      break;
1252
    case (MAXSUBDIVIDE):
4✔
1253
      if (getDouble(&(pLayer->grid->maxsubdivides), MS_NUM_CHECK_GT, 0, -1) ==
4✔
1254
          -1)
1255
        return (-1);
1256
      break;
1257
    default:
×
1258
      msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)", "loadGrid()",
×
1259
                 msyystring_buffer, msyylineno);
1260
      return (-1);
×
1261
    }
1262
  }
1263
}
1264

1265
static void writeGrid(FILE *stream, int indent, graticuleObj *pGraticule) {
×
1266
  if (!pGraticule)
×
1267
    return;
1268

1269
  indent++;
×
1270
  writeBlockBegin(stream, indent, "GRID");
×
1271
  writeString(stream, indent, "LABELFORMAT", NULL, pGraticule->labelformat);
×
1272
  writeNumber(stream, indent, "MAXARCS", 0, pGraticule->maxarcs);
×
1273
  writeNumber(stream, indent, "MAXSUBDIVIDE", 0, pGraticule->maxsubdivides);
×
1274
  writeNumber(stream, indent, "MAXINTERVAL", 0, pGraticule->maxincrement);
×
1275
  writeNumber(stream, indent, "MINARCS", 0, pGraticule->minarcs);
×
1276
  writeNumber(stream, indent, "MININTERVAL", 0, pGraticule->minincrement);
×
1277
  writeNumber(stream, indent, "MINSUBDIVIDE", 0, pGraticule->minsubdivides);
×
1278
  writeBlockEnd(stream, indent, "GRID");
×
1279
}
1280

1281
static int loadProjection(projectionObj *p, mapObj *map) {
11,455✔
1282
  p->gt.need_geotransform = MS_FALSE;
11,455✔
1283

1284
  if (p->proj != NULL || p->numargs != 0) {
11,455✔
1285
    msSetError(MS_MISCERR,
×
1286
               "Projection is already initialized. Multiple projection "
1287
               "definitions are not allowed in this object. (line %d)",
1288
               "loadProjection()", msyylineno);
1289
    return (-1);
×
1290
  }
1291

1292
  for (;;) {
1293
    switch (msyylex()) {
23,229✔
1294
    case (EOF):
×
1295
      msSetError(MS_EOFERR, NULL, "loadProjection()");
×
1296
      return (-1);
×
1297
    case (END):
11,455✔
1298
      if (p->numargs == 1 && strstr(p->args[0], "+") != NULL) {
11,455✔
1299
        char *one_line_def = p->args[0];
1300
        int result;
1301

1302
        p->args[0] = NULL;
568✔
1303
        p->numargs = 0;
568✔
1304
        result = msLoadProjectionString(p, one_line_def);
568✔
1305
        free(one_line_def);
568✔
1306
        return result;
568✔
1307
      } else {
1308
        if (p->numargs != 0) {
10,887✔
1309
          if (map && map->numlayers > 0) {
10,887✔
1310
            layerObj *prevLayer = GET_LAYER(map, map->numlayers - 1);
6,900✔
1311
            if (prevLayer->projection.numargs == p->numargs) {
6,900✔
1312
              bool same = true;
1313
              for (int i = 0; i < p->numargs; ++i) {
13,393✔
1314
                if (strcmp(p->args[i], prevLayer->projection.args[i]) != 0) {
6,951✔
1315
                  same = false;
1316
                  break;
1317
                }
1318
              }
1319
              if (same) {
6,885✔
1320
                return msCloneProjectionFrom(p, &(prevLayer->projection));
6,442✔
1321
              }
1322
            }
1323
          }
1324

1325
          return msProcessProjection(p);
4,445✔
1326
        } else
1327
          return 0;
1328
      }
1329
      break;
1330
    case (MS_STRING):
11,774✔
1331
    case (MS_AUTO):
1332
      if (p->numargs == MS_MAXPROJARGS) {
11,774✔
1333
        msSetError(MS_MISCERR,
×
1334
                   "Parsing error near (%s):(line %d): Too many arguments in "
1335
                   "projection string",
1336
                   "loadProjection()", msyystring_buffer, msyylineno);
1337
        return -1;
×
1338
      }
1339
      p->args[p->numargs] = msStrdup(msyystring_buffer);
11,774✔
1340
      p->numargs++;
11,774✔
1341
      break;
1342
    default:
×
1343
      msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
1344
                 "loadProjection()", msyystring_buffer, msyylineno);
1345
      return (-1);
×
1346
    }
1347
  } /* next token */
1348
}
1349

1350
/************************************************************************/
1351
/*                     msLoadProjectionStringEPSGLike                   */
1352
/************************************************************************/
1353

1354
static int msLoadProjectionStringEPSGLike(projectionObj *p, const char *value,
5,874✔
1355
                                          const char *pszPrefix,
1356
                                          int bFollowEPSGAxisOrder) {
1357
  size_t buffer_size = 0;
1358
  char *init_string = NULL;
1359
  const char *code;
1360
  const char *next_sep;
1361
  size_t prefix_len;
1362

1363
  prefix_len = strlen(pszPrefix);
5,874✔
1364
  if (strncasecmp(value, pszPrefix, prefix_len) != 0)
5,874✔
1365
    return -1;
1366

1367
  code = value + prefix_len;
5,174✔
1368
  next_sep = strchr(code, pszPrefix[prefix_len - 1]);
5,174✔
1369
  if (next_sep != NULL)
5,174✔
1370
    code = next_sep + 1;
99✔
1371

1372
  buffer_size = 10 + strlen(code) + 1;
5,174✔
1373
  init_string = (char *)msSmallMalloc(buffer_size);
5,174✔
1374

1375
  /* translate into PROJ.4 format. */
1376
  snprintf(init_string, buffer_size, "init=epsg:%s", code);
1377

1378
  p->args = (char **)msSmallMalloc(sizeof(char *) * 2);
5,174✔
1379
  p->args[0] = init_string;
5,174✔
1380
  p->numargs = 1;
5,174✔
1381

1382
  if (bFollowEPSGAxisOrder && msIsAxisInverted(atoi(code))) {
8,612✔
1383
    p->args[1] = msStrdup("+epsgaxis=ne");
3,132✔
1384
    p->numargs = 2;
3,132✔
1385
  }
1386

1387
  return 0;
1388
}
1389

1390
/************************************************************************/
1391
/*                     msLoadProjectionStringCRSLike                    */
1392
/************************************************************************/
1393

1394
static int msLoadProjectionStringCRSLike(projectionObj *p, const char *value,
254✔
1395
                                         const char *pszPrefix) {
1396
  char init_string[100];
1397
  const char *id;
1398
  const char *next_sep;
1399
  size_t prefix_len;
1400

1401
  prefix_len = strlen(pszPrefix);
254✔
1402
  if (strncasecmp(value, pszPrefix, prefix_len) != 0)
254✔
1403
    return -1;
1404

1405
  id = value + prefix_len;
14✔
1406
  next_sep = strchr(id, pszPrefix[prefix_len - 1]);
14✔
1407
  if (next_sep != NULL)
14✔
1408
    id = next_sep + 1;
6✔
1409

1410
  init_string[0] = '\0';
14✔
1411

1412
  if (strcasecmp(id, "84") == 0 || strcasecmp(id, "CRS84") == 0)
14✔
1413
    strncpy(init_string, "init=epsg:4326", sizeof(init_string));
1414
  else if (strcasecmp(id, "83") == 0 || strcasecmp(id, "CRS83") == 0)
×
1415
    strncpy(init_string, "init=epsg:4269", sizeof(init_string));
1416
  else if (strcasecmp(id, "27") == 0 || strcasecmp(id, "CRS27") == 0)
×
1417
    strncpy(init_string, "init=epsg:4267", sizeof(init_string));
1418
  else {
1419
    msSetError(MS_PROJERR, "Unrecognised OGC CRS def '%s'.",
×
1420
               "msLoadProjectionString()", value);
1421
    return -1;
×
1422
  }
1423

1424
  p->args = (char **)msSmallMalloc(sizeof(char *) * 2);
14✔
1425
  p->args[0] = msStrdup(init_string);
14✔
1426
  p->numargs = 1;
14✔
1427

1428
  return 0;
14✔
1429
}
1430

1431
/************************************************************************/
1432
/*                         msLoadProjectionStringEPSG                   */
1433
/*                                                                      */
1434
/*      Checks for EPSG type projection and set the axes for a          */
1435
/*      certain code ranges.                                            */
1436
/*      Use for now in WMS 1.3.0 and WFS >= 1.1.0                       */
1437
/************************************************************************/
1438
int msLoadProjectionStringEPSG(projectionObj *p, const char *value) {
3,367✔
1439
  assert(p);
1440

1441
  msFreeProjectionExceptContext(p);
3,367✔
1442

1443
  p->gt.need_geotransform = MS_FALSE;
3,367✔
1444
#ifdef USE_PROJ_FASTPATHS
1445
  if (strcasestr(value, "epsg:4326")) {
1446
    p->wellknownprojection = wkp_lonlat;
1447
  } else if (strcasestr(value, "epsg:3857")) {
1448
    p->wellknownprojection = wkp_gmerc;
1449
  } else {
1450
    p->wellknownprojection = wkp_none;
1451
  }
1452
#endif
1453

1454
  if (msLoadProjectionStringEPSGLike(p, value, "EPSG:", MS_TRUE) == 0) {
3,367✔
1455
    return msProcessProjection(p);
3,329✔
1456
  }
1457

1458
  return msLoadProjectionString(p, value);
38✔
1459
}
1460

1461
int msLoadProjectionCodeString(projectionObj *p, const char *value) {
64✔
1462

1463
  int num_params = 0;
64✔
1464

1465
  // exit if init= is already at the start of the string e.g. from
1466
  // msOGRSpatialRef2ProjectionObj
1467
  if (strncasecmp(value, "init=", 5) == 0) {
64✔
1468
    return -1;
1469
  }
1470

1471
  if (!strchr(value, ':')) {
11✔
1472
    return -1;
1473
  }
1474

1475
  char **papszList = msStringSplit(value, ':', &(num_params));
10✔
1476

1477
  if (num_params != 2) {
10✔
1478
    msFreeCharArray(papszList, num_params);
×
1479
    return -1;
×
1480
  }
1481

1482
  const size_t buffer_size = 5 + strlen(value) + 1;
10✔
1483
  char *init_string = (char *)msSmallMalloc(buffer_size);
10✔
1484

1485
  /* translate into PROJ format. */
1486
  snprintf(init_string, buffer_size, "init=%s:%s", papszList[0], papszList[1]);
10✔
1487

1488
  p->args = (char **)msSmallMalloc(sizeof(char *));
10✔
1489
  p->args[0] = init_string;
10✔
1490
  p->numargs = 1;
10✔
1491

1492
  msFreeCharArray(papszList, num_params);
10✔
1493

1494
  return 0;
10✔
1495
}
1496

1497
int msLoadProjectionString(projectionObj *p, const char *value) {
3,063✔
1498
  assert(p);
1499
  p->gt.need_geotransform = MS_FALSE;
3,063✔
1500

1501
  msFreeProjectionExceptContext(p);
3,063✔
1502

1503
  /*
1504
   * Handle new style definitions, the same as they would be given to
1505
   * the proj program.
1506
   * eg.
1507
   *    "+proj=utm +zone=11 +ellps=WGS84"
1508
   */
1509
  if (value[0] == '+') {
3,063✔
1510
    char *trimmed;
1511
    int i, i_out = 0;
1512

1513
    trimmed = msStrdup(value + 1);
1,140✔
1514
    for (i = 1; value[i] != '\0'; i++) {
37,787✔
1515
      if (!isspace(value[i]))
36,647✔
1516
        trimmed[i_out++] = value[i];
34,588✔
1517
    }
1518
    trimmed[i_out] = '\0';
1,140✔
1519

1520
    p->args = msStringSplit(trimmed, '+', &p->numargs);
1,140✔
1521
    free(trimmed);
1,140✔
1522
  } else if (strncasecmp(value, "AUTO:", 5) == 0 ||
1,923✔
1523
             strncasecmp(value, "AUTO2:", 6) == 0) {
1,923✔
1524
    /* WMS/WFS AUTO projection: "AUTO:proj_id,units_id,lon0,lat0" */
1525
    /* WMS 1.3.0 projection: "AUTO2:auto_crs_id,factor,lon0,lat0"*/
1526
    /* Keep the projection defn into a single token for writeProjection() */
1527
    /* to work fine. */
1528
    p->args = (char **)msSmallMalloc(sizeof(char *));
×
1529
    p->args[0] = msStrdup(value);
×
1530
    p->numargs = 1;
×
1531
  } else if (msLoadProjectionStringEPSGLike(p, value, "EPSG:", MS_FALSE) == 0) {
1,923✔
1532
    /* Assume long/lat ordering. Use msLoadProjectionStringEPSG() if wanting to
1533
     * follow EPSG axis */
1534
  } else if (msLoadProjectionStringEPSGLike(
190✔
1535
                 p, value, "urn:ogc:def:crs:EPSG:", MS_TRUE) == 0) {
1536
  } else if (msLoadProjectionStringEPSGLike(
111✔
1537
                 p, value, "urn:EPSG:geographicCRS:", MS_TRUE) == 0) {
1538
  } else if (msLoadProjectionStringEPSGLike(
107✔
1539
                 p, value, "urn:x-ogc:def:crs:EPSG:", MS_TRUE) == 0) {
1540
    /*this case is to account for OGC CITE tests where x-ogc was used
1541
      before the ogc name became an official NID. Note also we also account
1542
      for the fact that a space for the version of the espg is not used with
1543
      CITE tests. (Syntax used could be urn:ogc:def:objectType:authority:code)*/
1544
  } else if (msLoadProjectionStringCRSLike(p, value, "urn:ogc:def:crs:OGC:") ==
107✔
1545
             0) {
1546
  } else if (msLoadProjectionStringEPSGLike(
101✔
1547
                 p, value, "http://www.opengis.net/def/crs/EPSG/", MS_TRUE) ==
1548
             0) {
1549
    /* URI projection support */
1550
  } else if (msLoadProjectionStringCRSLike(
75✔
1551
                 p, value, "http://www.opengis.net/def/crs/OGC/") == 0) {
1552
    /* Mandatory support for this URI format specified in WFS1.1 (also in 1.0?)
1553
     */
1554
  } else if (msLoadProjectionStringEPSGLike(
75✔
1555
                 p, value, "http://www.opengis.net/gml/srs/epsg.xml#",
1556
                 MS_FALSE) == 0) {
1557
    /* We assume always long/lat ordering, as that is what GeoServer does... */
1558
  } else if (msLoadProjectionStringCRSLike(p, value, "CRS:") == 0) {
72✔
1559
  } else if (msLoadProjectionCodeString(p, value) == 0) {
64✔
1560
    /* allow strings in the form AUTH:XXXX */
1561
  }
1562
  /*
1563
   * Handle old style comma delimited.  eg. "proj=utm,zone=11,ellps=WGS84".
1564
   */
1565
  else {
1566
    p->args = msStringSplit(value, ',', &p->numargs);
54✔
1567
  }
1568

1569
  return msProcessProjection(p);
3,063✔
1570
}
1571

1572
static void writeProjection(FILE *stream, int indent, projectionObj *p) {
8✔
1573
  int i;
1574

1575
  if (!p || p->numargs <= 0)
8✔
1576
    return;
1577
  indent++;
8✔
1578
  writeBlockBegin(stream, indent, "PROJECTION");
8✔
1579
  for (i = 0; i < p->numargs; i++)
16✔
1580
    writeString(stream, indent, NULL, NULL, p->args[i]);
8✔
1581
  writeBlockEnd(stream, indent, "PROJECTION");
8✔
1582
}
1583

1584
void initLeader(labelLeaderObj *leader) {
16✔
1585
  leader->gridstep = 5;
16✔
1586
  leader->maxdistance = 0;
16✔
1587

1588
  /* Set maxstyles = 0, styles[] will be allocated as needed on first call
1589
   * to msGrowLabelLeaderStyles()
1590
   */
1591
  leader->numstyles = leader->maxstyles = 0;
16✔
1592
  leader->styles = NULL;
16✔
1593
}
16✔
1594

1595
/*
1596
** Initialize, load and free a labelObj structure
1597
*/
1598
void initLabel(labelObj *label) {
18,939✔
1599
  int i;
1600

1601
  MS_REFCNT_INIT(label);
18,939✔
1602

1603
  label->align = MS_ALIGN_DEFAULT;
18,939✔
1604
  MS_INIT_COLOR(label->color, 0, 0, 0, 255);
18,939✔
1605
  MS_INIT_COLOR(label->outlinecolor, -1, -1, -1, 255); /* don't use it */
18,939✔
1606
  label->outlinewidth = 1;
18,939✔
1607

1608
  MS_INIT_COLOR(label->shadowcolor, -1, -1, -1, 255); /* don't use it */
18,939✔
1609
  label->shadowsizex = label->shadowsizey = 1;
18,939✔
1610

1611
  label->font = NULL;
18,939✔
1612
  label->size = MS_MEDIUM;
18,939✔
1613

1614
  label->position = MS_CC;
18,939✔
1615
  label->angle = 0;
18,939✔
1616
  label->anglemode = MS_NONE;
18,939✔
1617
  label->minsize = MS_MINFONTSIZE;
18,939✔
1618
  label->maxsize = MS_MAXFONTSIZE;
18,939✔
1619
  label->buffer = 0;
18,939✔
1620
  label->offsetx = label->offsety = 0;
18,939✔
1621
  label->minscaledenom = -1;
18,939✔
1622
  label->maxscaledenom = -1;
18,939✔
1623
  label->minfeaturesize = -1; /* no limit */
18,939✔
1624
  label->autominfeaturesize = MS_FALSE;
18,939✔
1625
  label->mindistance = -1;       /* no limit */
18,939✔
1626
  label->repeatdistance = 0;     /* no repeat */
18,939✔
1627
  label->maxoverlapangle = 22.5; /* default max overlap angle */
18,939✔
1628
  label->partials = MS_FALSE;
18,939✔
1629
  label->wrap = '\0';
18,939✔
1630
  label->maxlength = 0;
18,939✔
1631
  label->space_size_10 = 0.0;
18,939✔
1632

1633
  label->encoding = NULL;
18,939✔
1634

1635
  label->force = MS_OFF;
18,939✔
1636
  label->priority = MS_DEFAULT_LABEL_PRIORITY;
18,939✔
1637

1638
  /* Set maxstyles = 0, styles[] will be allocated as needed on first call
1639
   * to msGrowLabelStyles()
1640
   */
1641
  label->numstyles = label->maxstyles = 0;
18,939✔
1642
  label->styles = NULL;
18,939✔
1643

1644
  label->numbindings = 0;
18,939✔
1645
  label->nexprbindings = 0;
18,939✔
1646
  for (i = 0; i < MS_LABEL_BINDING_LENGTH; i++) {
246,207✔
1647
    label->bindings[i].item = NULL;
227,268✔
1648
    label->bindings[i].index = -1;
227,268✔
1649
    msInitExpression(&(label->exprBindings[i]));
227,268✔
1650
  }
1651

1652
  msInitExpression(&(label->expression));
18,939✔
1653
  msInitExpression(&(label->text));
18,939✔
1654

1655
  label->leader = NULL;
18,939✔
1656

1657
  label->sizeunits = MS_INHERIT;
18,939✔
1658
  label->scalefactor = 1.0;
18,939✔
1659

1660
  return;
18,939✔
1661
}
1662

1663
int freeLabelLeader(labelLeaderObj *leader) {
16✔
1664
  int i;
1665
  for (i = 0; i < leader->numstyles; i++) {
32✔
1666
    if (freeStyle(leader->styles[i]) == MS_SUCCESS) {
16✔
1667
      msFree(leader->styles[i]);
16✔
1668
    }
1669
  }
1670
  msFree(leader->styles);
16✔
1671

1672
  return MS_SUCCESS;
16✔
1673
}
1674
int freeLabel(labelObj *label) {
22,430✔
1675
  int i;
1676

1677
  if (MS_REFCNT_DECR_IS_NOT_ZERO(label)) {
22,430✔
1678
    return MS_FAILURE;
1679
  }
1680

1681
  msFree(label->font);
18,862✔
1682
  msFree(label->encoding);
18,862✔
1683

1684
  for (i = 0; i < label->numstyles; i++) { /* each style */
24,675✔
1685
    if (label->styles[i] != NULL) {
5,813✔
1686
      if (freeStyle(label->styles[i]) == MS_SUCCESS) {
5,813✔
1687
        msFree(label->styles[i]);
5,809✔
1688
      }
1689
    }
1690
  }
1691
  msFree(label->styles);
18,862✔
1692

1693
  for (i = 0; i < MS_LABEL_BINDING_LENGTH; i++) {
245,206✔
1694
    msFree(label->bindings[i].item);
226,344✔
1695
    msFreeExpression(&(label->exprBindings[i]));
226,344✔
1696
  }
1697

1698
  msFreeExpression(&(label->expression));
18,862✔
1699
  msFreeExpression(&(label->text));
18,862✔
1700

1701
  if (label->leader) {
18,862✔
1702
    freeLabelLeader(label->leader);
×
1703
    msFree(label->leader);
×
1704
    label->leader = NULL;
×
1705
  }
1706

1707
  return MS_SUCCESS;
1708
}
1709

1710
static int loadLeader(labelLeaderObj *leader) {
15✔
1711
  for (;;) {
1712
    switch (msyylex()) {
53✔
1713
    case (END):
1714
      return (0);
1715
      break;
1716
    case (EOF):
×
1717
      msSetError(MS_EOFERR, NULL, "loadLeader()");
×
1718
      return (-1);
×
1719
    case GRIDSTEP:
8✔
1720
      if (getInteger(&(leader->gridstep), MS_NUM_CHECK_GT, 0, -1) == -1)
8✔
1721
        return (-1);
1722
      break;
1723
    case MAXDISTANCE:
15✔
1724
      if (getInteger(&(leader->maxdistance), MS_NUM_CHECK_GT, 0, -1) == -1)
15✔
1725
        return (-1);
1726
      break;
1727
    case STYLE:
15✔
1728
      if (msGrowLeaderStyles(leader) == NULL)
15✔
1729
        return (-1);
1730
      initStyle(leader->styles[leader->numstyles]);
15✔
1731
      if (loadStyle(leader->styles[leader->numstyles]) != MS_SUCCESS) {
15✔
1732
        freeStyle(leader->styles[leader->numstyles]);
×
1733
        free(leader->styles[leader->numstyles]);
×
1734
        leader->styles[leader->numstyles] = NULL;
×
1735
        return -1;
×
1736
      }
1737
      leader->numstyles++;
15✔
1738
      break;
15✔
1739
    default:
×
1740
      if (strlen(msyystring_buffer) > 0) {
×
1741
        msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
1742
                   "loadLeader()", msyystring_buffer, msyylineno);
1743
        return (-1);
×
1744
      } else {
1745
        return (0); /* end of a string, not an error */
1746
      }
1747
    }
1748
  }
1749
}
1750

1751
static int loadLabel(labelObj *label) {
3,619✔
1752
  int symbol;
1753

1754
  for (;;) {
1755
    switch (msyylex()) {
20,972✔
1756
    case (ANGLE):
380✔
1757
      if ((symbol = getSymbol(5, MS_NUMBER, MS_AUTO, MS_AUTO2, MS_FOLLOW,
380✔
1758
                              MS_BINDING)) == -1)
1759
        return (-1);
1760

1761
      if (symbol == MS_NUMBER) {
380✔
1762
        if (msCheckNumber(msyynumber, MS_NUM_CHECK_RANGE, -360.0, 360.0) ==
290✔
1763
            MS_FAILURE) {
1764
          msSetError(MS_MISCERR,
×
1765
                     "Invalid ANGLE, must be between -360 and 360 (line %d)",
1766
                     "loadLabel()", msyylineno);
1767
          return (MS_FAILURE);
×
1768
        }
1769
        label->angle = (double)msyynumber;
290✔
1770
      } else if (symbol == MS_BINDING) {
90✔
1771
        if (label->bindings[MS_LABEL_BINDING_ANGLE].item != NULL)
×
1772
          msFree(label->bindings[MS_LABEL_BINDING_ANGLE].item);
×
1773
        label->bindings[MS_LABEL_BINDING_ANGLE].item =
×
1774
            msStrdup(msyystring_buffer);
×
1775
        label->numbindings++;
×
1776
      } else {
1777
        label->anglemode = symbol;
90✔
1778
      }
1779
      break;
1780
    case (ALIGN):
196✔
1781
      if ((symbol = getSymbol(4, MS_ALIGN_LEFT, MS_ALIGN_CENTER, MS_ALIGN_RIGHT,
196✔
1782
                              MS_BINDING)) == -1)
1783
        return (-1);
1784
      if ((symbol == MS_ALIGN_LEFT) || (symbol == MS_ALIGN_CENTER) ||
196✔
1785
          (symbol == MS_ALIGN_RIGHT)) {
1786
        label->align = symbol;
194✔
1787
      } else {
1788
        if (label->bindings[MS_LABEL_BINDING_ALIGN].item != NULL)
2✔
1789
          msFree(label->bindings[MS_LABEL_BINDING_ALIGN].item);
×
1790
        label->bindings[MS_LABEL_BINDING_ALIGN].item =
2✔
1791
            msStrdup(msyystring_buffer);
2✔
1792
        label->numbindings++;
2✔
1793
      }
1794
      break;
1795
    case (ANTIALIAS): /*ignore*/
4✔
1796
      msyylex();
4✔
1797
      break;
4✔
1798
    case (BUFFER):
677✔
1799
      if (getInteger(&(label->buffer), MS_NUM_CHECK_NONE, -1, -1) == -1)
677✔
1800
        return (-1);
1801
      break;
1802
    case (COLOR):
3,469✔
1803
      if (loadColor(&(label->color),
3,469✔
1804
                    &(label->bindings[MS_LABEL_BINDING_COLOR])) != MS_SUCCESS)
1805
        return (-1);
1806
      if (label->bindings[MS_LABEL_BINDING_COLOR].item)
3,469✔
1807
        label->numbindings++;
3✔
1808
      break;
1809
    case (ENCODING):
4✔
1810
      if ((getString(&label->encoding)) == MS_FAILURE)
4✔
1811
        return (-1);
1812
      break;
1813
    case (END):
1814
      return (0);
1815
      break;
1816
    case (EOF):
×
1817
      msSetError(MS_EOFERR, NULL, "loadLabel()");
×
1818
      return (-1);
×
1819
    case (EXPRESSION):
112✔
1820
      if (loadExpression(&(label->expression)) == -1)
112✔
1821
        return (-1); /* loadExpression() cleans up previously allocated
1822
                        expression */
1823
      break;
1824
    case (FONT):
2,047✔
1825
      if ((symbol = getSymbol(2, MS_STRING, MS_BINDING)) == -1)
2,047✔
1826
        return (-1);
1827

1828
      if (symbol == MS_STRING) {
2,047✔
1829
        if (label->font != NULL)
2,047✔
1830
          msFree(label->font);
×
1831
        label->font = msStrdup(msyystring_buffer);
2,047✔
1832
      } else {
1833
        if (label->bindings[MS_LABEL_BINDING_FONT].item != NULL)
×
1834
          msFree(label->bindings[MS_LABEL_BINDING_FONT].item);
×
1835
        label->bindings[MS_LABEL_BINDING_FONT].item =
×
1836
            msStrdup(msyystring_buffer);
×
1837
        label->numbindings++;
×
1838
      }
1839
      break;
1840
    case (FORCE):
200✔
1841
      switch (msyylex()) {
200✔
1842
      case MS_ON:
72✔
1843
        label->force = MS_ON;
72✔
1844
        break;
72✔
1845
      case MS_OFF:
×
1846
        label->force = MS_OFF;
×
1847
        break;
×
1848
      case GROUP:
128✔
1849
        label->force = MS_LABEL_FORCE_GROUP;
128✔
1850
        break;
128✔
1851
      default:
×
1852
        msSetError(MS_MISCERR,
×
1853
                   "Invalid FORCE, must be ON,OFF,or GROUP (line %d)",
1854
                   "loadLabel()", msyylineno);
1855
        return (-1);
×
1856
      }
1857
      break;
1858
    case (LABEL):
1859
      break; /* for string loads */
1860
    case (LEADER):
×
1861
      msSetError(MS_MISCERR,
×
1862
                 "LABEL LEADER not implemented. LEADER goes at the CLASS level "
1863
                 "(line %d)",
1864
                 "loadLabel()", msyylineno);
1865
      return (-1);
×
1866
    case (MAXSIZE):
×
1867
      if (getInteger(&(label->maxsize), MS_NUM_CHECK_GT, 0, -1) == -1)
×
1868
        return (-1);
1869
      break;
1870
    case (MAXSCALEDENOM):
×
1871
      if (getDouble(&(label->maxscaledenom), MS_NUM_CHECK_GTE, 0, -1) == -1)
×
1872
        return (-1);
1873
      break;
1874
    case (MAXLENGTH):
266✔
1875
      if (getInteger(&(label->maxlength), MS_NUM_CHECK_GT, 0, -1) == -1)
266✔
1876
        return (-1);
1877
      break;
1878
    case (MINDISTANCE):
20✔
1879
      if (getInteger(&(label->mindistance), MS_NUM_CHECK_GT, 0, -1) == -1)
20✔
1880
        return (-1);
1881
      break;
1882
    case (REPEATDISTANCE):
12✔
1883
      if (getInteger(&(label->repeatdistance), MS_NUM_CHECK_GT, 0, -1) == -1)
12✔
1884
        return (-1);
1885
      break;
1886
    case (MAXOVERLAPANGLE):
43✔
1887
      if (getDouble(&(label->maxoverlapangle), MS_NUM_CHECK_RANGE, 0, 360) ==
43✔
1888
          -1)
1889
        return (-1);
1890
      break;
1891
    case (MINFEATURESIZE):
×
1892
      if ((symbol = getSymbol(2, MS_NUMBER, MS_AUTO)) == -1)
×
1893
        return (-1);
1894
      if (symbol == MS_NUMBER) {
×
1895
        if (msCheckNumber(msyynumber, MS_NUM_CHECK_GT, 0, -1) == MS_FAILURE) {
×
1896
          msSetError(MS_MISCERR,
×
1897
                     "Invalid MINFEATURESIZE, must be greater than 0 (line %d)",
1898
                     "loadLabel()", msyylineno);
1899
          return (MS_FAILURE);
×
1900
        }
1901
        label->minfeaturesize = (int)msyynumber;
×
1902
      } else
1903
        label->autominfeaturesize = MS_TRUE;
×
1904
      break;
1905
    case (MINSCALEDENOM):
×
1906
      if (getDouble(&(label->minscaledenom), MS_NUM_CHECK_GTE, 0, -1) == -1)
×
1907
        return (-1);
1908
      break;
1909
    case (MINSIZE):
×
1910
      if (getInteger(&(label->minsize), MS_NUM_CHECK_GT, 0, -1) == -1)
×
1911
        return (-1);
1912
      break;
1913
    case (OFFSET):
98✔
1914
      if ((symbol = getSymbol(2, MS_NUMBER, MS_BINDING)) == -1)
98✔
1915
        return (MS_FAILURE);
1916
      if (symbol == MS_NUMBER)
98✔
1917
        label->offsetx = (int)msyynumber; // any integer ok
96✔
1918
      else {
1919
        if (label->bindings[MS_LABEL_BINDING_OFFSET_X].item != NULL)
2✔
1920
          msFree(label->bindings[MS_LABEL_BINDING_OFFSET_X].item);
×
1921
        label->bindings[MS_LABEL_BINDING_OFFSET_X].item =
2✔
1922
            msStrdup(msyystring_buffer);
2✔
1923
        label->numbindings++;
2✔
1924
      }
1925

1926
      if ((symbol = getSymbol(2, MS_NUMBER, MS_BINDING)) == -1)
98✔
1927
        return (MS_FAILURE);
1928
      if (symbol == MS_NUMBER)
98✔
1929
        label->offsety = (int)msyynumber; // any integer ok
96✔
1930
      else {
1931
        if (label->bindings[MS_LABEL_BINDING_OFFSET_Y].item != NULL)
2✔
1932
          msFree(label->bindings[MS_LABEL_BINDING_OFFSET_Y].item);
×
1933
        label->bindings[MS_LABEL_BINDING_OFFSET_Y].item =
2✔
1934
            msStrdup(msyystring_buffer);
2✔
1935
        label->numbindings++;
2✔
1936
      }
1937
      break;
1938
    case (OUTLINECOLOR):
579✔
1939
      if (loadColor(&(label->outlinecolor),
579✔
1940
                    &(label->bindings[MS_LABEL_BINDING_OUTLINECOLOR])) !=
1941
          MS_SUCCESS)
1942
        return (-1);
1943
      if (label->bindings[MS_LABEL_BINDING_OUTLINECOLOR].item)
579✔
1944
        label->numbindings++;
×
1945
      break;
1946
    case (OUTLINEWIDTH):
142✔
1947
      if (getInteger(&(label->outlinewidth), MS_NUM_CHECK_GT, 0, -1) == -1)
142✔
1948
        return (-1);
1949
      break;
1950
    case (PARTIALS):
646✔
1951
      if ((label->partials = getSymbol(2, MS_TRUE, MS_FALSE)) == -1)
646✔
1952
        return (-1);
1953
      break;
1954
    case (POSITION):
1,485✔
1955
      if ((label->position =
1,485✔
1956
               getSymbol(11, MS_UL, MS_UC, MS_UR, MS_CL, MS_CC, MS_CR, MS_LL,
1,485✔
1957
                         MS_LC, MS_LR, MS_AUTO, MS_BINDING)) == -1)
1958
        return (-1);
1959
      if (label->position == MS_BINDING) {
1,485✔
1960
        if (label->bindings[MS_LABEL_BINDING_POSITION].item != NULL)
×
1961
          msFree(label->bindings[MS_LABEL_BINDING_POSITION].item);
×
1962
        label->bindings[MS_LABEL_BINDING_POSITION].item =
×
1963
            msStrdup(msyystring_buffer);
×
1964
        label->numbindings++;
×
1965
      }
1966
      break;
1967
    case (PRIORITY):
20✔
1968
      if (label->exprBindings[MS_LABEL_BINDING_PRIORITY].string) {
20✔
1969
        msFreeExpression(&label->exprBindings[MS_LABEL_BINDING_PRIORITY]);
3✔
1970
        label->nexprbindings--;
3✔
1971
      }
1972

1973
      if ((symbol = getSymbol(3, MS_EXPRESSION, MS_NUMBER, MS_BINDING)) == -1)
20✔
1974
        return (-1);
1975
      if (symbol == MS_NUMBER) {
20✔
1976
        if (msCheckNumber(msyynumber, MS_NUM_CHECK_RANGE, 1,
11✔
1977
                          MS_MAX_LABEL_PRIORITY) == MS_FAILURE) {
1978
          msSetError(
×
1979
              MS_MISCERR,
1980
              "Invalid PRIORITY, must be an integer between 1 and %d (line %d)",
1981
              "loadLabel()", MS_MAX_LABEL_PRIORITY, msyylineno);
1982
          return (-1);
×
1983
        }
1984
        label->priority = (int)msyynumber;
11✔
1985
      } else if (symbol == MS_EXPRESSION) {
9✔
1986
        msFree(label->exprBindings[MS_LABEL_BINDING_PRIORITY].string);
6✔
1987
        label->exprBindings[MS_LABEL_BINDING_PRIORITY].string =
6✔
1988
            msStrdup(msyystring_buffer);
6✔
1989
        label->exprBindings[MS_LABEL_BINDING_PRIORITY].type = MS_EXPRESSION;
6✔
1990
        label->nexprbindings++;
6✔
1991
      } else {
1992
        if (label->bindings[MS_LABEL_BINDING_PRIORITY].item != NULL)
3✔
1993
          msFree(label->bindings[MS_LABEL_BINDING_PRIORITY].item);
×
1994
        label->bindings[MS_LABEL_BINDING_PRIORITY].item =
3✔
1995
            msStrdup(msyystring_buffer);
3✔
1996
        label->numbindings++;
3✔
1997
      }
1998
      break;
1999
    case (SHADOWCOLOR):
×
2000
      if (loadColor(&(label->shadowcolor), NULL) != MS_SUCCESS)
×
2001
        return (-1);
2002
      break;
2003
    case (SHADOWSIZE):
×
2004
      if ((symbol = getSymbol(2, MS_NUMBER, MS_BINDING)) == -1)
×
2005
        return (-1);
2006
      if (symbol == MS_NUMBER) {
×
2007
        label->shadowsizex = (int)msyynumber; // x offset, any int ok
×
2008
      } else {
2009
        if (label->bindings[MS_LABEL_BINDING_SHADOWSIZEX].item != NULL)
×
2010
          msFree(label->bindings[MS_LABEL_BINDING_SHADOWSIZEX].item);
×
2011
        label->bindings[MS_LABEL_BINDING_SHADOWSIZEX].item =
×
2012
            msStrdup(msyystring_buffer);
×
2013
        label->numbindings++;
×
2014
      }
2015

2016
      if ((symbol = getSymbol(2, MS_NUMBER, MS_BINDING)) == -1)
×
2017
        return (-1);
2018
      if (symbol == MS_NUMBER) {
×
2019
        label->shadowsizey = (int)msyynumber; // y offset, any int ok
×
2020
      } else {
2021
        if (label->bindings[MS_LABEL_BINDING_SHADOWSIZEY].item != NULL)
×
2022
          msFree(label->bindings[MS_LABEL_BINDING_SHADOWSIZEY].item);
×
2023
        label->bindings[MS_LABEL_BINDING_SHADOWSIZEY].item =
×
2024
            msStrdup(msyystring_buffer);
×
2025
        label->numbindings++;
×
2026
      }
2027
      break;
2028
    case (SIZE):
2,692✔
2029
      if (label->bindings[MS_LABEL_BINDING_SIZE].item) {
2,692✔
2030
        msFree(label->bindings[MS_LABEL_BINDING_SIZE].item);
×
2031
        label->bindings[MS_LABEL_BINDING_SIZE].item = NULL;
×
2032
        label->numbindings--;
×
2033
      }
2034
      if (label->exprBindings[MS_LABEL_BINDING_SIZE].string) {
2,692✔
2035
        msFreeExpression(&label->exprBindings[MS_LABEL_BINDING_SIZE]);
×
2036
        label->nexprbindings--;
×
2037
      }
2038

2039
      if ((symbol = getSymbol(8, MS_EXPRESSION, MS_NUMBER, MS_BINDING, MS_TINY,
2,692✔
2040
                              MS_SMALL, MS_MEDIUM, MS_LARGE, MS_GIANT)) == -1)
2041
        return (-1);
2042

2043
      if (symbol == MS_NUMBER) {
2,692✔
2044
        if (msCheckNumber(msyynumber, MS_NUM_CHECK_GT, 0, -1) == MS_FAILURE) {
2,178✔
2045
          msSetError(MS_MISCERR,
×
2046
                     "Invalid SIZE, must be greater than 0 (line %d)",
2047
                     "loadLabel()", msyylineno);
2048
          return (-1);
×
2049
        }
2050
        label->size = (double)msyynumber;
2,178✔
2051
      } else if (symbol == MS_BINDING) {
514✔
2052
        label->bindings[MS_LABEL_BINDING_SIZE].item =
3✔
2053
            msStrdup(msyystring_buffer);
3✔
2054
        label->numbindings++;
3✔
2055
      } else if (symbol == MS_EXPRESSION) {
511✔
2056
        msFree(label->exprBindings[MS_LABEL_BINDING_SIZE].string);
2✔
2057
        label->exprBindings[MS_LABEL_BINDING_SIZE].string =
2✔
2058
            msStrdup(msyystring_buffer);
2✔
2059
        label->exprBindings[MS_LABEL_BINDING_SIZE].type = MS_EXPRESSION;
2✔
2060
        label->nexprbindings++;
2✔
2061
      } else
2062
        label->size = symbol;
509✔
2063
      break;
2064
    case (STYLE):
1,166✔
2065
      if (msGrowLabelStyles(label) == NULL)
1,166✔
2066
        return (-1);
2067
      initStyle(label->styles[label->numstyles]);
1,166✔
2068
      if (loadStyle(label->styles[label->numstyles]) != MS_SUCCESS) {
1,166✔
2069
        freeStyle(label->styles[label->numstyles]);
×
2070
        free(label->styles[label->numstyles]);
×
2071
        label->styles[label->numstyles] = NULL;
×
2072
        return (-1);
×
2073
      }
2074
      if (label->styles[label->numstyles]->_geomtransform.type ==
1,166✔
2075
          MS_GEOMTRANSFORM_NONE)
2076
        label->styles[label->numstyles]->_geomtransform.type =
98✔
2077
            MS_GEOMTRANSFORM_LABELPOINT; /* set a default, a marker? */
2078
      label->numstyles++;
1,166✔
2079
      break;
1,166✔
2080
    case (TEXT):
320✔
2081
      if (loadExpression(&(label->text)) == -1)
320✔
2082
        return (-1); /* loadExpression() cleans up previously allocated
2083
                        expression */
2084
      if ((label->text.type != MS_STRING) &&
320✔
2085
          (label->text.type != MS_EXPRESSION)) {
2086
        msSetError(
×
2087
            MS_MISCERR,
2088
            "Text expressions support constant or tagged replacement strings.",
2089
            "loadLabel()");
2090
        return (-1);
×
2091
      }
2092
      break;
2093
    case (TYPE):
2,518✔
2094
      if (getSymbol(2, MS_TRUETYPE, MS_BITMAP) == -1)
2,518✔
2095
        return (-1); /* ignore TYPE */
2096
      break;
2097
    case (WRAP):
257✔
2098
      if (getCharacter(&(label->wrap)) == -1)
257✔
2099
        return (-1);
2100
      break;
2101
    default:
×
2102
      if (strlen(msyystring_buffer) > 0) {
×
2103
        msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
2104
                   "loadLabel()", msyystring_buffer, msyylineno);
2105
        return (-1);
×
2106
      } else {
2107
        return (0); /* end of a string, not an error */
2108
      }
2109
    }
2110
  } /* next token */
2111
}
2112

2113
int msUpdateLabelFromString(labelObj *label, char *string) {
×
2114
  if (!label || !string)
×
2115
    return MS_FAILURE;
2116

2117
  msAcquireLock(TLOCK_PARSER);
×
2118

2119
  msyystate = MS_TOKENIZE_STRING;
×
2120
  msyystring = string;
×
2121
  msyylex(); /* sets things up, but doesn't process any tokens */
×
2122

2123
  msyylineno = 1; /* start at line 1 */
×
2124

2125
  if (loadLabel(label) == -1) {
×
2126
    msReleaseLock(TLOCK_PARSER);
×
2127
    return MS_FAILURE; /* parse error */
×
2128
    ;
2129
  }
2130

2131
  msyylex_destroy();
×
2132
  msReleaseLock(TLOCK_PARSER);
×
2133
  return MS_SUCCESS;
×
2134
}
2135

2136
static void writeLeader(FILE *stream, int indent, labelLeaderObj *leader) {
×
2137
  int i;
2138
  if (leader->maxdistance == 0 && leader->numstyles == 0) {
×
2139
    return;
2140
  }
2141
  indent++;
×
2142
  writeBlockBegin(stream, indent, "LEADER");
×
2143
  writeNumber(stream, indent, "MAXDISTANCE", 0, leader->maxdistance);
×
2144
  writeNumber(stream, indent, "GRIDSTEP", 5, leader->gridstep);
×
2145
  for (i = 0; i < leader->numstyles; i++)
×
2146
    writeStyle(stream, indent, leader->styles[i]);
×
2147

2148
  writeBlockEnd(stream, indent, "LEADER");
×
2149
}
2150

2151
static void writeLabel(FILE *stream, int indent, labelObj *label) {
6✔
2152
  int i;
2153
  colorObj c;
2154

2155
  if (label->size == -1)
6✔
2156
    return; /* there is no default label anymore */
×
2157

2158
  indent++;
6✔
2159
  writeBlockBegin(stream, indent, "LABEL");
6✔
2160

2161
  if (label->numbindings > 0 && label->bindings[MS_LABEL_BINDING_ANGLE].item)
6✔
2162
    writeAttributeBinding(stream, indent, "ANGLE",
×
2163
                          &(label->bindings[MS_LABEL_BINDING_ANGLE]));
2164
  else
2165
    writeNumberOrKeyword(stream, indent, "ANGLE", 0, label->angle,
6✔
2166
                         label->anglemode, 3, MS_FOLLOW, "FOLLOW", MS_AUTO,
6✔
2167
                         "AUTO", MS_AUTO2, "AUTO2");
2168

2169
  writeExpression(stream, indent, "EXPRESSION", &(label->expression));
6✔
2170

2171
  if (label->numbindings > 0 && label->bindings[MS_LABEL_BINDING_FONT].item)
6✔
2172
    writeAttributeBinding(stream, indent, "FONT",
×
2173
                          &(label->bindings[MS_LABEL_BINDING_FONT]));
2174
  else
2175
    writeString(stream, indent, "FONT", NULL, label->font);
6✔
2176

2177
  writeNumber(stream, indent, "MAXSIZE", MS_MAXFONTSIZE, label->maxsize);
6✔
2178
  writeNumber(stream, indent, "MINSIZE", MS_MINFONTSIZE, label->minsize);
6✔
2179

2180
  if (label->numbindings > 0 && label->bindings[MS_LABEL_BINDING_SIZE].item)
6✔
2181
    writeAttributeBinding(stream, indent, "SIZE",
×
2182
                          &(label->bindings[MS_LABEL_BINDING_SIZE]));
2183
  else
2184
    writeNumber(stream, indent, "SIZE", -1, label->size);
6✔
2185

2186
  writeKeyword(stream, indent, "ALIGN", label->align, 3, MS_ALIGN_LEFT, "LEFT",
6✔
2187
               MS_ALIGN_CENTER, "CENTER", MS_ALIGN_RIGHT, "RIGHT");
2188
  writeNumber(stream, indent, "BUFFER", 0, label->buffer);
6✔
2189

2190
  if (label->numbindings > 0 && label->bindings[MS_LABEL_BINDING_COLOR].item)
6✔
2191
    writeAttributeBinding(stream, indent, "COLOR",
×
2192
                          &(label->bindings[MS_LABEL_BINDING_COLOR]));
2193
  else {
2194
    MS_INIT_COLOR(c, 0, 0, 0, 255);
6✔
2195
    writeColor(stream, indent, "COLOR", &c, &(label->color));
6✔
2196
  }
2197

2198
  writeString(stream, indent, "ENCODING", NULL, label->encoding);
6✔
2199
  if (label->leader)
6✔
2200
    writeLeader(stream, indent, label->leader);
×
2201
  writeKeyword(stream, indent, "FORCE", label->force, 2, MS_TRUE, "TRUE",
6✔
2202
               MS_LABEL_FORCE_GROUP, "GROUP");
2203
  writeNumber(stream, indent, "MAXLENGTH", 0, label->maxlength);
6✔
2204
  writeNumber(stream, indent, "MAXSCALEDENOM", -1, label->maxscaledenom);
6✔
2205
  writeNumber(stream, indent, "MINDISTANCE", -1, label->mindistance);
6✔
2206
  writeNumberOrKeyword(stream, indent, "MINFEATURESIZE", -1,
6✔
2207
                       label->minfeaturesize, 1, label->autominfeaturesize,
6✔
2208
                       MS_TRUE, "AUTO");
2209
  writeNumber(stream, indent, "MINSCALEDENOM", -1, label->minscaledenom);
6✔
2210
  writeDimension(stream, indent, "OFFSET", label->offsetx, label->offsety, NULL,
6✔
2211
                 NULL);
2212

2213
  if (label->numbindings > 0 &&
6✔
2214
      label->bindings[MS_LABEL_BINDING_OUTLINECOLOR].item)
×
2215
    writeAttributeBinding(stream, indent, "OUTLINECOLOR",
×
2216
                          &(label->bindings[MS_LABEL_BINDING_OUTLINECOLOR]));
2217
  else
2218
    writeColor(stream, indent, "OUTLINECOLOR", NULL, &(label->outlinecolor));
6✔
2219

2220
  writeNumber(stream, indent, "OUTLINEWIDTH", 1, label->outlinewidth);
6✔
2221
  writeKeyword(stream, indent, "PARTIALS", label->partials, 1, MS_TRUE, "TRUE");
6✔
2222

2223
  if (label->numbindings > 0 && label->bindings[MS_LABEL_BINDING_POSITION].item)
6✔
2224
    writeAttributeBinding(stream, indent, "POSITION",
×
2225
                          &(label->bindings[MS_LABEL_BINDING_POSITION]));
2226
  else
2227
    writeKeyword(stream, indent, "POSITION", label->position, 10, MS_UL, "UL",
6✔
2228
                 MS_UC, "UC", MS_UR, "UR", MS_CL, "CL", MS_CC, "CC", MS_CR,
2229
                 "CR", MS_LL, "LL", MS_LC, "LC", MS_LR, "LR", MS_AUTO, "AUTO");
2230

2231
  if (label->numbindings > 0 && label->bindings[MS_LABEL_BINDING_PRIORITY].item)
6✔
2232
    writeAttributeBinding(stream, indent, "PRIORITY",
×
2233
                          &(label->bindings[MS_LABEL_BINDING_PRIORITY]));
2234
  else if (label->nexprbindings > 0 &&
6✔
2235
           label->exprBindings[MS_LABEL_BINDING_PRIORITY].string)
1✔
2236
    writeExpression(stream, indent, "PRIORITY",
1✔
2237
                    &(label->exprBindings[MS_LABEL_BINDING_PRIORITY]));
2238
  else
2239
    writeNumber(stream, indent, "PRIORITY", MS_DEFAULT_LABEL_PRIORITY,
5✔
2240
                label->priority);
5✔
2241

2242
  writeNumber(stream, indent, "REPEATDISTANCE", 0, label->repeatdistance);
6✔
2243
  writeColor(stream, indent, "SHADOWCOLOR", NULL, &(label->shadowcolor));
6✔
2244
  writeDimension(stream, indent, "SHADOWSIZE", label->shadowsizex,
6✔
2245
                 label->shadowsizey,
6✔
2246
                 label->bindings[MS_LABEL_BINDING_SHADOWSIZEX].item,
2247
                 label->bindings[MS_LABEL_BINDING_SHADOWSIZEY].item);
2248

2249
  writeNumber(stream, indent, "MAXOVERLAPANGLE", 22.5, label->maxoverlapangle);
6✔
2250
  for (i = 0; i < label->numstyles; i++)
7✔
2251
    writeStyle(stream, indent, label->styles[i]);
1✔
2252

2253
  writeExpression(stream, indent, "TEXT", &(label->text));
6✔
2254

2255
  writeCharacter(stream, indent, "WRAP", '\0', label->wrap);
6✔
2256
  writeBlockEnd(stream, indent, "LABEL");
6✔
2257
}
2258

2259
char *msWriteLabelToString(labelObj *label) {
1✔
2260
  msIOContext context;
2261
  msIOBuffer buffer;
2262

2263
  context.label = NULL;
1✔
2264
  context.write_channel = MS_TRUE;
1✔
2265
  context.readWriteFunc = msIO_bufferWrite;
1✔
2266
  context.cbData = &buffer;
1✔
2267
  buffer.data = NULL;
1✔
2268
  buffer.data_len = 0;
1✔
2269
  buffer.data_offset = 0;
1✔
2270

2271
  msIO_installHandlers(NULL, &context, NULL);
1✔
2272

2273
  writeLabel(stdout, -1, label);
1✔
2274
  msIO_bufferWrite(&buffer, "", 1);
1✔
2275

2276
  msIO_installHandlers(NULL, NULL, NULL);
1✔
2277

2278
  return (char *)buffer.data;
1✔
2279
}
2280

2281
void msInitExpression(expressionObj *exp) {
1,413,827✔
2282
  memset(exp, 0, sizeof(*exp));
2283
  exp->type = MS_STRING;
1,413,827✔
2284
}
1,413,827✔
2285

2286
void msFreeExpressionTokens(expressionObj *exp) {
830,659✔
2287
  tokenListNodeObjPtr node = NULL;
2288
  tokenListNodeObjPtr nextNode = NULL;
2289

2290
  if (!exp)
830,659✔
2291
    return;
2292

2293
  if (exp->tokens) {
830,659✔
2294
    node = exp->tokens;
2295
    while (node != NULL) {
9,524✔
2296
      nextNode = node->next;
8,258✔
2297

2298
      msFree(node->tokensrc); /* not set very often */
8,258✔
2299

2300
      switch (node->token) {
8,258✔
2301
      case MS_TOKEN_BINDING_DOUBLE:
1,671✔
2302
      case MS_TOKEN_BINDING_INTEGER:
2303
      case MS_TOKEN_BINDING_STRING:
2304
      case MS_TOKEN_BINDING_TIME:
2305
        msFree(node->tokenval.bindval.item);
1,671✔
2306
        break;
1,671✔
2307
      case MS_TOKEN_LITERAL_TIME:
2308
        /* anything to do? */
2309
        break;
2310
      case MS_TOKEN_LITERAL_STRING:
289✔
2311
        msFree(node->tokenval.strval);
289✔
2312
        break;
289✔
2313
      case MS_TOKEN_LITERAL_SHAPE:
107✔
2314
        msFreeShape(node->tokenval.shpval);
107✔
2315
        free(node->tokenval.shpval);
107✔
2316
        break;
107✔
2317
      }
2318

2319
      msFree(node);
8,258✔
2320
      node = nextNode;
2321
    }
2322
    exp->tokens = exp->curtoken = NULL;
1,266✔
2323
  }
2324
}
2325

2326
void msFreeExpression(expressionObj *exp) {
708,846✔
2327
  if (!exp)
708,846✔
2328
    return;
2329
  msFree(exp->string);
708,846✔
2330
  msFree(exp->native_string);
708,846✔
2331
  if ((exp->type == MS_REGEX) && exp->compiled)
708,846✔
2332
    ms_regfree(&(exp->regex));
27✔
2333
  msFreeExpressionTokens(exp);
708,846✔
2334
  msInitExpression(exp); /* re-initialize */
708,846✔
2335
}
2336

2337
int loadExpression(expressionObj *exp) {
4,788✔
2338
  /* TODO: should we call msFreeExpression if exp->string != NULL? We do some
2339
   * checking to avoid a leak but is it enough... */
2340

2341
  msyystring_icase = MS_TRUE;
4,788✔
2342
  if ((exp->type = getSymbol(6, MS_STRING, MS_EXPRESSION, MS_REGEX, MS_ISTRING,
4,788✔
2343
                             MS_IREGEX, MS_LIST)) == -1)
2344
    return (-1);
2345
  if (exp->string != NULL) {
4,788✔
2346
    msFree(exp->string);
×
2347
    msFree(exp->native_string);
×
2348
  }
2349
  exp->string = msStrdup(msyystring_buffer);
4,788✔
2350
  exp->native_string = NULL;
4,788✔
2351

2352
  if (exp->type == MS_ISTRING) {
4,788✔
2353
    exp->flags = exp->flags | MS_EXP_INSENSITIVE;
19✔
2354
    exp->type = MS_STRING;
19✔
2355
  } else if (exp->type == MS_IREGEX) {
4,769✔
2356
    exp->flags = exp->flags | MS_EXP_INSENSITIVE;
19✔
2357
    exp->type = MS_REGEX;
19✔
2358
  }
2359

2360
  return (0);
2361
}
2362

2363
/* ---------------------------------------------------------------------------
2364
   msLoadExpressionString and loadExpressionString
2365

2366
   msLoadExpressionString wraps call to loadExpressionString with mutex
2367
   acquisition and release.  This function should be used everywhere outside
2368
   the mapfile loading phase of an application.  loadExpressionString does
2369
   not check for a mutex!  It should be used only within code that has
2370
   properly acquired a mutex.
2371

2372
   See bug 339 for more details -- SG.
2373
   ------------------------------------------------------------------------ */
2374

2375
int msLoadExpressionString(expressionObj *exp, const char *value) {
2,791✔
2376
  int retval = MS_FAILURE;
2377

2378
  msAcquireLock(TLOCK_PARSER);
2,791✔
2379
  retval = loadExpressionString(exp, value);
2,791✔
2380
  msReleaseLock(TLOCK_PARSER);
2,791✔
2381

2382
  return retval;
2,791✔
2383
}
2384

2385
int loadExpressionString(expressionObj *exp, const char *value) {
2,791✔
2386
  msyystate = MS_TOKENIZE_STRING;
2,791✔
2387
  msyystring = value;
2,791✔
2388
  msyylex(); /* sets things up but processes no tokens */
2,791✔
2389

2390
  msFreeExpression(exp); /* we're totally replacing the old expression so free
2,791✔
2391
                            (which re-inits) to start over */
2392

2393
  msyystring_icase = MS_TRUE;
2,791✔
2394
  if ((exp->type = getSymbol2(5, MS_EXPRESSION, MS_REGEX, MS_IREGEX, MS_ISTRING,
2,791✔
2395
                              MS_LIST)) != -1) {
2396
    exp->string = msStrdup(msyystring_buffer);
2,774✔
2397

2398
    if (exp->type == MS_ISTRING) {
2,774✔
2399
      exp->type = MS_STRING;
×
2400
      exp->flags = exp->flags | MS_EXP_INSENSITIVE;
×
2401
    } else if (exp->type == MS_IREGEX) {
2,774✔
2402
      exp->type = MS_REGEX;
×
2403
      exp->flags = exp->flags | MS_EXP_INSENSITIVE;
×
2404
    }
2405
  } else {
2406
    /* failure above is not an error since we'll consider anything not matching
2407
     * (like an unquoted number) as a STRING) */
2408
    exp->type = MS_STRING;
17✔
2409
    if ((strlen(value) - strlen(msyystring_buffer)) == 2)
17✔
2410
      exp->string = msStrdup(msyystring_buffer); /* value was quoted */
3✔
2411
    else
2412
      exp->string = msStrdup(value); /* use the whole value */
14✔
2413
  }
2414

2415
  return (0);
2,791✔
2416
}
2417

2418
/* msGetExpressionString()
2419
 *
2420
 * Returns the string representation of this expression, including delimiters
2421
 * and any flags (e.g. i = case-insensitive).
2422
 *
2423
 * Returns a newly allocated buffer that should be freed by the caller or NULL.
2424
 */
2425
char *msGetExpressionString(expressionObj *exp) {
14✔
2426
  if (exp->string) {
14✔
2427
    char *exprstring;
2428
    size_t buffer_size;
2429
    const char *case_insensitive = "";
2430

2431
    if (exp->flags & MS_EXP_INSENSITIVE)
12✔
2432
      case_insensitive = "i";
2433

2434
    /* Alloc buffer big enough for string + 2 delimiters + 'i' + \0 */
2435
    buffer_size = strlen(exp->string) + 4;
12✔
2436
    exprstring = (char *)msSmallMalloc(buffer_size);
12✔
2437

2438
    switch (exp->type) {
12✔
2439
    case (MS_REGEX):
1✔
2440
      snprintf(exprstring, buffer_size, "/%s/%s", exp->string,
1✔
2441
               case_insensitive);
2442
      return exprstring;
1✔
2443
    case (MS_STRING):
5✔
2444
      snprintf(exprstring, buffer_size, "\"%s\"%s", exp->string,
5✔
2445
               case_insensitive);
2446
      return exprstring;
5✔
2447
    case (MS_EXPRESSION):
6✔
2448
      snprintf(exprstring, buffer_size, "(%s)", exp->string);
6✔
2449
      return exprstring;
6✔
2450
    case (MS_LIST):
×
2451
      snprintf(exprstring, buffer_size, "{%s}", exp->string);
×
2452
      return exprstring;
×
2453
    default:
×
2454
      /* We should never get to here really! */
2455
      free(exprstring);
×
2456
      return NULL;
×
2457
    }
2458
  }
2459
  return NULL;
2460
}
2461

2462
static void writeExpression(FILE *stream, int indent, const char *name,
47✔
2463
                            expressionObj *exp) {
2464
  if (!exp || !exp->string)
47✔
2465
    return;
2466

2467
  writeIndent(stream, ++indent);
2468
  switch (exp->type) {
5✔
2469
  case (MS_LIST):
×
2470
    fprintf(stream, "%s {%s}", name, exp->string);
×
2471
    break;
2472
  case (MS_REGEX):
×
2473
    msIO_fprintf(stream, "%s /%s/", name, exp->string);
×
2474
    break;
×
2475
  case (MS_STRING):
4✔
2476
    msIO_fprintf(stream, "%s ", name);
4✔
2477
    writeStringElement(stream, exp->string);
4✔
2478
    break;
4✔
2479
  case (MS_EXPRESSION):
1✔
2480
    msIO_fprintf(stream, "%s (%s)", name, exp->string);
1✔
2481
    break;
1✔
2482
  }
2483
  if ((exp->type == MS_STRING || exp->type == MS_REGEX) &&
5✔
2484
      (exp->flags & MS_EXP_INSENSITIVE))
4✔
2485
    msIO_fprintf(stream, "i");
×
2486
  writeLineFeed(stream);
2487
}
2488

2489
int loadHashTable(hashTableObj *ptable) {
15,847✔
2490
  assert(ptable);
2491

2492
  for (;;) {
2493
    switch (msyylex()) {
111,153✔
2494
    case (EOF):
×
2495
      msSetError(MS_EOFERR, NULL, "loadHashTable()");
×
2496
      return (MS_FAILURE);
×
2497
    case (END):
2498
      return (MS_SUCCESS);
2499
    case (MS_STRING): {
95,306✔
2500
      char *data = NULL;
95,306✔
2501
      char *key =
2502
          msStrdup(msyystring_buffer); /* the key is *always* a string */
95,306✔
2503
      if (getString(&data) == MS_FAILURE) {
95,306✔
2504
        free(key);
×
2505
        return (MS_FAILURE);
×
2506
      }
2507
      msInsertHashTable(ptable, key, data);
95,306✔
2508

2509
      free(key);
95,306✔
2510
      free(data);
95,306✔
2511
      break;
95,306✔
2512
    }
2513
    default:
×
2514
      msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
2515
                 "loadHashTable()", msyystring_buffer, msyylineno);
2516
      return (MS_FAILURE);
×
2517
    }
2518
  }
2519

2520
  return (MS_SUCCESS);
2521
}
2522

2523
static void writeHashTable(FILE *stream, int indent, const char *title,
48✔
2524
                           hashTableObj *table) {
2525
  struct hashObj *tp;
2526
  int i;
2527

2528
  if (!table)
48✔
2529
    return;
2530
  if (msHashIsEmpty(table))
48✔
2531
    return;
2532

2533
  indent++;
4✔
2534
  writeBlockBegin(stream, indent, title);
4✔
2535
  for (i = 0; i < MS_HASHSIZE; i++) {
168✔
2536
    if (table->items[i] != NULL) {
164✔
2537
      for (tp = table->items[i]; tp != NULL; tp = tp->next)
28✔
2538
        writeNameValuePair(stream, indent, tp->key, tp->data);
14✔
2539
    }
2540
  }
2541
  writeBlockEnd(stream, indent, title);
4✔
2542
}
2543

2544
static void writeHashTableInline(FILE *stream, int indent, char *name,
1✔
2545
                                 hashTableObj *table) {
2546
  struct hashObj *tp = NULL;
2547
  int i;
2548

2549
  if (!table)
1✔
2550
    return;
2551
  if (msHashIsEmpty(table))
1✔
2552
    return;
2553

2554
  ++indent;
2555
  for (i = 0; i < MS_HASHSIZE; ++i) {
×
2556
    if (table->items[i] != NULL) {
×
2557
      for (tp = table->items[i]; tp != NULL; tp = tp->next) {
×
2558
        writeIndent(stream, indent);
2559
        msIO_fprintf(stream, "%s ", name);
×
2560
        writeStringElement(stream, tp->key);
×
2561
        msIO_fprintf(stream, " ");
×
2562
        writeStringElement(stream, tp->data);
×
2563
        writeLineFeed(stream);
2564
      }
2565
    }
2566
  }
2567
}
2568

2569
/*
2570
** Initialize, load and free a cluster object
2571
*/
2572
void initCluster(clusterObj *cluster) {
14,153✔
2573
  cluster->maxdistance = 10;
14,153✔
2574
  cluster->buffer = 0;
14,153✔
2575
  cluster->region = NULL;
14,153✔
2576
  msInitExpression(&(cluster->group));
14,153✔
2577
  msInitExpression(&(cluster->filter));
14,153✔
2578
}
14,153✔
2579

2580
void freeCluster(clusterObj *cluster) {
14,118✔
2581
  msFree(cluster->region);
14,118✔
2582
  msFreeExpression(&(cluster->group));
14,118✔
2583
  msFreeExpression(&(cluster->filter));
14,118✔
2584
}
14,118✔
2585

2586
int loadCluster(clusterObj *cluster) {
5✔
2587
  for (;;) {
2588
    switch (msyylex()) {
16✔
2589
    case (CLUSTER):
2590
      break; /* for string loads */
2591
    case (MAXDISTANCE):
5✔
2592
      if (getDouble(&(cluster->maxdistance), MS_NUM_CHECK_GT, 0, -1) == -1)
5✔
2593
        return (-1);
2594
      break;
2595
    case (BUFFER):
×
2596
      if (getDouble(&(cluster->buffer), MS_NUM_CHECK_GT, 0, -1) == -1)
×
2597
        return (-1);
2598
      break;
2599
    case (REGION):
5✔
2600
      if (getString(&cluster->region) == MS_FAILURE)
5✔
2601
        return (-1);
2602
      break;
2603
    case (END):
2604
      return (0);
2605
      break;
2606
    case (GROUP):
×
2607
      if (loadExpression(&(cluster->group)) == -1)
×
2608
        return (-1);
2609
      break;
2610
    case (FILTER):
×
2611
      if (loadExpression(&(cluster->filter)) == -1)
×
2612
        return (-1);
2613
      break;
2614
    default:
×
2615
      if (strlen(msyystring_buffer) > 0) {
×
2616
        msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
2617
                   "loadCluster()", msyystring_buffer, msyylineno);
2618
        return (-1);
×
2619
      } else {
2620
        return (0); /* end of a string, not an error */
2621
      }
2622
    }
2623
  }
2624
  return (MS_SUCCESS);
2625
}
2626

2627
int msUpdateClusterFromString(clusterObj *cluster, char *string) {
1✔
2628
  if (!cluster || !string)
1✔
2629
    return MS_FAILURE;
2630

2631
  msAcquireLock(TLOCK_PARSER);
1✔
2632

2633
  msyystate = MS_TOKENIZE_STRING;
1✔
2634
  msyystring = string;
1✔
2635
  msyylex(); /* sets things up, but doesn't process any tokens */
1✔
2636

2637
  msyylineno = 1; /* start at line 1 */
1✔
2638

2639
  if (loadCluster(cluster) == -1) {
1✔
2640
    msReleaseLock(TLOCK_PARSER);
×
2641
    return MS_FAILURE; /* parse error */
×
2642
    ;
2643
  }
2644

2645
  msyylex_destroy();
1✔
2646
  msReleaseLock(TLOCK_PARSER);
1✔
2647
  return MS_SUCCESS;
1✔
2648
}
2649

2650
static void writeCluster(FILE *stream, int indent, clusterObj *cluster) {
8✔
2651

2652
  if (cluster->maxdistance == 10 && cluster->buffer == 0.0 &&
8✔
2653
      cluster->region == NULL && cluster->group.string == NULL &&
7✔
2654
      cluster->filter.string == NULL)
7✔
2655
    return; /* Nothing to write */
2656

2657
  indent++;
1✔
2658
  writeBlockBegin(stream, indent, "CLUSTER");
1✔
2659
  writeNumber(stream, indent, "MAXDISTANCE", 10, cluster->maxdistance);
1✔
2660
  writeNumber(stream, indent, "BUFFER", 0, cluster->buffer);
1✔
2661
  writeString(stream, indent, "REGION", NULL, cluster->region);
1✔
2662
  writeExpression(stream, indent, "GROUP", &(cluster->group));
1✔
2663
  writeExpression(stream, indent, "FILTER", &(cluster->filter));
1✔
2664
  writeBlockEnd(stream, indent, "CLUSTER");
1✔
2665
}
2666

2667
char *msWriteClusterToString(clusterObj *cluster) {
1✔
2668
  msIOContext context;
2669
  msIOBuffer buffer;
2670

2671
  context.label = NULL;
1✔
2672
  context.write_channel = MS_TRUE;
1✔
2673
  context.readWriteFunc = msIO_bufferWrite;
1✔
2674
  context.cbData = &buffer;
1✔
2675
  buffer.data = NULL;
1✔
2676
  buffer.data_len = 0;
1✔
2677
  buffer.data_offset = 0;
1✔
2678

2679
  msIO_installHandlers(NULL, &context, NULL);
1✔
2680

2681
  writeCluster(stdout, -1, cluster);
1✔
2682
  msIO_bufferWrite(&buffer, "", 1);
1✔
2683

2684
  msIO_installHandlers(NULL, NULL, NULL);
1✔
2685

2686
  return (char *)buffer.data;
1✔
2687
}
2688

2689
/*
2690
** Initialize, load and free a single style
2691
*/
2692
int initStyle(styleObj *style) {
24,970✔
2693
  int i;
2694
  MS_REFCNT_INIT(style);
24,970✔
2695
  MS_INIT_COLOR(style->color, -1, -1, -1, 255); /* must explicitly set colors */
24,970✔
2696
  MS_INIT_COLOR(style->outlinecolor, -1, -1, -1, 255);
24,970✔
2697
  /* New Color Range fields*/
2698
  MS_INIT_COLOR(style->mincolor, -1, -1, -1, 255);
24,970✔
2699
  MS_INIT_COLOR(style->maxcolor, -1, -1, -1, 255);
24,970✔
2700
  style->minvalue = 0.0;
24,970✔
2701
  style->maxvalue = 1.0;
24,970✔
2702
  style->rangeitem = NULL;
24,970✔
2703
  /* End Color Range fields*/
2704
  style->symbol = 0; /* there is always a default symbol*/
24,970✔
2705
  style->symbolname = NULL;
24,970✔
2706
  style->size = -1; /* in SIZEUNITS (layerObj) */
24,970✔
2707
  style->minsize = MS_MINSYMBOLSIZE;
24,970✔
2708
  style->maxsize = MS_MAXSYMBOLSIZE;
24,970✔
2709
  style->width = 1;        /* in pixels */
24,970✔
2710
  style->outlinewidth = 0; /* in pixels */
24,970✔
2711
  style->minwidth = MS_MINSYMBOLWIDTH;
24,970✔
2712
  style->maxwidth = MS_MAXSYMBOLWIDTH;
24,970✔
2713
  style->minscaledenom = style->maxscaledenom = -1.0;
24,970✔
2714
  style->offsetx = style->offsety = 0;                   /* no offset */
24,970✔
2715
  style->polaroffsetpixel = style->polaroffsetangle = 0; /* no polar offset */
24,970✔
2716
  style->angle = 0;
24,970✔
2717
  style->autoangle = MS_FALSE;
24,970✔
2718
  style->antialiased = MS_TRUE;
24,970✔
2719
  style->opacity = 100; /* fully opaque */
24,970✔
2720

2721
  msInitExpression(&(style->_geomtransform));
24,970✔
2722
  style->_geomtransform.type = MS_GEOMTRANSFORM_NONE;
24,970✔
2723

2724
  style->patternlength = 0; /* solid line */
24,970✔
2725
  style->gap = 0;
24,970✔
2726
  style->initialgap = -1;
24,970✔
2727
  style->linecap = MS_CJC_DEFAULT_CAPS;
24,970✔
2728
  style->linejoin = MS_CJC_DEFAULT_JOINS;
24,970✔
2729
  style->linejoinmaxsize = MS_CJC_DEFAULT_JOIN_MAXSIZE;
24,970✔
2730

2731
  style->numbindings = 0;
24,970✔
2732
  style->nexprbindings = 0;
24,970✔
2733
  for (i = 0; i < MS_STYLE_BINDING_LENGTH; i++) {
324,610✔
2734
    style->bindings[i].item = NULL;
299,640✔
2735
    style->bindings[i].index = -1;
299,640✔
2736
    msInitExpression(&(style->exprBindings[i]));
299,640✔
2737
  }
2738

2739
  style->sizeunits = MS_INHERIT;
24,970✔
2740
  style->scalefactor = 1.0;
24,970✔
2741

2742
  return MS_SUCCESS;
24,970✔
2743
}
2744

2745
int loadStyle(styleObj *style) {
14,028✔
2746
  int symbol;
2747

2748
  for (;;) {
2749
    switch (msyylex()) {
49,295✔
2750
      /* New Color Range fields*/
2751
    case (COLORRANGE):
48✔
2752
      /*These are both in one line now*/
2753
      if (loadColor(&(style->mincolor), NULL) != MS_SUCCESS)
48✔
2754
        return (MS_FAILURE);
2755
      if (loadColor(&(style->maxcolor), NULL) != MS_SUCCESS)
48✔
2756
        return (MS_FAILURE);
2757
      break;
2758
    case (DATARANGE):
48✔
2759
      /*These are both in one line now*/
2760
      if (getDouble(&(style->minvalue), MS_NUM_CHECK_NONE, -1, -1) == -1)
48✔
2761
        return (MS_FAILURE);
2762
      if (getDouble(&(style->maxvalue), MS_NUM_CHECK_NONE, -1, -1) == -1)
48✔
2763
        return (MS_FAILURE);
2764
      break;
2765
    case (RANGEITEM):
16✔
2766
      if (getString(&style->rangeitem) == MS_FAILURE)
16✔
2767
        return (MS_FAILURE);
2768
      break;
2769
      /* End Range fields*/
2770
    case (ANGLE):
386✔
2771
      if ((symbol = getSymbol(3, MS_NUMBER, MS_BINDING, MS_AUTO)) == -1)
386✔
2772
        return (MS_FAILURE);
2773

2774
      if (symbol == MS_NUMBER) {
386✔
2775
        if (msCheckNumber(msyynumber, MS_NUM_CHECK_RANGE, -360.0, 360.0) ==
230✔
2776
            MS_FAILURE) {
2777
          msSetError(MS_MISCERR,
×
2778
                     "Invalid ANGLE, must be between -360 and 360 (line %d)",
2779
                     "loadStyle()", msyylineno);
2780
          return (MS_FAILURE);
×
2781
        }
2782
        style->angle = (double)msyynumber;
230✔
2783
      } else if (symbol == MS_BINDING) {
156✔
2784
        if (style->bindings[MS_STYLE_BINDING_ANGLE].item != NULL)
104✔
2785
          msFree(style->bindings[MS_STYLE_BINDING_ANGLE].item);
×
2786
        style->bindings[MS_STYLE_BINDING_ANGLE].item =
104✔
2787
            msStrdup(msyystring_buffer);
104✔
2788
        style->numbindings++;
104✔
2789
      } else {
2790
        style->autoangle = MS_TRUE;
52✔
2791
      }
2792
      break;
2793
    case (ANTIALIAS):
2✔
2794
      if ((symbol = getSymbol(2, MS_TRUE, MS_FALSE)) == -1)
2✔
2795
        return (MS_FAILURE);
2796
      if (symbol == MS_FALSE) {
2✔
2797
        style->antialiased = MS_FALSE;
1✔
2798
      }
2799
      break;
2800
    case (COLOR):
12,515✔
2801
      if (loadColor(&(style->color),
12,515✔
2802
                    &(style->bindings[MS_STYLE_BINDING_COLOR])) != MS_SUCCESS)
2803
        return (MS_FAILURE);
2804
      if (style->bindings[MS_STYLE_BINDING_COLOR].item)
12,515✔
2805
        style->numbindings++;
20✔
2806
      break;
2807
    case (EOF):
×
2808
      msSetError(MS_EOFERR, NULL, "loadStyle()");
×
2809
      return (MS_FAILURE); /* missing END (probably) */
×
2810
    case (END): {
14,028✔
2811
      int alpha;
2812

2813
      /* apply opacity as the alpha channel color(s) */
2814
      if (style->opacity < 100) {
14,028✔
2815
        alpha = MS_NINT(style->opacity * 2.55);
244✔
2816

2817
        style->color.alpha = alpha;
244✔
2818
        style->outlinecolor.alpha = alpha;
244✔
2819

2820
        style->mincolor.alpha = alpha;
244✔
2821
        style->maxcolor.alpha = alpha;
244✔
2822
      }
2823

2824
      return (MS_SUCCESS);
2825
    } break;
2826
    case (GAP):
87✔
2827
      if (getDouble(&(style->gap), MS_NUM_CHECK_NONE, -1, -1) == -1)
87✔
2828
        return (MS_FAILURE);
2829
      break;
2830
    case (INITIALGAP):
45✔
2831
      if (getDouble(&(style->initialgap), MS_NUM_CHECK_GTE, 0, -1) ==
45✔
2832
          -1) { // zero is ok
2833
        msSetError(MS_MISCERR,
×
2834
                   "INITIALGAP requires a positive values (line %d)",
2835
                   "loadStyle()", msyylineno);
2836
        return (MS_FAILURE);
×
2837
      }
2838
      break;
2839
    case (MAXSCALEDENOM):
×
2840
      if (getDouble(&(style->maxscaledenom), MS_NUM_CHECK_GTE, 0, -1) == -1)
×
2841
        return (MS_FAILURE);
2842
      break;
2843
    case (MINSCALEDENOM):
16✔
2844
      if (getDouble(&(style->minscaledenom), MS_NUM_CHECK_GTE, 0, -1) == -1)
16✔
2845
        return (MS_FAILURE);
2846
      break;
2847
    case (GEOMTRANSFORM): {
1,201✔
2848
      int s;
2849
      if ((s = getSymbol(2, MS_STRING, MS_EXPRESSION)) == -1)
1,201✔
2850
        return (MS_FAILURE);
2851
      if (s == MS_STRING)
1,201✔
2852
        msStyleSetGeomTransform(style, msyystring_buffer);
1,165✔
2853
      else {
2854
        /* handle expression case here for the moment */
2855
        msFree(style->_geomtransform.string);
36✔
2856
        style->_geomtransform.string = msStrdup(msyystring_buffer);
36✔
2857
        style->_geomtransform.type = MS_GEOMTRANSFORM_EXPRESSION;
36✔
2858
      }
2859
    } break;
2860
    case (LINECAP):
36✔
2861
      if ((style->linecap = getSymbol(4, MS_CJC_BUTT, MS_CJC_ROUND,
36✔
2862
                                      MS_CJC_SQUARE, MS_CJC_TRIANGLE)) == -1)
2863
        return (MS_FAILURE);
2864
      break;
2865
    case (LINEJOIN):
×
2866
      if ((style->linejoin = getSymbol(4, MS_CJC_NONE, MS_CJC_ROUND,
×
2867
                                       MS_CJC_MITER, MS_CJC_BEVEL)) == -1)
2868
        return (MS_FAILURE);
2869
      break;
2870
    case (LINEJOINMAXSIZE):
×
2871
      if (getDouble(&(style->linejoinmaxsize), MS_NUM_CHECK_GT, 0, -1) == -1)
×
2872
        return (MS_FAILURE);
2873
      break;
2874
    case (MAXSIZE):
75✔
2875
      if (getDouble(&(style->maxsize), MS_NUM_CHECK_GT, 0, -1) == -1)
75✔
2876
        return (MS_FAILURE);
2877
      break;
2878
    case (MINSIZE):
×
2879
      if (getDouble(&(style->minsize), MS_NUM_CHECK_GTE, 0, -1) == -1)
×
2880
        return (MS_FAILURE);
2881
      break;
2882
    case (MAXWIDTH):
×
2883
      if (getDouble(&(style->maxwidth), MS_NUM_CHECK_GT, 0, -1) == -1)
×
2884
        return (MS_FAILURE);
2885
      break;
2886
    case (MINWIDTH):
×
2887
      if (getDouble(&(style->minwidth), MS_NUM_CHECK_GTE, 0, -1) == -1)
×
2888
        return (MS_FAILURE);
2889
      break;
2890
    case (OFFSET):
176✔
2891
      if ((symbol = getSymbol(2, MS_NUMBER, MS_BINDING)) == -1)
176✔
2892
        return (MS_FAILURE);
2893
      if (symbol == MS_NUMBER)
176✔
2894
        style->offsetx = (double)msyynumber; // any double ok
176✔
2895
      else {
2896
        if (style->bindings[MS_STYLE_BINDING_OFFSET_X].item != NULL)
×
2897
          msFree(style->bindings[MS_STYLE_BINDING_OFFSET_X].item);
×
2898
        style->bindings[MS_STYLE_BINDING_OFFSET_X].item =
×
2899
            msStrdup(msyystring_buffer);
×
2900
        style->numbindings++;
×
2901
      }
2902

2903
      if ((symbol = getSymbol(2, MS_NUMBER, MS_BINDING)) == -1)
176✔
2904
        return (MS_FAILURE);
2905
      if (symbol == MS_NUMBER)
176✔
2906
        style->offsety = (double)msyynumber; // any double ok
176✔
2907
      else {
2908
        if (style->bindings[MS_STYLE_BINDING_OFFSET_Y].item != NULL)
×
2909
          msFree(style->bindings[MS_STYLE_BINDING_OFFSET_Y].item);
×
2910
        style->bindings[MS_STYLE_BINDING_OFFSET_Y].item =
×
2911
            msStrdup(msyystring_buffer);
×
2912
        style->numbindings++;
×
2913
      }
2914
      break;
2915
    case (OPACITY):
248✔
2916
      if ((symbol = getSymbol(2, MS_NUMBER, MS_BINDING)) == -1)
248✔
2917
        return (MS_FAILURE);
2918
      if (symbol == MS_NUMBER)
248✔
2919
        style->opacity = MS_MAX(MS_MIN((int)msyynumber, 100),
248✔
2920
                                0); /* force opacity to between 0 and 100 */
2921
      else {
2922
        if (style->bindings[MS_STYLE_BINDING_OPACITY].item != NULL)
×
2923
          msFree(style->bindings[MS_STYLE_BINDING_OPACITY].item);
×
2924
        style->bindings[MS_STYLE_BINDING_OPACITY].item =
×
2925
            msStrdup(msyystring_buffer);
×
2926
        style->numbindings++;
×
2927
      }
2928
      break;
2929
    case (OUTLINECOLOR):
3,926✔
2930
      if (loadColor(&(style->outlinecolor),
3,926✔
2931
                    &(style->bindings[MS_STYLE_BINDING_OUTLINECOLOR])) !=
2932
          MS_SUCCESS)
2933
        return (MS_FAILURE);
2934
      if (style->bindings[MS_STYLE_BINDING_OUTLINECOLOR].item)
3,926✔
2935
        style->numbindings++;
×
2936
      break;
2937
    case (PATTERN): {
2938
      int done = MS_FALSE;
2939
      for (;;) { /* read till the next END */
2940
        switch (msyylex()) {
236✔
2941
        case (END):
72✔
2942
          if (style->patternlength < 2) {
72✔
2943
            msSetError(MS_SYMERR,
×
2944
                       "Not enough pattern elements. A minimum of 2 are "
2945
                       "required (line %d)",
2946
                       "loadStyle()", msyylineno);
2947
            return (MS_FAILURE);
×
2948
          }
2949
          done = MS_TRUE;
2950
          break;
2951
        case (MS_NUMBER): /* read the pattern values */
164✔
2952
          if (style->patternlength == MS_MAXPATTERNLENGTH) {
164✔
2953
            msSetError(MS_SYMERR, "Pattern too long.", "loadStyle()");
×
2954
            return (MS_FAILURE);
×
2955
          }
2956
          style->pattern[style->patternlength] =
164✔
2957
              atof(msyystring_buffer); // good enough?
164✔
2958
          style->patternlength++;
164✔
2959
          break;
164✔
2960
        default:
×
2961
          msSetError(MS_TYPEERR, "Parsing error near (%s):(line %d)",
×
2962
                     "loadStyle()", msyystring_buffer, msyylineno);
2963
          return (MS_FAILURE);
×
2964
        }
2965
        if (done == MS_TRUE)
236✔
2966
          break;
2967
      }
2968
      break;
2969
    }
2970
    case (OUTLINEWIDTH):
43✔
2971
      if ((symbol = getSymbol(2, MS_NUMBER, MS_BINDING)) == -1)
43✔
2972
        return (MS_FAILURE);
2973
      if (symbol == MS_NUMBER) {
43✔
2974
        if (msCheckNumber(msyynumber, MS_NUM_CHECK_GTE, 0, -1) == MS_FAILURE) {
25✔
2975
          msSetError(MS_MISCERR,
×
2976
                     "Invalid OUTLINEWIDTH, must be greater then or equal to 0 "
2977
                     "(line %d)",
2978
                     "loadStyle()", msyylineno);
2979
          return (MS_FAILURE);
×
2980
        }
2981
        style->outlinewidth = (double)msyynumber;
25✔
2982
      } else {
2983
        if (style->bindings[MS_STYLE_BINDING_OUTLINEWIDTH].item != NULL)
18✔
2984
          msFree(style->bindings[MS_STYLE_BINDING_OUTLINEWIDTH].item);
×
2985
        style->bindings[MS_STYLE_BINDING_OUTLINEWIDTH].item =
18✔
2986
            msStrdup(msyystring_buffer);
18✔
2987
        style->numbindings++;
18✔
2988
      }
2989
      break;
2990
    case (SIZE):
5,800✔
2991
      if (style->exprBindings[MS_STYLE_BINDING_SIZE].string) {
5,800✔
2992
        msFreeExpression(&style->exprBindings[MS_STYLE_BINDING_SIZE]);
3✔
2993
        style->nexprbindings--;
3✔
2994
      }
2995

2996
      if ((symbol = getSymbol(3, MS_EXPRESSION, MS_NUMBER, MS_BINDING)) == -1)
5,800✔
2997
        return (MS_FAILURE);
2998
      if (symbol == MS_NUMBER) {
5,800✔
2999
        if (msCheckNumber(msyynumber, MS_NUM_CHECK_GT, 0, -1) == MS_FAILURE) {
5,702✔
3000
          msSetError(MS_MISCERR,
×
3001
                     "Invalid SIZE, must be greater than 0 (line %d)",
3002
                     "loadStyle()", msyylineno);
3003
          return (MS_FAILURE);
×
3004
        }
3005
        style->size = (double)msyynumber;
5,702✔
3006
      } else if (symbol == MS_EXPRESSION) {
98✔
3007
        msFree(style->exprBindings[MS_STYLE_BINDING_SIZE].string);
6✔
3008
        style->exprBindings[MS_STYLE_BINDING_SIZE].string =
6✔
3009
            msStrdup(msyystring_buffer);
6✔
3010
        style->exprBindings[MS_STYLE_BINDING_SIZE].type = MS_EXPRESSION;
6✔
3011
        style->nexprbindings++;
6✔
3012
      } else {
3013
        if (style->bindings[MS_STYLE_BINDING_SIZE].item != NULL)
92✔
3014
          msFree(style->bindings[MS_STYLE_BINDING_SIZE].item);
3✔
3015
        style->bindings[MS_STYLE_BINDING_SIZE].item =
92✔
3016
            msStrdup(msyystring_buffer);
92✔
3017
        style->numbindings++;
92✔
3018
      }
3019
      break;
3020
    case (STYLE):
3021
      break; /* for string loads */
3022
    case (SYMBOL):
7,077✔
3023
      if ((symbol = getSymbol(3, MS_NUMBER, MS_STRING, MS_BINDING)) == -1)
7,077✔
3024
        return (MS_FAILURE);
3025
      if (symbol == MS_NUMBER) {
7,077✔
3026
        if (msCheckNumber(msyynumber, MS_NUM_CHECK_GTE, 0, -1) == MS_FAILURE) {
4,542✔
3027
          msSetError(
×
3028
              MS_MISCERR,
3029
              "Invalid SYMBOL id, must be greater than or equal to 0 (line %d)",
3030
              "loadStyle()", msyylineno);
3031
          return (MS_FAILURE);
×
3032
        }
3033
        if (style->symbolname != NULL) {
4,542✔
3034
          msFree(style->symbolname);
×
3035
          style->symbolname = NULL;
×
3036
        }
3037
        style->symbol = (int)msyynumber;
4,542✔
3038
      } else if (symbol == MS_STRING) {
2,535✔
3039
        if (style->symbolname != NULL)
2,519✔
3040
          msFree(style->symbolname);
×
3041
        style->symbolname = msStrdup(msyystring_buffer);
2,519✔
3042
      } else {
3043
        if (style->bindings[MS_STYLE_BINDING_SYMBOL].item != NULL)
16✔
3044
          msFree(style->bindings[MS_STYLE_BINDING_SYMBOL].item);
×
3045
        style->bindings[MS_STYLE_BINDING_SYMBOL].item =
16✔
3046
            msStrdup(msyystring_buffer);
16✔
3047
        style->numbindings++;
16✔
3048
      }
3049
      break;
3050
    case (WIDTH):
3,433✔
3051
      if ((symbol = getSymbol(2, MS_NUMBER, MS_BINDING)) == -1)
3,433✔
3052
        return (MS_FAILURE);
3053
      if (symbol == MS_NUMBER) {
3,433✔
3054
        if (msCheckNumber(msyynumber, MS_NUM_CHECK_GTE, 0, -1) == MS_FAILURE) {
3,415✔
3055
          msSetError(
×
3056
              MS_MISCERR,
3057
              "Invalid WIDTH, must be greater than or equal to 0 (line %d)",
3058
              "loadStyle()", msyylineno);
3059
          return (MS_FAILURE);
×
3060
        }
3061
        style->width = (double)msyynumber;
3,415✔
3062
      } else {
3063
        if (style->bindings[MS_STYLE_BINDING_WIDTH].item != NULL)
18✔
3064
          msFree(style->bindings[MS_STYLE_BINDING_WIDTH].item);
×
3065
        style->bindings[MS_STYLE_BINDING_WIDTH].item =
18✔
3066
            msStrdup(msyystring_buffer);
18✔
3067
        style->numbindings++;
18✔
3068
      }
3069
      break;
3070
    case (POLAROFFSET):
13✔
3071
      if ((symbol = getSymbol(2, MS_NUMBER, MS_BINDING)) == -1)
13✔
3072
        return (MS_FAILURE);
3073
      if (symbol == MS_NUMBER) {
13✔
3074
        style->polaroffsetpixel = (double)msyynumber; // ok?
×
3075
      } else {
3076
        if (style->bindings[MS_STYLE_BINDING_POLAROFFSET_PIXEL].item != NULL)
13✔
3077
          msFree(style->bindings[MS_STYLE_BINDING_POLAROFFSET_PIXEL].item);
×
3078
        style->bindings[MS_STYLE_BINDING_POLAROFFSET_PIXEL].item =
13✔
3079
            msStrdup(msyystring_buffer);
13✔
3080
        style->numbindings++;
13✔
3081
      }
3082

3083
      if ((symbol = getSymbol(2, MS_NUMBER, MS_BINDING)) == -1)
13✔
3084
        return (MS_FAILURE);
3085
      if (symbol == MS_NUMBER) {
13✔
3086
        style->polaroffsetangle = (double)msyynumber; // ok?
×
3087
      } else {
3088
        if (style->bindings[MS_STYLE_BINDING_POLAROFFSET_ANGLE].item != NULL)
13✔
3089
          msFree(style->bindings[MS_STYLE_BINDING_POLAROFFSET_ANGLE].item);
×
3090
        style->bindings[MS_STYLE_BINDING_POLAROFFSET_ANGLE].item =
13✔
3091
            msStrdup(msyystring_buffer);
13✔
3092
        style->numbindings++;
13✔
3093
      }
3094
      break;
3095
    default:
×
3096
      if (strlen(msyystring_buffer) > 0) {
×
3097
        msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
3098
                   "loadStyle()", msyystring_buffer, msyylineno);
3099
        return (MS_FAILURE);
×
3100
      } else {
3101
        return (MS_SUCCESS); /* end of a string, not an error */
3102
      }
3103
    }
3104
  }
3105
}
3106

3107
int msUpdateStyleFromString(styleObj *style, char *string) {
4✔
3108
  if (!style || !string)
4✔
3109
    return MS_FAILURE;
3110

3111
  msAcquireLock(TLOCK_PARSER);
4✔
3112

3113
  msyystate = MS_TOKENIZE_STRING;
4✔
3114
  msyystring = string;
4✔
3115
  msyylex(); /* sets things up, but doesn't process any tokens */
4✔
3116

3117
  msyylineno = 1; /* start at line 1 */
4✔
3118

3119
  if (loadStyle(style) == -1) {
4✔
3120
    msReleaseLock(TLOCK_PARSER);
×
3121
    return MS_FAILURE; /* parse error */
×
3122
    ;
3123
  }
3124

3125
  msyylex_destroy();
4✔
3126
  msReleaseLock(TLOCK_PARSER);
4✔
3127
  return MS_SUCCESS;
4✔
3128
}
3129

3130
int freeStyle(styleObj *style) {
24,983✔
3131
  int i;
3132

3133
  if (MS_REFCNT_DECR_IS_NOT_ZERO(style)) {
24,983✔
3134
    return MS_FAILURE;
3135
  }
3136

3137
  msFree(style->symbolname);
24,929✔
3138
  msFreeExpression(&style->_geomtransform);
24,929✔
3139
  msFree(style->rangeitem);
24,929✔
3140

3141
  for (i = 0; i < MS_STYLE_BINDING_LENGTH; i++) {
324,077✔
3142
    msFree(style->bindings[i].item);
299,148✔
3143
    msFreeExpression(&(style->exprBindings[i]));
299,148✔
3144
  }
3145

3146
  return MS_SUCCESS;
3147
}
3148

3149
void writeStyle(FILE *stream, int indent, styleObj *style) {
11✔
3150

3151
  indent++;
11✔
3152
  writeBlockBegin(stream, indent, "STYLE");
11✔
3153

3154
  if (style->numbindings > 0 && style->bindings[MS_STYLE_BINDING_ANGLE].item)
11✔
3155
    writeAttributeBinding(stream, indent, "ANGLE",
×
3156
                          &(style->bindings[MS_STYLE_BINDING_ANGLE]));
3157
  else
3158
    writeNumberOrKeyword(stream, indent, "ANGLE", 0, style->angle,
11✔
3159
                         style->autoangle, 1, MS_TRUE, "AUTO");
3160

3161
  if (style->numbindings > 0 && style->bindings[MS_STYLE_BINDING_COLOR].item)
11✔
3162
    writeAttributeBinding(stream, indent, "COLOR",
×
3163
                          &(style->bindings[MS_STYLE_BINDING_COLOR]));
3164
  else
3165
    writeColor(stream, indent, "COLOR", NULL, &(style->color));
11✔
3166

3167
  writeNumber(stream, indent, "GAP", 0, style->gap);
11✔
3168
  writeNumber(stream, indent, "INITIALGAP", -1, style->initialgap);
11✔
3169

3170
  if (style->_geomtransform.type == MS_GEOMTRANSFORM_EXPRESSION) {
11✔
3171
    writeIndent(stream, indent + 1);
×
3172
    msIO_fprintf(stream, "GEOMTRANSFORM (%s)\n", style->_geomtransform.string);
×
3173
  } else if (style->_geomtransform.type != MS_GEOMTRANSFORM_NONE) {
11✔
3174
    writeKeyword(stream, indent, "GEOMTRANSFORM", style->_geomtransform.type, 8,
1✔
3175
                 MS_GEOMTRANSFORM_BBOX, "\"bbox\"", MS_GEOMTRANSFORM_END,
3176
                 "\"end\"", MS_GEOMTRANSFORM_LABELPOINT, "\"labelpnt\"",
3177
                 MS_GEOMTRANSFORM_LABELPOLY, "\"labelpoly\"",
3178
                 MS_GEOMTRANSFORM_LABELCENTER, "\"labelcenter\"",
3179
                 MS_GEOMTRANSFORM_START, "\"start\"", MS_GEOMTRANSFORM_VERTICES,
3180
                 "\"vertices\"", MS_GEOMTRANSFORM_CENTROID, "\"centroid\"");
3181
  }
3182

3183
  if (style->linecap != MS_CJC_DEFAULT_CAPS) {
11✔
3184
    writeKeyword(stream, indent, "LINECAP", (int)style->linecap, 5, MS_CJC_NONE,
×
3185
                 "NONE", MS_CJC_ROUND, "ROUND", MS_CJC_SQUARE, "SQUARE",
3186
                 MS_CJC_BUTT, "BUTT", MS_CJC_TRIANGLE, "TRIANGLE");
3187
  }
3188
  if (style->linejoin != MS_CJC_DEFAULT_JOINS) {
11✔
3189
    writeKeyword(stream, indent, "LINEJOIN", (int)style->linejoin, 5,
×
3190
                 MS_CJC_NONE, "NONE", MS_CJC_ROUND, "ROUND", MS_CJC_BEVEL,
3191
                 "BEVEL", MS_CJC_MITER, "MITER");
3192
  }
3193
  writeNumber(stream, indent, "LINEJOINMAXSIZE", MS_CJC_DEFAULT_JOIN_MAXSIZE,
11✔
3194
              style->linejoinmaxsize);
3195

3196
  writeNumber(stream, indent, "MAXSCALEDENOM", -1, style->maxscaledenom);
11✔
3197
  writeNumber(stream, indent, "MAXSIZE", MS_MAXSYMBOLSIZE, style->maxsize);
11✔
3198
  writeNumber(stream, indent, "MAXWIDTH", MS_MAXSYMBOLWIDTH, style->maxwidth);
11✔
3199
  writeNumber(stream, indent, "MINSCALEDENOM", -1, style->minscaledenom);
11✔
3200
  writeNumber(stream, indent, "MINSIZE", MS_MINSYMBOLSIZE, style->minsize);
11✔
3201
  writeNumber(stream, indent, "MINWIDTH", MS_MINSYMBOLWIDTH, style->minwidth);
11✔
3202
  if ((style->numbindings > 0 &&
11✔
3203
       (style->bindings[MS_STYLE_BINDING_OFFSET_X].item ||
×
3204
        style->bindings[MS_STYLE_BINDING_OFFSET_Y].item)) ||
×
3205
      style->offsetx != 0 || style->offsety != 0)
11✔
3206
    writeDimension(stream, indent, "OFFSET", style->offsetx, style->offsety,
1✔
3207
                   style->bindings[MS_STYLE_BINDING_OFFSET_X].item,
3208
                   style->bindings[MS_STYLE_BINDING_OFFSET_Y].item);
3209
  if ((style->numbindings > 0 &&
11✔
3210
       (style->bindings[MS_STYLE_BINDING_POLAROFFSET_PIXEL].item ||
×
3211
        style->bindings[MS_STYLE_BINDING_POLAROFFSET_ANGLE].item)) ||
×
3212
      style->polaroffsetangle != 0 || style->polaroffsetpixel != 0)
11✔
3213
    writeDimension(stream, indent, "POLAROFFSET", style->polaroffsetpixel,
×
3214
                   style->polaroffsetangle,
3215
                   style->bindings[MS_STYLE_BINDING_POLAROFFSET_PIXEL].item,
3216
                   style->bindings[MS_STYLE_BINDING_POLAROFFSET_ANGLE].item);
3217

3218
  if (style->numbindings > 0 && style->bindings[MS_STYLE_BINDING_OPACITY].item)
11✔
3219
    writeAttributeBinding(stream, indent, "OPACITY",
×
3220
                          &(style->bindings[MS_STYLE_BINDING_OPACITY]));
3221
  else
3222
    writeNumber(stream, indent, "OPACITY", 100, style->opacity);
11✔
3223

3224
  if (style->numbindings > 0 &&
11✔
3225
      style->bindings[MS_STYLE_BINDING_OUTLINECOLOR].item)
×
3226
    writeAttributeBinding(stream, indent, "OUTLINECOLOR",
×
3227
                          &(style->bindings[MS_STYLE_BINDING_OUTLINECOLOR]));
3228
  else
3229
    writeColor(stream, indent, "OUTLINECOLOR", NULL, &(style->outlinecolor));
11✔
3230

3231
  if (style->numbindings > 0 &&
11✔
3232
      style->bindings[MS_STYLE_BINDING_OUTLINEWIDTH].item)
×
3233
    writeAttributeBinding(stream, indent, "OUTLINEWIDTH",
×
3234
                          &(style->bindings[MS_STYLE_BINDING_OUTLINEWIDTH]));
3235
  else
3236
    writeNumber(stream, indent, "OUTLINEWIDTH", 0, style->outlinewidth);
11✔
3237

3238
  /* PATTERN */
3239
  if (style->patternlength != 0) {
11✔
3240
    int i;
3241
    indent++;
×
3242
    writeBlockBegin(stream, indent, "PATTERN");
×
3243
    writeIndent(stream, indent);
3244
    for (i = 0; i < style->patternlength; i++)
×
3245
      msIO_fprintf(stream, " %.2f", style->pattern[i]);
×
3246
    msIO_fprintf(stream, "\n");
×
3247
    writeBlockEnd(stream, indent, "PATTERN");
×
3248
    indent--;
3249
  }
3250

3251
  if (style->numbindings > 0 && style->bindings[MS_STYLE_BINDING_SIZE].item)
11✔
3252
    writeAttributeBinding(stream, indent, "SIZE",
×
3253
                          &(style->bindings[MS_STYLE_BINDING_SIZE]));
3254
  else
3255
    writeNumber(stream, indent, "SIZE", -1, style->size);
11✔
3256

3257
  if (style->numbindings > 0 && style->bindings[MS_STYLE_BINDING_SYMBOL].item)
11✔
3258
    writeAttributeBinding(stream, indent, "SYMBOL",
×
3259
                          &(style->bindings[MS_STYLE_BINDING_SYMBOL]));
3260
  else
3261
    writeNumberOrString(stream, indent, "SYMBOL", 0, style->symbol,
11✔
3262
                        style->symbolname);
3263

3264
  if (style->numbindings > 0 && style->bindings[MS_STYLE_BINDING_WIDTH].item)
11✔
3265
    writeAttributeBinding(stream, indent, "WIDTH",
×
3266
                          &(style->bindings[MS_STYLE_BINDING_WIDTH]));
3267
  else
3268
    writeNumber(stream, indent, "WIDTH", 1, style->width);
11✔
3269

3270
  writeString(stream, indent, "RANGEITEM", NULL, style->rangeitem);
11✔
3271
  /* If COLORRANGE is valid, assume DATARANGE also needs to be written */
3272
  if (MS_VALID_COLOR(style->mincolor) && MS_VALID_COLOR(style->maxcolor)) {
11✔
3273
    writeColorRange(stream, indent, "COLORRANGE", &(style->mincolor),
×
3274
                    &(style->maxcolor));
3275
    writeDoubleRange(stream, indent, "DATARANGE", style->minvalue,
×
3276
                     style->maxvalue);
3277
  }
3278

3279
  writeBlockEnd(stream, indent, "STYLE");
11✔
3280
}
11✔
3281

3282
char *msWriteStyleToString(styleObj *style) {
1✔
3283
  msIOContext context;
3284
  msIOBuffer buffer;
3285

3286
  context.label = NULL;
1✔
3287
  context.write_channel = MS_TRUE;
1✔
3288
  context.readWriteFunc = msIO_bufferWrite;
1✔
3289
  context.cbData = &buffer;
1✔
3290
  buffer.data = NULL;
1✔
3291
  buffer.data_len = 0;
1✔
3292
  buffer.data_offset = 0;
1✔
3293

3294
  msIO_installHandlers(NULL, &context, NULL);
1✔
3295

3296
  writeStyle(stdout, -1, style);
1✔
3297
  msIO_bufferWrite(&buffer, "", 1);
1✔
3298

3299
  msIO_installHandlers(NULL, NULL, NULL);
1✔
3300

3301
  return (char *)buffer.data;
1✔
3302
}
3303

3304
/*
3305
** Initialize, load and free a single class
3306
*/
3307
int initClass(classObj *class) {
19,163✔
3308
  class->status = MS_ON;
19,163✔
3309
  class->debug = MS_OFF;
19,163✔
3310
  MS_REFCNT_INIT(class);
19,163✔
3311
  class->isfallback = FALSE;
19,163✔
3312

3313
  msInitExpression(&(class->expression));
19,163✔
3314
  class->name = NULL;
19,163✔
3315
  class->title = NULL;
19,163✔
3316
  msInitExpression(&(class->text));
19,163✔
3317

3318
  class->template = NULL;
19,163✔
3319

3320
  initHashTable(&(class->metadata));
19,163✔
3321
  initHashTable(&(class->validation));
19,163✔
3322

3323
  class->maxscaledenom = class->minscaledenom = -1.0;
19,163✔
3324
  class->minfeaturesize = -1; /* no limit */
19,163✔
3325

3326
  /* Set maxstyles = 0, styles[] will be allocated as needed on first call
3327
   * to msGrowClassStyles()
3328
   */
3329
  class->numstyles = class->maxstyles = 0;
19,163✔
3330
  class->styles = NULL;
19,163✔
3331

3332
  class->numlabels = class->maxlabels = 0;
19,163✔
3333
  class->labels = NULL;
19,163✔
3334

3335
  class->keyimage = NULL;
19,163✔
3336

3337
  class->group = NULL;
19,163✔
3338

3339
  class->leader = NULL;
19,163✔
3340

3341
  class->sizeunits = MS_INHERIT;
19,163✔
3342
  class->scalefactor = 1.0;
19,163✔
3343

3344
  return (0);
19,163✔
3345
}
3346

3347
int freeClass(classObj *class) {
19,237✔
3348
  int i;
3349

3350
  if (MS_REFCNT_DECR_IS_NOT_ZERO(class)) {
19,237✔
3351
    return MS_FAILURE;
3352
  }
3353

3354
  msFreeExpression(&(class->expression));
19,122✔
3355
  msFreeExpression(&(class->text));
19,122✔
3356
  msFree(class->name);
19,122✔
3357
  msFree(class->title);
19,122✔
3358
  msFree(class->template);
19,122✔
3359
  msFree(class->group);
19,122✔
3360

3361
  msFreeHashItems(&(class->metadata));
19,122✔
3362
  msFreeHashItems(&(class->validation));
19,122✔
3363

3364
  for (i = 0; i < class->numstyles; i++) { /* each style */
38,120✔
3365
    if (class->styles[i] != NULL) {
18,998✔
3366
      if (freeStyle(class->styles[i]) == MS_SUCCESS) {
18,998✔
3367
        msFree(class->styles[i]);
18,995✔
3368
      }
3369
    }
3370
  }
3371
  msFree(class->styles);
19,122✔
3372

3373
  for (i = 0; i < class->numlabels; i++) { /* each label */
22,407✔
3374
    if (class->labels[i] != NULL) {
3,285✔
3375
      if (freeLabel(class->labels[i]) == MS_SUCCESS) {
3,285✔
3376
        msFree(class->labels[i]);
3,282✔
3377
      }
3378
    }
3379
  }
3380
  msFree(class->labels);
19,122✔
3381

3382
  msFree(class->keyimage);
19,122✔
3383

3384
  if (class->leader) {
19,122✔
3385
    freeLabelLeader(class->leader);
16✔
3386
    msFree(class->leader);
16✔
3387
    class->leader = NULL;
16✔
3388
  }
3389

3390
  return MS_SUCCESS;
3391
}
3392

3393
/*
3394
** Ensure there is at least one free entry in the sttyles array of this
3395
** classObj. Grow the allocated styles array if necessary and allocate
3396
** a new style for styles[numstyles] if there is not already one,
3397
** setting its contents to all zero bytes (i.e. does not call initStyle()
3398
** on it).
3399
**
3400
** This function is safe to use for the initial allocation of the styles[]
3401
** array as well (i.e. when maxstyles==0 and styles==NULL)
3402
**
3403
** Returns a reference to the new styleObj on success, NULL on error.
3404
*/
3405
styleObj *msGrowClassStyles(classObj *class) {
19,121✔
3406
  /* Do we need to increase the size of styles[] by  MS_STYLE_ALLOCSIZE?
3407
   */
3408
  if (class->numstyles == class->maxstyles) {
19,121✔
3409
    styleObj **newStylePtr;
3410
    int i, newsize;
3411

3412
    newsize = class->maxstyles + MS_STYLE_ALLOCSIZE;
18,102✔
3413

3414
    /* Alloc/realloc styles */
3415
    newStylePtr =
3416
        (styleObj **)realloc(class->styles, newsize * sizeof(styleObj *));
18,102✔
3417
    MS_CHECK_ALLOC(newStylePtr, newsize * sizeof(styleObj *), NULL);
18,102✔
3418

3419
    class->styles = newStylePtr;
18,102✔
3420
    class->maxstyles = newsize;
18,102✔
3421
    for (i = class->numstyles; i < class->maxstyles; i++) {
90,510✔
3422
      class->styles[i] = NULL;
72,408✔
3423
    }
3424
  }
3425

3426
  if (class->styles[class->numstyles] == NULL) {
19,121✔
3427
    class->styles[class->numstyles] = (styleObj *)calloc(1, sizeof(styleObj));
19,121✔
3428
    MS_CHECK_ALLOC(class->styles[class->numstyles], sizeof(styleObj), NULL);
19,121✔
3429
  }
3430

3431
  return class->styles[class->numstyles];
19,121✔
3432
}
3433

3434
/* exactly the same as for a classObj */
3435
styleObj *msGrowLabelStyles(labelObj *label) {
5,818✔
3436
  /* Do we need to increase the size of styles[] by  MS_STYLE_ALLOCSIZE?
3437
   */
3438
  if (label->numstyles == label->maxstyles) {
5,818✔
3439
    styleObj **newStylePtr;
3440
    int i, newsize;
3441

3442
    newsize = label->maxstyles + MS_STYLE_ALLOCSIZE;
5,788✔
3443

3444
    /* Alloc/realloc styles */
3445
    newStylePtr =
3446
        (styleObj **)realloc(label->styles, newsize * sizeof(styleObj *));
5,788✔
3447
    MS_CHECK_ALLOC(newStylePtr, newsize * sizeof(styleObj *), NULL);
5,788✔
3448

3449
    label->styles = newStylePtr;
5,788✔
3450
    label->maxstyles = newsize;
5,788✔
3451
    for (i = label->numstyles; i < label->maxstyles; i++) {
28,940✔
3452
      label->styles[i] = NULL;
23,152✔
3453
    }
3454
  }
3455

3456
  if (label->styles[label->numstyles] == NULL) {
5,818✔
3457
    label->styles[label->numstyles] = (styleObj *)calloc(1, sizeof(styleObj));
5,818✔
3458
    MS_CHECK_ALLOC(label->styles[label->numstyles], sizeof(styleObj), NULL);
5,818✔
3459
  }
3460

3461
  return label->styles[label->numstyles];
5,818✔
3462
}
3463

3464
/* exactly the same as for a labelLeaderObj, needs refactoring */
3465
styleObj *msGrowLeaderStyles(labelLeaderObj *leader) {
16✔
3466
  /* Do we need to increase the size of styles[] by  MS_STYLE_ALLOCSIZE?
3467
   */
3468
  if (leader->numstyles == leader->maxstyles) {
16✔
3469
    styleObj **newStylePtr;
3470
    int i, newsize;
3471

3472
    newsize = leader->maxstyles + MS_STYLE_ALLOCSIZE;
16✔
3473

3474
    /* Alloc/realloc styles */
3475
    newStylePtr =
3476
        (styleObj **)realloc(leader->styles, newsize * sizeof(styleObj *));
16✔
3477
    MS_CHECK_ALLOC(newStylePtr, newsize * sizeof(styleObj *), NULL);
16✔
3478

3479
    leader->styles = newStylePtr;
16✔
3480
    leader->maxstyles = newsize;
16✔
3481
    for (i = leader->numstyles; i < leader->maxstyles; i++) {
80✔
3482
      leader->styles[i] = NULL;
64✔
3483
    }
3484
  }
3485

3486
  if (leader->styles[leader->numstyles] == NULL) {
16✔
3487
    leader->styles[leader->numstyles] = (styleObj *)calloc(1, sizeof(styleObj));
16✔
3488
    MS_CHECK_ALLOC(leader->styles[leader->numstyles], sizeof(styleObj), NULL);
16✔
3489
  }
3490

3491
  return leader->styles[leader->numstyles];
16✔
3492
}
3493

3494
/* msMaybeAllocateClassStyle()
3495
**
3496
** Ensure that requested style index exists and has been initialized.
3497
**
3498
** Returns MS_SUCCESS/MS_FAILURE.
3499
*/
3500
int msMaybeAllocateClassStyle(classObj *c, int idx) {
3,208✔
3501
  if (c == NULL)
3,208✔
3502
    return MS_FAILURE;
3503

3504
  if (idx < 0) {
3,208✔
3505
    msSetError(MS_MISCERR, "Invalid style index: %d",
×
3506
               "msMaybeAllocateClassStyle()", idx);
3507
    return MS_FAILURE;
×
3508
  }
3509

3510
  /* Alloc empty styles as needed up to idx.
3511
   * Nothing to do if requested style already exists
3512
   */
3513
  while (c->numstyles <= idx) {
6,414✔
3514
    if (msGrowClassStyles(c) == NULL)
3,206✔
3515
      return MS_FAILURE;
3516

3517
    if (initStyle(c->styles[c->numstyles]) == MS_FAILURE) {
3,206✔
3518
      msSetError(MS_MISCERR, "Failed to init new styleObj",
×
3519
                 "msMaybeAllocateClassStyle()");
3520
      freeStyle(c->styles[c->numstyles]);
×
3521
      free(c->styles[c->numstyles]);
×
3522
      c->styles[c->numstyles] = NULL;
×
3523
      return (MS_FAILURE);
×
3524
    }
3525
    c->numstyles++;
3,206✔
3526
  }
3527
  return MS_SUCCESS;
3528
}
3529

3530
/*
3531
 * Reset style info in the class to defaults
3532
 * the only members we don't touch are name, expression, and join/query stuff
3533
 * This is used with STYLEITEM before overwriting the contents of a class.
3534
 */
3535
void resetClassStyle(classObj *class) {
51✔
3536
  int i;
3537

3538
  /* reset labels */
3539
  for (i = 0; i < class->numlabels; i++) {
54✔
3540
    if (class->labels[i] != NULL) {
3✔
3541
      if (freeLabel(class->labels[i]) == MS_SUCCESS) {
3✔
3542
        msFree(class->labels[i]);
3✔
3543
      }
3544
      class->labels[i] = NULL;
3✔
3545
    }
3546
  }
3547
  class->numlabels = 0;
51✔
3548

3549
  msFreeExpression(&(class->text));
51✔
3550
  msInitExpression(&(class->text));
51✔
3551

3552
  /* reset styles */
3553
  for (i = 0; i < class->numstyles; i++) {
132✔
3554
    if (class->styles[i] != NULL) {
81✔
3555
      if (freeStyle(class->styles[i]) == MS_SUCCESS) {
81✔
3556
        msFree(class->styles[i]);
81✔
3557
      }
3558
      class->styles[i] = NULL;
81✔
3559
    }
3560
  }
3561
  class->numstyles = 0;
51✔
3562

3563
  class->layer = NULL;
51✔
3564
}
51✔
3565

3566
labelObj *msGrowClassLabels(classObj *class) {
3,305✔
3567

3568
  /* Do we need to increase the size of labels[] by MS_LABEL_ALLOCSIZE?
3569
   */
3570
  if (class->numlabels == class->maxlabels) {
3,305✔
3571
    labelObj **newLabelPtr;
3572
    int i, newsize;
3573

3574
    newsize = class->maxlabels + MS_LABEL_ALLOCSIZE;
3,234✔
3575

3576
    /* Alloc/realloc labels */
3577
    newLabelPtr =
3578
        (labelObj **)realloc(class->labels, newsize * sizeof(labelObj *));
3,234✔
3579
    MS_CHECK_ALLOC(newLabelPtr, newsize * sizeof(labelObj *), NULL);
3,234✔
3580

3581
    class->labels = newLabelPtr;
3,234✔
3582
    class->maxlabels = newsize;
3,234✔
3583
    for (i = class->numlabels; i < class->maxlabels; i++) {
9,702✔
3584
      class->labels[i] = NULL;
6,468✔
3585
    }
3586
  }
3587

3588
  if (class->labels[class->numlabels] == NULL) {
3,305✔
3589
    class->labels[class->numlabels] = (labelObj *)calloc(1, sizeof(labelObj));
3,305✔
3590
    MS_CHECK_ALLOC(class->labels[class->numlabels], sizeof(labelObj), NULL);
3,305✔
3591
  }
3592

3593
  return class->labels[class->numlabels];
3,305✔
3594
}
3595

3596
int loadClass(classObj *class, layerObj *layer) {
13,068✔
3597
  if (!class || !layer)
13,068✔
3598
    return (-1);
3599

3600
  class->layer = (layerObj *)layer;
13,068✔
3601

3602
  for (;;) {
3603
    switch (msyylex()) {
43,617✔
3604
    case (CLASS):
3605
      break; /* for string loads */
3606
    case (DEBUG):
×
3607
      if ((class->debug = getSymbol(3, MS_ON, MS_OFF, MS_NUMBER)) == -1)
×
3608
        return (-1);
3609
      if (class->debug == MS_NUMBER) {
×
3610
        if (msCheckNumber(msyynumber, MS_NUM_CHECK_RANGE, 0, 5) == MS_FAILURE) {
×
3611
          msSetError(MS_MISCERR,
×
3612
                     "Invalid DEBUG level, must be between 0 and 5 (line %d)",
3613
                     "loadClass()", msyylineno);
3614
          return (-1);
×
3615
        }
3616
        class->debug = (int)msyynumber;
×
3617
      }
3618
      break;
3619
    case (EOF):
×
3620
      msSetError(MS_EOFERR, NULL, "loadClass()");
×
3621
      return (-1);
×
3622
    case (END):
3623
      return (0);
3624
      break;
3625
    case (EXPRESSION):
3,316✔
3626
      if (loadExpression(&(class->expression)) == -1)
3,316✔
3627
        return (-1); /* loadExpression() cleans up previously allocated
3628
                        expression */
3629
      break;
3630
    case (GROUP):
1,770✔
3631
      if (getString(&class->group) == MS_FAILURE)
1,770✔
3632
        return (-1); /* getString() cleans up previously allocated string */
3633
      break;
3634
    case (KEYIMAGE):
29✔
3635
      if (getString(&class->keyimage) == MS_FAILURE)
29✔
3636
        return (-1); /* getString() cleans up previously allocated string */
3637
      break;
3638
    case (LABEL):
3,129✔
3639
      if (msGrowClassLabels(class) == NULL)
3,129✔
3640
        return (-1);
3641
      initLabel(class->labels[class->numlabels]);
3,129✔
3642
      class->labels[class->numlabels]->size =
3,129✔
3643
          MS_MEDIUM; /* only set a default if the LABEL section is present */
3644
      if (loadLabel(class->labels[class->numlabels]) == -1) {
3,129✔
3645
        freeLabel(class->labels[class->numlabels]);
×
3646
        free(class->labels[class->numlabels]);
×
3647
        class->labels[class->numlabels] = NULL;
×
3648
        return (-1);
×
3649
      }
3650
      class->numlabels++;
3,129✔
3651
      break;
3,129✔
3652
    case (LEADER):
15✔
3653
      if (!class->leader) {
15✔
3654
        class->leader = msSmallMalloc(sizeof(labelLeaderObj));
15✔
3655
        initLeader(class->leader);
15✔
3656
      }
3657
      if (loadLeader(class->leader) == -1)
15✔
3658
        return (-1);
3659
      break;
3660
    case (MAXSCALE):
44✔
3661
    case (MAXSCALEDENOM):
3662
      if (getDouble(&(class->maxscaledenom), MS_NUM_CHECK_GTE, 0, -1) == -1)
44✔
3663
        return (-1);
3664
      break;
3665
    case (METADATA):
402✔
3666
      if (loadHashTable(&(class->metadata)) != MS_SUCCESS)
402✔
3667
        return (-1);
3668
      break;
3669
    case (MINSCALE):
32✔
3670
    case (MINSCALEDENOM):
3671
      if (getDouble(&(class->minscaledenom), MS_NUM_CHECK_GTE, 0, -1) == -1)
32✔
3672
        return (-1);
3673
      break;
3674
    case (MINFEATURESIZE):
×
3675
      if (getInteger(&(class->minfeaturesize), MS_NUM_CHECK_GT, 0, -1) == -1)
×
3676
        return (-1);
3677
      break;
3678
    case (NAME):
8,026✔
3679
      if (getString(&class->name) == MS_FAILURE)
8,026✔
3680
        return (-1);
3681
      break;
3682
    case (STATUS):
×
3683
      if ((class->status = getSymbol(2, MS_ON, MS_OFF)) == -1)
×
3684
        return (-1);
3685
      break;
3686
    case (STYLE):
12,843✔
3687
      if (msGrowClassStyles(class) == NULL)
12,843✔
3688
        return (-1);
3689
      initStyle(class->styles[class->numstyles]);
12,843✔
3690
      if (loadStyle(class->styles[class->numstyles]) != MS_SUCCESS) {
12,843✔
3691
        freeStyle(class->styles[class->numstyles]);
×
3692
        free(class->styles[class->numstyles]);
×
3693
        class->styles[class->numstyles] = NULL;
×
3694
        return (-1);
×
3695
      }
3696
      class->numstyles++;
12,843✔
3697
      break;
12,843✔
3698
    case (TEMPLATE):
402✔
3699
      if (getString(&class->template) == MS_FAILURE)
402✔
3700
        return (-1); /* getString() cleans up previously allocated string */
3701
      break;
3702
    case (TEXT):
493✔
3703
      if (loadExpression(&(class->text)) == -1)
493✔
3704
        return (-1); /* loadExpression() cleans up previously allocated
3705
                        expression */
3706
      if ((class->text.type != MS_STRING) &&
493✔
3707
          (class->text.type != MS_EXPRESSION)) {
3708
        msSetError(
×
3709
            MS_MISCERR,
3710
            "Text expressions support constant or tagged replacement strings.",
3711
            "loadClass()");
3712
        return (-1);
×
3713
      }
3714
      break;
3715
    case (TITLE):
32✔
3716
      if (getString(&class->title) == MS_FAILURE)
32✔
3717
        return (-1); /* getString() cleans up previously allocated string */
3718
      break;
3719
    case (VALIDATION):
13✔
3720
      if (loadHashTable(&(class->validation)) != MS_SUCCESS)
13✔
3721
        return (-1);
3722
      break;
3723
    case (FALLBACK):
3✔
3724
      if ((class->isfallback = getSymbol(2, MS_TRUE, MS_FALSE)) == -1)
3✔
3725
        return (-1);
3726
      break;
3727
    default:
×
3728
      if (strlen(msyystring_buffer) > 0) {
×
3729
        msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
3730
                   "loadClass()", msyystring_buffer, msyylineno);
3731
        return (-1);
×
3732
      } else {
3733
        return (0); /* end of a string, not an error */
3734
      }
3735
    }
3736
  }
3737
}
3738

3739
static int classResolveSymbolNames(classObj *class) {
13,068✔
3740
  int i, j;
3741
  int try_addimage_if_notfound = MS_TRUE;
3742

3743
  /* step through styles and labels to resolve symbol names */
3744
  /* class styles */
3745
  for (i = 0; i < class->numstyles; i++) {
25,911✔
3746
    if (class->styles[i]->symbolname) {
12,843✔
3747
      if ((class->styles[i]->symbol = msGetSymbolIndex(
1,825✔
3748
               &(class->layer->map->symbolset), class->styles[i]->symbolname,
1,825✔
3749
               try_addimage_if_notfound)) == -1) {
3750
        msSetError(MS_MISCERR,
×
3751
                   "Undefined symbol \"%s\" in class, style %d of layer %s.",
3752
                   "classResolveSymbolNames()", class->styles[i]->symbolname, i,
×
3753
                   class->layer->name);
×
3754
        return MS_FAILURE;
×
3755
      }
3756
    }
3757
  }
3758

3759
  /* label styles */
3760
  for (i = 0; i < class->numlabels; i++) {
16,197✔
3761
    for (j = 0; j < class->labels[i]->numstyles; j++) {
4,295✔
3762
      if (class->labels[i]->styles[j]->symbolname) {
1,166✔
3763
        if ((class->labels[i]->styles[j]->symbol =
694✔
3764
                 msGetSymbolIndex(&(class->layer->map->symbolset),
694✔
3765
                                  class->labels[i]->styles[j]->symbolname,
3766
                                  try_addimage_if_notfound)) == -1) {
3767
          msSetError(
×
3768
              MS_MISCERR,
3769
              "Undefined symbol \"%s\" in class, label style %d of layer %s.",
3770
              "classResolveSymbolNames()",
3771
              class->labels[i]->styles[j]->symbolname, j, class->layer->name);
×
3772
          return MS_FAILURE;
×
3773
        }
3774
      }
3775
    }
3776
  }
3777

3778
  return MS_SUCCESS;
3779
}
3780

3781
int msUpdateClassFromString(classObj *class, char *string) {
×
3782
  if (!class || !string)
×
3783
    return MS_FAILURE;
3784

3785
  msAcquireLock(TLOCK_PARSER);
×
3786

3787
  msyystate = MS_TOKENIZE_STRING;
×
3788
  msyystring = string;
×
3789
  msyylex(); /* sets things up, but doesn't process any tokens */
×
3790

3791
  msyylineno = 1; /* start at line 1 */
×
3792

3793
  if (loadClass(class, class->layer) == -1) {
×
3794
    msReleaseLock(TLOCK_PARSER);
×
3795
    return MS_FAILURE; /* parse error */
×
3796
    ;
3797
  }
3798

3799
  msyylex_destroy();
×
3800
  msReleaseLock(TLOCK_PARSER);
×
3801

3802
  if (classResolveSymbolNames(class) != MS_SUCCESS)
×
3803
    return MS_FAILURE;
3804

3805
  return MS_SUCCESS;
3806
}
3807

3808
static void writeClass(FILE *stream, int indent, classObj *class) {
9✔
3809
  int i;
3810

3811
  if (class->status == MS_DELETE)
9✔
3812
    return;
3813

3814
  indent++;
9✔
3815
  writeBlockBegin(stream, indent, "CLASS");
9✔
3816
  writeString(stream, indent, "NAME", NULL, class->name);
9✔
3817
  writeString(stream, indent, "GROUP", NULL, class->group);
9✔
3818
  writeNumber(stream, indent, "DEBUG", 0, class->debug);
9✔
3819
  writeExpression(stream, indent, "EXPRESSION", &(class->expression));
9✔
3820
  writeString(stream, indent, "KEYIMAGE", NULL, class->keyimage);
9✔
3821
  for (i = 0; i < class->numlabels; i++)
12✔
3822
    writeLabel(stream, indent, class->labels[i]);
3✔
3823
  if (class->leader)
9✔
3824
    writeLeader(stream, indent, class->leader);
×
3825
  writeNumber(stream, indent, "MAXSCALEDENOM", -1, class->maxscaledenom);
9✔
3826
  writeHashTable(stream, indent, "METADATA", &(class->metadata));
9✔
3827
  writeNumber(stream, indent, "MINSCALEDENOM", -1, class->minscaledenom);
9✔
3828
  writeNumber(stream, indent, "MINFEATURESIZE", -1, class->minfeaturesize);
9✔
3829
  writeKeyword(stream, indent, "STATUS", class->status, 1, MS_OFF, "OFF");
9✔
3830
  for (i = 0; i < class->numstyles; i++)
18✔
3831
    writeStyle(stream, indent, class->styles[i]);
9✔
3832
  writeString(stream, indent, "TEMPLATE", NULL, class->template);
9✔
3833
  writeExpression(stream, indent, "TEXT", &(class->text));
9✔
3834
  writeString(stream, indent, "TITLE", NULL, class->title);
9✔
3835
  writeHashTable(stream, indent, "VALIDATION", &(class->validation));
9✔
3836
  writeKeyword(stream, indent, "FALLBACK", class->isfallback, 1, MS_TRUE,
9✔
3837
               "TRUE");
3838
  writeBlockEnd(stream, indent, "CLASS");
9✔
3839
}
3840

3841
static void writeIdentify(FILE *stream, int indent, layerObj *layer) {
7✔
3842
  if (layer->identificationclassauto || layer->identificationclassgroup) {
7✔
3843
    indent++;
×
3844
    writeBlockBegin(stream, indent, "IDENTIFY");
×
3845
    if (layer->identificationclassauto) {
×
3846
      writeIndent(stream, indent + 1);
×
3847
      msIO_fprintf(stream, "CLASSAUTO\n");
×
3848
    }
3849
    if (layer->identificationclassgroup)
×
3850
      writeString(stream, indent, "CLASSGROUP", NULL,
×
3851
                  layer->identificationclassgroup);
3852
    writeBlockEnd(stream, indent, "IDENTIFY");
×
3853
  }
3854
}
7✔
3855

3856
char *msWriteClassToString(classObj *class) {
×
3857
  msIOContext context;
3858
  msIOBuffer buffer;
3859

3860
  context.label = NULL;
×
3861
  context.write_channel = MS_TRUE;
×
3862
  context.readWriteFunc = msIO_bufferWrite;
×
3863
  context.cbData = &buffer;
×
3864
  buffer.data = NULL;
×
3865
  buffer.data_len = 0;
×
3866
  buffer.data_offset = 0;
×
3867

3868
  msIO_installHandlers(NULL, &context, NULL);
×
3869

3870
  writeClass(stdout, -1, class);
×
3871
  msIO_bufferWrite(&buffer, "", 1);
×
3872

3873
  msIO_installHandlers(NULL, NULL, NULL);
×
3874

3875
  return (char *)buffer.data;
×
3876
}
3877

3878
int initCompositingFilter(CompositingFilter *filter) {
×
3879
  filter->filter = NULL;
×
3880
  filter->next = NULL;
×
3881
  return MS_SUCCESS;
×
3882
}
3883

3884
void freeCompositingFilter(CompositingFilter *filter) {
82✔
3885
  if (!filter)
82✔
3886
    return;
3887
  if (filter->next)
×
3888
    freeCompositingFilter(filter->next);
×
3889
  free(filter->filter);
×
3890
  free(filter);
×
3891
}
3892

3893
int initLayerCompositer(LayerCompositer *compositer) {
82✔
3894
  compositer->comp_op = MS_COMPOP_SRC_OVER;
82✔
3895
  compositer->opacity = 100;
82✔
3896
  compositer->next = NULL;
82✔
3897
  compositer->filter = NULL;
82✔
3898
  return MS_SUCCESS;
82✔
3899
}
3900

3901
void freeLayerCompositer(LayerCompositer *compositer) {
82✔
3902
  if (!compositer)
82✔
3903
    return;
3904
  if (compositer->next)
82✔
3905
    freeLayerCompositer(compositer->next);
×
3906
  freeCompositingFilter(compositer->filter);
82✔
3907
  free(compositer);
82✔
3908
}
3909

3910
/*
3911
** Initialize, load and free a single layer structure
3912
*/
3913
int initLayer(layerObj *layer, mapObj *map) {
14,153✔
3914
  if (layer == NULL) {
14,153✔
3915
    msSetError(MS_MEMERR, "Layer is null", "initLayer()");
×
3916
    return (-1);
×
3917
  }
3918
  layer->debug = (int)msGetGlobalDebugLevel();
14,153✔
3919
  MS_REFCNT_INIT(layer);
14,153✔
3920

3921
  /* Set maxclasses = 0, class[] will be allocated as needed on first call
3922
   * to msGrowLayerClasses()
3923
   */
3924
  layer->numclasses = 0;
14,153✔
3925
  layer->maxclasses = 0;
14,153✔
3926
  layer->class = NULL;
14,153✔
3927

3928
  layer->name = NULL;
14,153✔
3929
  layer->group = NULL;
14,153✔
3930
  layer->status = MS_OFF;
14,153✔
3931
  layer->data = NULL;
14,153✔
3932
  layer->rendermode = MS_FIRST_MATCHING_CLASS;
14,153✔
3933

3934
  layer->map = map; /* point back to the encompassing structure */
14,153✔
3935

3936
  layer->type = -1;
14,153✔
3937

3938
  layer->toleranceunits = MS_PIXELS;
14,153✔
3939
  layer->tolerance =
14,153✔
3940
      -1; /* perhaps this should have a different value based on type */
3941
  layer->identificationclassauto = MS_FALSE;
14,153✔
3942
  layer->identificationclassgroup = NULL;
14,153✔
3943

3944
  layer->symbolscaledenom = -1.0; /* -1 means nothing is set */
14,153✔
3945
  layer->scalefactor = 1.0;
14,153✔
3946
  layer->maxscaledenom = -1.0;
14,153✔
3947
  layer->minscaledenom = -1.0;
14,153✔
3948
  layer->minfeaturesize = -1; /* no limit */
14,153✔
3949
  layer->maxgeowidth = -1.0;
14,153✔
3950
  layer->mingeowidth = -1.0;
14,153✔
3951

3952
  layer->sizeunits = MS_PIXELS;
14,153✔
3953

3954
  layer->maxfeatures = -1; /* no quota */
14,153✔
3955
  layer->startindex = -1;  /*used for pagination*/
14,153✔
3956

3957
  layer->scaletokens = NULL;
14,153✔
3958
  layer->numscaletokens = 0;
14,153✔
3959

3960
  layer->template = layer->header = layer->footer = NULL;
14,153✔
3961

3962
  layer->transform = MS_TRUE;
14,153✔
3963

3964
  layer->classitem = NULL;
14,153✔
3965
  layer->classitemindex = -1;
14,153✔
3966

3967
  layer->units = MS_METERS;
14,153✔
3968
  if (msInitProjection(&(layer->projection)) == -1)
14,153✔
3969
    return (-1);
3970

3971
  if (map) {
14,153✔
3972
    msProjectionInheritContextFrom(&(layer->projection), &(map->projection));
14,122✔
3973
  }
3974

3975
  layer->project = MS_TRUE;
14,153✔
3976
  layer->reprojectorLayerToMap = NULL;
14,153✔
3977
  layer->reprojectorMapToLayer = NULL;
14,153✔
3978

3979
  initCluster(&layer->cluster);
14,153✔
3980

3981
  MS_INIT_COLOR(layer->offsite, -1, -1, -1, 255);
14,153✔
3982

3983
  layer->labelcache = MS_ON;
14,153✔
3984
  layer->postlabelcache = MS_FALSE;
14,153✔
3985

3986
  layer->labelitem = NULL;
14,153✔
3987
  layer->labelitemindex = -1;
14,153✔
3988

3989
  layer->labelmaxscaledenom = -1;
14,153✔
3990
  layer->labelminscaledenom = -1;
14,153✔
3991

3992
  layer->tileitem = msStrdup("location");
14,153✔
3993
  layer->tileitemindex = -1;
14,153✔
3994
  layer->tileindex = NULL;
14,153✔
3995
  layer->tilesrs = NULL;
14,153✔
3996

3997
  layer->bandsitem = NULL;
14,153✔
3998
  layer->bandsitemindex = -1;
14,153✔
3999

4000
  layer->currentfeature = layer->features = NULL;
14,153✔
4001

4002
  layer->connection = NULL;
14,153✔
4003
  layer->plugin_library = NULL;
14,153✔
4004
  layer->plugin_library_original = NULL;
14,153✔
4005
  layer->connectiontype = MS_SHAPEFILE;
14,153✔
4006
  layer->vtable = NULL;
14,153✔
4007
  layer->classgroup = NULL;
14,153✔
4008

4009
  layer->layerinfo = NULL;
14,153✔
4010
  layer->wfslayerinfo = NULL;
14,153✔
4011

4012
  layer->items = NULL;
14,153✔
4013
  layer->iteminfo = NULL;
14,153✔
4014
  layer->numitems = 0;
14,153✔
4015

4016
  layer->resultcache = NULL;
14,153✔
4017

4018
  msInitExpression(&(layer->filter));
14,153✔
4019
  layer->filteritem = NULL;
14,153✔
4020
  layer->filteritemindex = -1;
14,153✔
4021

4022
  layer->
4023
    requires
4024
  = layer->labelrequires = NULL;
14,153✔
4025

4026
  initHashTable(&(layer->metadata));
14,153✔
4027
  initHashTable(&(layer->bindvals));
14,153✔
4028
  initHashTable(&(layer->validation));
14,153✔
4029

4030
  layer->styleitem = NULL;
14,153✔
4031
  layer->styleitemindex = -1;
14,153✔
4032

4033
  layer->processing = NULL;
14,153✔
4034
  layer->numjoins = 0;
14,153✔
4035
  layer->joins = (joinObj *)malloc(MS_MAXJOINS * sizeof(joinObj));
14,153✔
4036
  MS_CHECK_ALLOC(layer->joins, MS_MAXJOINS * sizeof(joinObj), -1);
14,153✔
4037

4038
  layer->extent.minx = -1.0;
14,153✔
4039
  layer->extent.miny = -1.0;
14,153✔
4040
  layer->extent.maxx = -1.0;
14,153✔
4041
  layer->extent.maxy = -1.0;
14,153✔
4042

4043
  layer->mask = NULL;
14,153✔
4044
  layer->maskimage = NULL;
14,153✔
4045
  layer->grid = NULL;
14,153✔
4046

4047
  msInitExpression(&(layer->_geomtransform));
14,153✔
4048
  layer->_geomtransform.type = MS_GEOMTRANSFORM_NONE;
14,153✔
4049

4050
  msInitExpression(&(layer->utfdata));
14,153✔
4051
  layer->utfitem = NULL;
14,153✔
4052
  layer->utfitemindex = -1;
14,153✔
4053

4054
  layer->encoding = NULL;
14,153✔
4055

4056
  layer->sortBy.nProperties = 0;
14,153✔
4057
  layer->sortBy.properties = NULL;
14,153✔
4058
  layer->orig_st = NULL;
14,153✔
4059

4060
  layer->compositer = NULL;
14,153✔
4061

4062
  initHashTable(&(layer->connectionoptions));
14,153✔
4063

4064
  return (0);
14,153✔
4065
}
4066

4067
int initScaleToken(scaleTokenObj *token) {
74✔
4068
  token->n_entries = 0;
74✔
4069
  token->name = NULL;
74✔
4070
  token->tokens = NULL;
74✔
4071
  return MS_SUCCESS;
74✔
4072
}
4073

4074
int freeScaleTokenEntry(scaleTokenEntryObj *token) {
168✔
4075
  msFree(token->value);
168✔
4076
  return MS_SUCCESS;
168✔
4077
}
4078

4079
int freeScaleToken(scaleTokenObj *scaletoken) {
74✔
4080
  int i;
4081
  msFree(scaletoken->name);
74✔
4082
  for (i = 0; i < scaletoken->n_entries; i++) {
242✔
4083
    freeScaleTokenEntry(&scaletoken->tokens[i]);
168✔
4084
  }
4085
  msFree(scaletoken->tokens);
74✔
4086
  return MS_SUCCESS;
74✔
4087
}
4088

4089
int freeLayer(layerObj *layer) {
14,454✔
4090
  int i;
4091
  if (!layer)
14,454✔
4092
    return MS_FAILURE;
4093
  if (MS_REFCNT_DECR_IS_NOT_ZERO(layer)) {
14,454✔
4094
    return MS_FAILURE;
4095
  }
4096

4097
  if (layer->debug >= MS_DEBUGLEVEL_VVV)
14,118✔
4098
    msDebug("freeLayer(): freeing layer at %p.\n", layer);
21✔
4099

4100
  if (msLayerIsOpen(layer))
14,118✔
4101
    msLayerClose(layer);
909✔
4102

4103
  msFree(layer->name);
14,118✔
4104
  msFree(layer->encoding);
14,118✔
4105
  msFree(layer->group);
14,118✔
4106
  msFree(layer->data);
14,118✔
4107
  msFree(layer->classitem);
14,118✔
4108
  msFree(layer->labelitem);
14,118✔
4109
  msFree(layer->header);
14,118✔
4110
  msFree(layer->footer);
14,118✔
4111
  msFree(layer->template);
14,118✔
4112
  msFree(layer->tileindex);
14,118✔
4113
  msFree(layer->tileitem);
14,118✔
4114
  msFree(layer->tilesrs);
14,118✔
4115
  msFree(layer->bandsitem);
14,118✔
4116
  msFree(layer->plugin_library);
14,118✔
4117
  msFree(layer->plugin_library_original);
14,118✔
4118
  msFree(layer->connection);
14,118✔
4119
  msFree(layer->vtable);
14,118✔
4120
  msFree(layer->classgroup);
14,118✔
4121

4122
  msProjectDestroyReprojector(layer->reprojectorLayerToMap);
14,118✔
4123
  msProjectDestroyReprojector(layer->reprojectorMapToLayer);
14,118✔
4124
  msFreeProjection(&(layer->projection));
14,118✔
4125
  msFreeExpression(&layer->_geomtransform);
14,118✔
4126

4127
  freeCluster(&layer->cluster);
14,118✔
4128

4129
  for (i = 0; i < layer->maxclasses; i++) {
106,838✔
4130
    if (layer->class[i] != NULL) {
92,720✔
4131
      layer->class[i]->layer = NULL;
18,916✔
4132
      if (freeClass(layer->class[i]) == MS_SUCCESS) {
18,916✔
4133
        msFree(layer->class[i]);
18,907✔
4134
      }
4135
    }
4136
  }
4137
  msFree(layer->class);
14,118✔
4138

4139
  if (layer->numscaletokens > 0) {
14,118✔
4140
    for (i = 0; i < layer->numscaletokens; i++) {
120✔
4141
      freeScaleToken(&layer->scaletokens[i]);
74✔
4142
    }
4143
    msFree(layer->scaletokens);
46✔
4144
  }
4145

4146
  if (layer->features)
14,118✔
4147
    freeFeatureList(layer->features);
2,454✔
4148

4149
  if (layer->resultcache) {
14,118✔
4150
    cleanupResultCache(layer->resultcache);
921✔
4151
    msFree(layer->resultcache);
921✔
4152
  }
4153

4154
  msFree(layer->identificationclassgroup);
14,118✔
4155

4156
  msFree(layer->styleitem);
14,118✔
4157

4158
  msFree(layer->filteritem);
14,118✔
4159
  msFreeExpression(&(layer->filter));
14,118✔
4160

4161
  msFree(layer->requires);
14,118✔
4162
  msFree(layer->labelrequires);
14,118✔
4163

4164
  msFreeHashItems(&(layer->metadata));
14,118✔
4165
  msFreeHashItems(&(layer->validation));
14,118✔
4166
  msFreeHashItems(&layer->bindvals);
14,118✔
4167

4168
  CSLDestroy(layer->processing);
14,118✔
4169

4170
  for (i = 0; i < layer->numjoins; i++) /* each join */
14,118✔
4171
    freeJoin(&(layer->joins[i]));
×
4172
  msFree(layer->joins);
14,118✔
4173
  layer->numjoins = 0;
14,118✔
4174

4175
  layer->classgroup = NULL;
14,118✔
4176

4177
  msFree(layer->mask);
14,118✔
4178
  if (layer->maskimage) {
14,118✔
4179
    msFreeImage(layer->maskimage);
47✔
4180
  }
4181

4182
  if (layer->compositer) {
14,118✔
4183
    freeLayerCompositer(layer->compositer);
82✔
4184
  }
4185

4186
  if (layer->grid) {
14,118✔
4187
    freeGrid(layer->grid);
8✔
4188
    msFree(layer->grid);
8✔
4189
  }
4190

4191
  msFreeExpression(&(layer->utfdata));
14,118✔
4192
  msFree(layer->utfitem);
14,118✔
4193

4194
  for (i = 0; i < layer->sortBy.nProperties; i++)
14,131✔
4195
    msFree(layer->sortBy.properties[i].item);
13✔
4196
  msFree(layer->sortBy.properties);
14,118✔
4197

4198
  msFreeHashItems(&layer->connectionoptions);
14,118✔
4199

4200
  return MS_SUCCESS;
14,118✔
4201
}
4202

4203
/*
4204
** Ensure there is at least one free entry in the class array of this
4205
** layerObj. Grow the allocated class array if necessary and allocate
4206
** a new class for class[numclasses] if there is not already one,
4207
** setting its contents to all zero bytes (i.e. does not call initClass()
4208
** on it).
4209
**
4210
** This function is safe to use for the initial allocation of the class[]
4211
** array as well (i.e. when maxclasses==0 and class==NULL)
4212
**
4213
** Returns a reference to the new classObj on success, NULL on error.
4214
*/
4215
classObj *msGrowLayerClasses(layerObj *layer) {
19,157✔
4216
  /* Do we need to increase the size of class[] by  MS_CLASS_ALLOCSIZE?
4217
   */
4218
  if (layer->numclasses == layer->maxclasses) {
19,157✔
4219
    classObj **newClassPtr;
4220
    int i, newsize;
4221

4222
    newsize = layer->maxclasses + MS_CLASS_ALLOCSIZE;
11,619✔
4223

4224
    /* Alloc/realloc classes */
4225
    newClassPtr =
4226
        (classObj **)realloc(layer->class, newsize * sizeof(classObj *));
11,619✔
4227
    MS_CHECK_ALLOC(newClassPtr, newsize * sizeof(classObj *), NULL);
11,619✔
4228

4229
    layer->class = newClassPtr;
11,619✔
4230
    layer->maxclasses = newsize;
11,619✔
4231
    for (i = layer->numclasses; i < layer->maxclasses; i++) {
104,571✔
4232
      layer->class[i] = NULL;
92,952✔
4233
    }
4234
  }
4235

4236
  if (layer->class[layer->numclasses] == NULL) {
19,157✔
4237
    layer->class[layer->numclasses] = (classObj *)calloc(1, sizeof(classObj));
19,157✔
4238
    MS_CHECK_ALLOC(layer->class[layer->numclasses], sizeof(classObj), NULL);
19,157✔
4239
  }
4240

4241
  return layer->class[layer->numclasses];
19,157✔
4242
}
4243

4244
scaleTokenObj *msGrowLayerScaletokens(layerObj *layer) {
74✔
4245
  layer->scaletokens = msSmallRealloc(
148✔
4246
      layer->scaletokens, (layer->numscaletokens + 1) * sizeof(scaleTokenObj));
74✔
4247
  memset(&layer->scaletokens[layer->numscaletokens], 0, sizeof(scaleTokenObj));
74✔
4248
  return &layer->scaletokens[layer->numscaletokens];
74✔
4249
}
4250

4251
int loadScaletoken(scaleTokenObj *token, layerObj *layer) {
74✔
4252
  (void)layer;
4253
  for (;;) {
4254
    int stop = 0;
4255
    switch (msyylex()) {
222✔
4256
    case (EOF):
×
4257
      msSetError(MS_EOFERR, NULL, "loadScaletoken()");
×
4258
      return (MS_FAILURE);
×
4259
    case (NAME):
74✔
4260
      if (getString(&token->name) == MS_FAILURE)
74✔
4261
        return (MS_FAILURE);
4262
      break;
4263
    case (VALUES):
242✔
4264
      for (;;) {
4265
        if (stop)
4266
          break;
4267
        switch (msyylex()) {
242✔
4268
        case (EOF):
×
4269
          msSetError(MS_EOFERR, NULL, "loadScaletoken()");
×
4270
          return (MS_FAILURE);
×
4271
        case (END):
74✔
4272
          stop = 1;
4273
          if (token->n_entries == 0) {
74✔
4274
            msSetError(MS_PARSEERR,
×
4275
                       "Scaletoken (line:%d) has no VALUES defined",
4276
                       "loadScaleToken()", msyylineno);
4277
            return (MS_FAILURE);
×
4278
          }
4279
          token->tokens[token->n_entries - 1].maxscale = DBL_MAX;
74✔
4280
          break;
4281
        case (MS_STRING):
168✔
4282
          /* we have a key */
4283
          token->tokens =
168✔
4284
              msSmallRealloc(token->tokens, (token->n_entries + 1) *
168✔
4285
                                                sizeof(scaleTokenEntryObj));
4286

4287
          if (1 != sscanf(msyystring_buffer, "%lf",
168✔
4288
                          &token->tokens[token->n_entries].minscale)) {
168✔
4289
            msSetError(MS_PARSEERR,
×
4290
                       "failed to parse SCALETOKEN VALUE (%s):(line %d), "
4291
                       "expecting \"minscale\"",
4292
                       "loadScaletoken()", msyystring_buffer, msyylineno);
4293
            return (MS_FAILURE);
×
4294
          }
4295
          if (token->n_entries == 0) {
168✔
4296
            /* check supplied value was 0*/
4297
            if (token->tokens[0].minscale != 0) {
74✔
4298
              msSetError(MS_PARSEERR,
×
4299
                         "First SCALETOKEN VALUE (%s):(line %d) must be zero, "
4300
                         "expecting \"0\"",
4301
                         "loadScaletoken()", msyystring_buffer, msyylineno);
4302
              return (MS_FAILURE);
×
4303
            }
4304
          } else {
4305
            /* set max scale of previous token */
4306
            token->tokens[token->n_entries - 1].maxscale =
94✔
4307
                token->tokens[token->n_entries].minscale;
94✔
4308
          }
4309
          token->tokens[token->n_entries].value = NULL;
168✔
4310
          if (getString(&(token->tokens[token->n_entries].value)) == MS_FAILURE)
168✔
4311
            return (MS_FAILURE);
4312
          token->n_entries++;
168✔
4313
          break;
168✔
4314
        default:
×
4315
          msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
4316
                     "loadScaletoken()", msyystring_buffer, msyylineno);
4317
          return (MS_FAILURE);
×
4318
        }
4319
      }
4320
      break;
4321
    case (END):
74✔
4322
      if (!token->name || !*(token->name)) {
74✔
4323
        msSetError(MS_PARSEERR,
×
4324
                   "ScaleToken missing mandatory NAME entry (line %d)",
4325
                   "loadScaleToken()", msyylineno);
4326
        return MS_FAILURE;
×
4327
      }
4328
      if (token->n_entries == 0) {
74✔
4329
        msSetError(MS_PARSEERR,
×
4330
                   "ScaleToken missing at least one VALUES entry (line %d)",
4331
                   "loadScaleToken()", msyylineno);
4332
        return MS_FAILURE;
×
4333
      }
4334
      return MS_SUCCESS;
4335
    default:
×
4336
      msSetError(MS_IDENTERR, "Parsing error 2 near (%s):(line %d)",
×
4337
                 "loadScaletoken()", msyystring_buffer, msyylineno);
4338
      return (MS_FAILURE);
×
4339
    }
4340
  } /* next token*/
4341
}
4342

4343
static const struct {
4344
  CompositingOperation eOp;
4345
  const char *pszName;
4346
} CompOps[] = {
4347
    {MS_COMPOP_CLEAR, "clear"},
4348
    {MS_COMPOP_COLOR_BURN, "color-burn"},
4349
    {MS_COMPOP_COLOR_DODGE, "color-dodge"},
4350
    {MS_COMPOP_CONTRAST, "contrast"},
4351
    {MS_COMPOP_DARKEN, "darken"},
4352
    {MS_COMPOP_DIFFERENCE, "difference"},
4353
    {MS_COMPOP_DST, "dst"},
4354
    {MS_COMPOP_DST_ATOP, "dst-atop"},
4355
    {MS_COMPOP_DST_IN, "dst-in"},
4356
    {MS_COMPOP_DST_OUT, "dst-out"},
4357
    {MS_COMPOP_DST_OVER, "dst-over"},
4358
    {MS_COMPOP_EXCLUSION, "exclusion"},
4359
    {MS_COMPOP_HARD_LIGHT, "hard-light"},
4360
    {MS_COMPOP_HSL_COLOR, "hsl-color"},
4361
    {MS_COMPOP_HSL_HUE, "hsl-hue"},
4362
    {MS_COMPOP_HSL_LUMINOSITY, "hsl-luminosity"},
4363
    {MS_COMPOP_HSL_SATURATION, "hsl-saturation"},
4364
    {MS_COMPOP_INVERT, "invert"},
4365
    {MS_COMPOP_INVERT_RGB, "invert-rgb"},
4366
    {MS_COMPOP_LIGHTEN, "lighten"},
4367
    {MS_COMPOP_MINUS, "minus"},
4368
    {MS_COMPOP_MULTIPLY, "multiply"},
4369
    {MS_COMPOP_OVERLAY, "overlay"},
4370
    {MS_COMPOP_PLUS, "plus"},
4371
    {MS_COMPOP_SCREEN, "screen"},
4372
    {MS_COMPOP_SOFT_LIGHT, "soft-light"},
4373
    {MS_COMPOP_SRC, "src"},
4374
    {MS_COMPOP_SRC_ATOP, "src-atop"},
4375
    {MS_COMPOP_SRC_IN, "src-in"},
4376
    {MS_COMPOP_SRC_OUT, "src-out"},
4377
    {MS_COMPOP_SRC_OVER, "src-over"},
4378
    {MS_COMPOP_XOR, "xor"},
4379
};
4380

4381
#define SIZEOF_COMP_OPS ((int)(sizeof(CompOps) / sizeof(CompOps[0])))
4382

4383
int loadLayerCompositer(LayerCompositer *compositer) {
81✔
4384
  for (;;) {
4385
    switch (msyylex()) {
176✔
4386
    case COMPFILTER: {
×
4387
      CompositingFilter **filter = &compositer->filter;
×
4388
      while (*filter) {
×
4389
        filter = &((*filter)->next);
×
4390
      }
4391
      *filter = msSmallMalloc(sizeof(CompositingFilter));
×
4392
      initCompositingFilter(*filter);
×
4393
      if (getString(&((*filter)->filter)) == MS_FAILURE)
×
4394
        return (MS_FAILURE);
4395
    } break;
4396
    case COMPOP: {
14✔
4397
      char *compop = NULL;
14✔
4398
      if (getString(&compop) == MS_FAILURE)
14✔
4399
        return (MS_FAILURE);
×
4400

4401
      bool bFound = false;
4402
      for (int i = 0; i < SIZEOF_COMP_OPS; ++i) {
240✔
4403
        if (strcmp(compop, CompOps[i].pszName) == 0) {
240✔
4404
          bFound = true;
4405
          compositer->comp_op = CompOps[i].eOp;
14✔
4406
          break;
4407
        }
4408
      }
4409
      if (!bFound) {
4410
        msSetError(MS_PARSEERR, "Unknown COMPOP \"%s\"",
×
4411
                   "loadLayerCompositer()", compop);
4412
        free(compop);
×
4413
        return MS_FAILURE;
×
4414
      }
4415
      free(compop);
14✔
4416
    } break;
14✔
4417
    case END:
4418
      return MS_SUCCESS;
4419
    case OPACITY:
81✔
4420
      if (getInteger(&(compositer->opacity), MS_NUM_CHECK_RANGE, 0, 100) ==
81✔
4421
          -1) {
4422
        msSetError(MS_PARSEERR, "OPACITY must be between 0 and 100 (line %d)",
×
4423
                   "loadLayerCompositer()", msyylineno);
4424
        return MS_FAILURE;
×
4425
      }
4426
      break;
4427
    default:
×
4428
      msSetError(MS_IDENTERR, "Parsing error 2 near (%s):(line %d)",
×
4429
                 "loadLayerCompositer()", msyystring_buffer, msyylineno);
4430
      return (MS_FAILURE);
×
4431
    }
4432
  }
4433
}
4434

4435
static int loadIdentification(layerObj *layer) {
80✔
4436
  for (;;) {
4437
    switch (msyylex()) {
160✔
4438
    case TOLERANCE:
×
4439
      if (getDouble(&(layer->tolerance), MS_NUM_CHECK_GTE, 0, -1) == -1)
×
4440
        return (-1);
4441
      break;
4442

4443
    case TOLERANCEUNITS:
×
4444
      if ((layer->toleranceunits = getSymbol(
×
4445
               8, MS_INCHES, MS_FEET, MS_MILES, MS_METERS, MS_KILOMETERS,
4446
               MS_NAUTICALMILES, MS_DD, MS_PIXELS)) == -1)
4447
        return (-1);
4448
      break;
4449

4450
    case CLASSGROUP:
16✔
4451
      if (getString(&layer->identificationclassgroup) == MS_FAILURE)
16✔
4452
        return (-1);
4453
      break;
4454

4455
    case CLASSAUTO:
64✔
4456
      layer->identificationclassauto = MS_TRUE;
64✔
4457
      break;
64✔
4458

4459
    case END:
4460
      return MS_SUCCESS;
4461

4462
    default:
×
4463
      msSetError(MS_IDENTERR, "Parsing error 2 near (%s):(line %d)",
×
4464
                 "loadIdentification()", msyystring_buffer, msyylineno);
4465
      return (MS_FAILURE);
×
4466
    }
4467
  }
4468
}
4469

4470
int loadLayer(layerObj *layer, mapObj *map) {
13,262✔
4471
  int type;
4472

4473
  layer->map = (mapObj *)map;
13,262✔
4474

4475
  for (;;) {
4476
    switch (msyylex()) {
111,923✔
4477
    case (BINDVALS):
3✔
4478
      if (loadHashTable(&(layer->bindvals)) != MS_SUCCESS)
3✔
4479
        return (-1);
4480
      break;
4481
    case (CLASS):
13,068✔
4482
      if (msGrowLayerClasses(layer) == NULL)
13,068✔
4483
        return (-1);
4484
      initClass(layer->class[layer->numclasses]);
13,068✔
4485
      if (loadClass(layer->class[layer->numclasses], layer) == -1) {
13,068✔
4486
        freeClass(layer->class[layer->numclasses]);
×
4487
        free(layer->class[layer->numclasses]);
×
4488
        layer->class[layer->numclasses] = NULL;
×
4489
        return (-1);
×
4490
      }
4491
      layer->numclasses++;
13,068✔
4492
      break;
13,068✔
4493
    case (CLUSTER):
4✔
4494
      if (loadCluster(&layer->cluster) == -1)
4✔
4495
        return (-1);
4496
      break;
4497
    case (CLASSGROUP):
781✔
4498
      if (getString(&layer->classgroup) == MS_FAILURE)
781✔
4499
        return (-1); /* getString() cleans up previously allocated string */
4500
      break;
4501
    case (CLASSITEM):
3,146✔
4502
      if (getString(&layer->classitem) == MS_FAILURE)
3,146✔
4503
        return (-1); /* getString() cleans up previously allocated string */
4504
      break;
4505
    case (COMPOSITE): {
81✔
4506
      LayerCompositer *compositer = msSmallMalloc(sizeof(LayerCompositer));
81✔
4507
      initLayerCompositer(compositer);
81✔
4508
      if (MS_FAILURE == loadLayerCompositer(compositer)) {
81✔
4509
        freeLayerCompositer(compositer);
×
4510
        return -1;
×
4511
      }
4512
      if (!layer->compositer) {
81✔
4513
        layer->compositer = compositer;
81✔
4514
      } else {
4515
        LayerCompositer *lctmp = layer->compositer;
4516
        while (lctmp->next)
×
4517
          lctmp = lctmp->next;
4518
        lctmp->next = compositer;
×
4519
      }
4520
      break;
4521
    }
4522
    case (CONNECTION):
2,550✔
4523
      if (getString(&layer->connection) == MS_FAILURE)
2,550✔
4524
        return (-1); /* getString() cleans up previously allocated string */
4525
      break;
4526
    case (CONNECTIONTYPE):
2,612✔
4527
      if ((type = getSymbol(14, MS_OGR, MS_POSTGIS, MS_WMS, MS_ORACLESPATIAL,
2,612✔
4528
                            MS_WFS, MS_GRATICULE, MS_PLUGIN, MS_UNION,
4529
                            MS_UVRASTER, MS_CONTOUR, MS_KERNELDENSITY, MS_IDW,
4530
                            MS_FLATGEOBUF, MS_RASTER_LABEL)) == -1)
4531
        return (-1);
4532
      layer->connectiontype = type;
2,612✔
4533
      break;
2,612✔
4534
    case (DATA):
8,957✔
4535
      if (getString(&layer->data) == MS_FAILURE)
8,957✔
4536
        return (-1); /* getString() cleans up previously allocated string */
4537
      break;
4538
    case (DEBUG):
901✔
4539
      if ((layer->debug = getSymbol(3, MS_ON, MS_OFF, MS_NUMBER)) == -1)
901✔
4540
        return (-1);
4541
      if (layer->debug == MS_NUMBER) {
901✔
4542
        if (msCheckNumber(msyynumber, MS_NUM_CHECK_RANGE, 0, 5) == MS_FAILURE) {
61✔
4543
          msSetError(MS_MISCERR,
×
4544
                     "Invalid DEBUG level, must be between 0 and 5 (line %d)",
4545
                     "loadLayer()", msyylineno);
4546
          return (-1);
×
4547
        }
4548
        layer->debug = (int)msyynumber;
61✔
4549
      }
4550
      break;
4551
    case (EOF):
×
4552
      msSetError(MS_EOFERR, NULL, "loadLayer()");
×
4553
      return (-1);
×
4554
      break;
4555
    case (ENCODING):
6✔
4556
      if (getString(&layer->encoding) == MS_FAILURE)
6✔
4557
        return (-1);
4558
      break;
4559
    case (END):
13,262✔
4560
      if ((int)layer->type == -1) {
13,262✔
4561
        msSetError(MS_MISCERR, "Layer type not set.", "loadLayer()");
×
4562
        return (-1);
×
4563
      }
4564

4565
      return (0);
4566
      break;
4567
    case (EXTENT): {
581✔
4568
      if (getDouble(&(layer->extent.minx), MS_NUM_CHECK_NONE, -1, -1) == -1)
581✔
4569
        return (-1);
4570
      if (getDouble(&(layer->extent.miny), MS_NUM_CHECK_NONE, -1, -1) == -1)
581✔
4571
        return (-1);
4572
      if (getDouble(&(layer->extent.maxx), MS_NUM_CHECK_NONE, -1, -1) == -1)
581✔
4573
        return (-1);
4574
      if (getDouble(&(layer->extent.maxy), MS_NUM_CHECK_NONE, -1, -1) == -1)
581✔
4575
        return (-1);
4576
      if (!MS_VALID_EXTENT(layer->extent)) {
581✔
4577
        msSetError(MS_MISCERR,
×
4578
                   "Given layer extent is invalid. Check that it is in the "
4579
                   "form: minx, miny, maxx, maxy",
4580
                   "loadLayer()");
4581
        return (-1);
×
4582
      }
4583
      break;
4584
    }
4585
    case (FEATURE):
2,922✔
4586
      if ((int)layer->type == -1) {
2,922✔
4587
        msSetError(MS_MISCERR,
×
4588
                   "Layer type must be set before defining inline features.",
4589
                   "loadLayer()");
4590
        return (-1);
×
4591
      }
4592

4593
      if (layer->type == MS_LAYER_POLYGON)
2,922✔
4594
        type = MS_SHAPE_POLYGON;
4595
      else if (layer->type == MS_LAYER_LINE)
2,810✔
4596
        type = MS_SHAPE_LINE;
4597
      else
4598
        type = MS_SHAPE_POINT;
4599

4600
      layer->connectiontype = MS_INLINE;
2,922✔
4601

4602
      if (loadFeature(layer, type) == MS_FAILURE)
2,922✔
4603
        return (-1);
4604
      break;
4605
    case (FILTER):
546✔
4606
      if (loadExpression(&(layer->filter)) == -1)
546✔
4607
        return (-1); /* loadExpression() cleans up previously allocated
4608
                        expression */
4609
      break;
4610
    case (FILTERITEM):
221✔
4611
      if (getString(&layer->filteritem) == MS_FAILURE)
221✔
4612
        return (-1); /* getString() cleans up previously allocated string */
4613
      break;
4614
    case (FOOTER):
572✔
4615
      if (getString(&layer->footer) == MS_FAILURE)
572✔
4616
        return (-1); /* getString() cleans up previously allocated string */
4617
      break;
4618
    case (GRID):
8✔
4619
      layer->connectiontype = MS_GRATICULE;
8✔
4620
      if (layer->grid) {
8✔
4621
        freeGrid(layer->grid);
×
4622
        msFree(layer->grid);
×
4623
      }
4624
      layer->grid = (void *)malloc(sizeof(graticuleObj));
8✔
4625
      MS_CHECK_ALLOC(layer->grid, sizeof(graticuleObj), -1);
8✔
4626

4627
      initGrid(layer->grid);
8✔
4628
      loadGrid(layer);
8✔
4629
      break;
8✔
4630
    case (GROUP):
179✔
4631
      if (getString(&layer->group) == MS_FAILURE)
179✔
4632
        return (-1); /* getString() cleans up previously allocated string */
4633
      break;
4634
    case (GEOMTRANSFORM): {
43✔
4635
      if (getSymbol(1, MS_EXPRESSION) == -1)
43✔
4636
        return (MS_FAILURE);
4637
      /* handle expression case here for the moment */
4638
      msFree(layer->_geomtransform.string);
43✔
4639
      layer->_geomtransform.string = msStrdup(msyystring_buffer);
43✔
4640
      layer->_geomtransform.type = MS_GEOMTRANSFORM_EXPRESSION;
43✔
4641
    } break;
43✔
4642
    case (HEADER):
604✔
4643
      if (getString(&layer->header) == MS_FAILURE)
604✔
4644
        return (-1); /* getString() cleans up previously allocated string */
4645
      break;
4646
    case (JOIN):
×
4647
      if (layer->numjoins == MS_MAXJOINS) { /* no room */
×
4648
        msSetError(MS_IDENTERR, "Maximum number of joins reached.",
×
4649
                   "loadLayer()");
4650
        return (-1);
×
4651
      }
4652

4653
      if (loadJoin(&(layer->joins[layer->numjoins])) == -1) {
×
4654
        freeJoin(&(layer->joins[layer->numjoins]));
×
4655
        return (-1);
×
4656
      }
4657
      layer->numjoins++;
×
4658
      break;
×
4659
    case (LABELCACHE):
30✔
4660
      if ((layer->labelcache = getSymbol(2, MS_ON, MS_OFF)) == -1)
30✔
4661
        return (-1);
4662
      break;
4663
    case (LABELITEM):
925✔
4664
      if (getString(&layer->labelitem) == MS_FAILURE)
925✔
4665
        return (-1); /* getString() cleans up previously allocated string */
4666
      break;
4667
    case (LABELMAXSCALE):
×
4668
    case (LABELMAXSCALEDENOM):
4669
      if (getDouble(&(layer->labelmaxscaledenom), MS_NUM_CHECK_GTE, 0, -1) ==
×
4670
          -1)
4671
        return (-1);
4672
      break;
4673
    case (LABELMINSCALE):
×
4674
    case (LABELMINSCALEDENOM):
4675
      if (getDouble(&(layer->labelminscaledenom), MS_NUM_CHECK_GTE, 0, -1) ==
×
4676
          -1)
4677
        return (-1);
4678
      break;
4679
    case (LABELREQUIRES):
5✔
4680
      if (getString(&layer->labelrequires) == MS_FAILURE)
5✔
4681
        return (-1); /* getString() cleans up previously allocated string */
4682
      break;
4683
    case (LAYER):
4684
      break; /* for string loads */
4685
    case (MASK):
51✔
4686
      if (getString(&layer->mask) == MS_FAILURE)
51✔
4687
        return (-1); /* getString() cleans up previously allocated string */
4688
      break;
4689
    case (MAXFEATURES):
2✔
4690
      if (getInteger(&(layer->maxfeatures), MS_NUM_CHECK_GT, 0, -1) == -1)
2✔
4691
        return (-1);
4692
      break;
4693
    case (MAXSCALE):
3✔
4694
    case (MAXSCALEDENOM):
4695
      if (getDouble(&(layer->maxscaledenom), MS_NUM_CHECK_GTE, 0, -1) == -1)
3✔
4696
        return (-1);
4697
      break;
4698
    case (MAXGEOWIDTH):
×
4699
      if (getDouble(&(layer->maxgeowidth), MS_NUM_CHECK_GT, 0, -1) == -1)
×
4700
        return (-1);
4701
      break;
4702
    case (METADATA):
7,407✔
4703
      if (loadHashTable(&(layer->metadata)) != MS_SUCCESS)
7,407✔
4704
        return (-1);
4705
      break;
4706
    case (MINSCALE):
68✔
4707
    case (MINSCALEDENOM):
4708
      if (getDouble(&(layer->minscaledenom), MS_NUM_CHECK_GTE, 0, -1) == -1)
68✔
4709
        return (-1);
4710
      break;
4711
    case (MINGEOWIDTH):
×
4712
      if (getDouble(&(layer->mingeowidth), MS_NUM_CHECK_GT, 0, -1) == -1)
×
4713
        return (-1);
4714
      break;
4715
    case (MINFEATURESIZE):
×
4716
      if (getInteger(&(layer->minfeaturesize), MS_NUM_CHECK_GT, 0, -1) == -1)
×
4717
        return (-1);
4718
      break;
4719
    case (NAME):
13,169✔
4720
      if (getString(&layer->name) == MS_FAILURE)
13,169✔
4721
        return (-1);
4722
      break;
4723
    case (OFFSITE):
34✔
4724
      if (loadColor(&(layer->offsite), NULL) != MS_SUCCESS)
34✔
4725
        return (-1);
4726
      break;
4727

4728
    case (CONNECTIONOPTIONS):
2✔
4729
      if (loadHashTable(&(layer->connectionoptions)) != MS_SUCCESS)
2✔
4730
        return (-1);
4731
      break;
4732
    case (MS_PLUGIN): {
×
4733
      int rv;
4734
      if (map->config) { // value *must* represent a config key
×
4735
        char *value = NULL;
×
4736
        const char *plugin_library = NULL;
4737

4738
        if (getString(&value) == MS_FAILURE)
×
4739
          return (-1);
×
4740
        plugin_library = msConfigGetPlugin(map->config, value);
×
4741
        msFree(value);
×
4742
        if (!plugin_library) {
×
4743
          msSetError(MS_MISCERR,
×
4744
                     "Plugin value not found in config file. See "
4745
                     "mapserver.org/mapfile/config.html for more information.",
4746
                     "loadLayer()");
4747
          return (-1);
×
4748
        }
4749
        msFree(layer->plugin_library_original);
×
4750
        layer->plugin_library_original = strdup(plugin_library);
×
4751
      } else {
4752
        if (getString(&layer->plugin_library_original) == MS_FAILURE)
×
4753
          return (-1);
4754
      }
4755
      rv = msBuildPluginLibraryPath(&layer->plugin_library,
×
4756
                                    layer->plugin_library_original, map);
×
4757
      if (rv == MS_FAILURE)
×
4758
        return (-1);
4759
    } break;
4760
    case (PROCESSING): {
1,429✔
4761
      /* NOTE: processing array maintained as size+1 with NULL terminator.
4762
               This ensure that CSL (GDAL string list) functions can be
4763
               used on the list for easy processing. */
4764
      char *value = NULL;
1,429✔
4765
      if (getString(&value) == MS_FAILURE)
1,429✔
4766
        return (-1);
×
4767
      msLayerAddProcessing(layer, value);
1,429✔
4768
      free(value);
1,429✔
4769
      value = NULL;
4770
    } break;
1,429✔
4771
    case (POSTLABELCACHE):
×
4772
      if ((layer->postlabelcache = getSymbol(2, MS_TRUE, MS_FALSE)) == -1)
×
4773
        return (-1);
4774
      if (layer->postlabelcache)
×
4775
        layer->labelcache = MS_OFF;
×
4776
      break;
4777
    case (PROJECTION):
9,247✔
4778
      if (loadProjection(&(layer->projection), map) == -1)
9,247✔
4779
        return (-1);
4780
      layer->project = MS_TRUE;
9,247✔
4781
      break;
9,247✔
4782
    case (REQUIRES):
10✔
4783
      if (getString(&layer->requires) == MS_FAILURE)
10✔
4784
        return (-1); /* getString() cleans up previously allocated string */
4785
      break;
4786
    case (SCALETOKEN):
74✔
4787
      if (msGrowLayerScaletokens(layer) == NULL)
74✔
4788
        return (-1);
4789
      initScaleToken(&layer->scaletokens[layer->numscaletokens]);
74✔
4790
      if (loadScaletoken(&layer->scaletokens[layer->numscaletokens], layer) ==
74✔
4791
          -1) {
4792
        freeScaleToken(&layer->scaletokens[layer->numscaletokens]);
×
4793
        return (-1);
×
4794
      }
4795
      layer->numscaletokens++;
74✔
4796
      break;
74✔
4797
    case (SIZEUNITS):
256✔
4798
      if ((layer->sizeunits = getSymbol(
256✔
4799
               8, MS_INCHES, MS_FEET, MS_MILES, MS_METERS, MS_KILOMETERS,
4800
               MS_NAUTICALMILES, MS_DD, MS_PIXELS)) == -1)
4801
        return (-1);
4802
      break;
4803
    case (STATUS):
11,180✔
4804
      if ((layer->status = getSymbol(3, MS_ON, MS_OFF, MS_DEFAULT)) == -1)
11,180✔
4805
        return (-1);
4806
      break;
4807
    case (STYLEITEM):
132✔
4808
      if (getString(&layer->styleitem) == MS_FAILURE)
132✔
4809
        return (-1); /* getString() cleans up previously allocated string */
4810
      break;
4811
    case (SYMBOLSCALE):
1✔
4812
    case (SYMBOLSCALEDENOM):
4813
      if (getDouble(&(layer->symbolscaledenom), MS_NUM_CHECK_GTE, 1, -1) == -1)
1✔
4814
        return (-1);
4815
      break;
4816
    case (TEMPLATE):
2,388✔
4817
      if (getString(&layer->template) == MS_FAILURE)
2,388✔
4818
        return (-1); /* getString() cleans up previously allocated string */
4819
      break;
4820
    case (TILEINDEX):
303✔
4821
      if (getString(&layer->tileindex) == MS_FAILURE)
303✔
4822
        return (-1); /* getString() cleans up previously allocated string */
4823
      break;
4824
    case (TILEITEM):
301✔
4825
      if (getString(&layer->tileitem) == MS_FAILURE)
301✔
4826
        return (-1); /* getString() cleans up previously allocated string */
4827
      break;
4828
    case (TILESRS):
13✔
4829
      if (getString(&layer->tilesrs) == MS_FAILURE)
13✔
4830
        return (-1); /* getString() cleans up previously allocated string */
4831
      break;
4832
    case (IDENTIFY):
80✔
4833
      if (loadIdentification(layer) == MS_FAILURE)
80✔
4834
        return (-1);
4835
      break;
4836
    case (TOLERANCE):
65✔
4837
      if (getDouble(&(layer->tolerance), MS_NUM_CHECK_GTE, 0, -1) == -1)
65✔
4838
        return (-1);
4839
      break;
4840
    case (TOLERANCEUNITS):
37✔
4841
      if ((layer->toleranceunits = getSymbol(
37✔
4842
               8, MS_INCHES, MS_FEET, MS_MILES, MS_METERS, MS_KILOMETERS,
4843
               MS_NAUTICALMILES, MS_DD, MS_PIXELS)) == -1)
4844
        return (-1);
4845
      break;
4846
    case (TRANSFORM):
74✔
4847
      if ((layer->transform =
74✔
4848
               getSymbol(11, MS_TRUE, MS_FALSE, MS_UL, MS_UC, MS_UR, MS_CL,
74✔
4849
                         MS_CC, MS_CR, MS_LL, MS_LC, MS_LR)) == -1)
4850
        return (-1);
4851
      break;
4852
    case (TYPE):
13,266✔
4853
      if ((type =
13,266✔
4854
               getSymbol(9, MS_LAYER_POINT, MS_LAYER_LINE, MS_LAYER_RASTER,
13,266✔
4855
                         MS_LAYER_POLYGON, MS_LAYER_ANNOTATION, MS_LAYER_QUERY,
4856
                         MS_LAYER_CIRCLE, MS_LAYER_CHART, TILEINDEX)) == -1)
4857
        return (-1);
4858
      if (type == TILEINDEX)
13,266✔
4859
        type = MS_LAYER_TILEINDEX; /* TILEINDEX is also a parameter */
4860
      if (type == MS_LAYER_ANNOTATION) {
13,262✔
4861
        msSetError(MS_IDENTERR,
×
4862
                   "Annotation Layers have been removed. To obtain same "
4863
                   "functionality, use a layer with label->styles and no "
4864
                   "class->styles",
4865
                   "loadLayer()");
4866
        return -1;
×
4867
      }
4868
      layer->type = type;
13,266✔
4869
      break;
13,266✔
4870
    case (UNITS):
3✔
4871
      if ((layer->units = getSymbol(9, MS_INCHES, MS_FEET, MS_MILES, MS_METERS,
3✔
4872
                                    MS_KILOMETERS, MS_NAUTICALMILES, MS_DD,
4873
                                    MS_PIXELS, MS_PERCENTAGES)) == -1)
4874
        return (-1);
4875
      break;
4876
    case (UTFDATA):
1✔
4877
      if (loadExpression(&(layer->utfdata)) == -1)
1✔
4878
        return (-1); /* loadExpression() cleans up previously allocated
4879
                        expression */
4880
      break;
4881
    case (UTFITEM):
1✔
4882
      if (getString(&layer->utfitem) == MS_FAILURE)
1✔
4883
        return (-1);
4884
      break;
4885
    case (VALIDATION):
292✔
4886
      if (loadHashTable(&(layer->validation)) != MS_SUCCESS)
292✔
4887
        return (-1);
4888
      break;
4889
    default:
×
4890
      if (strlen(msyystring_buffer) > 0) {
×
4891
        msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
4892
                   "loadLayer()", msyystring_buffer, msyylineno);
4893
        return (-1);
×
4894
      } else {
4895
        return (0); /* end of a string, not an error */
4896
      }
4897
    }
4898
  } /* next token */
4899
}
4900

4901
int msUpdateLayerFromString(layerObj *layer, char *string) {
27✔
4902
  int i;
4903

4904
  if (!layer || !string)
27✔
4905
    return MS_FAILURE;
4906

4907
  msAcquireLock(TLOCK_PARSER);
27✔
4908

4909
  msyystate = MS_TOKENIZE_STRING;
27✔
4910
  msyystring = string;
27✔
4911
  msyylex(); /* sets things up, but doesn't process any tokens */
27✔
4912

4913
  msyylineno = 1; /* start at line 1 */
27✔
4914

4915
  if (loadLayer(layer, layer->map) == -1) {
27✔
4916
    msReleaseLock(TLOCK_PARSER);
×
4917
    return MS_FAILURE; /* parse error */
×
4918
    ;
4919
  }
4920

4921
  msyylex_destroy();
27✔
4922
  msReleaseLock(TLOCK_PARSER);
27✔
4923

4924
  /* step through classes to resolve symbol names */
4925
  for (i = 0; i < layer->numclasses; i++) {
27✔
4926
    if (classResolveSymbolNames(layer->class[i]) != MS_SUCCESS)
×
4927
      return MS_FAILURE;
4928
  }
4929

4930
  return MS_SUCCESS;
4931
}
4932

4933
static void writeCompositingFilter(FILE *stream, int indent,
4934
                                   CompositingFilter *filter) {
4935
  while (filter) {
×
4936
    writeString(stream, indent, "COMPFILTER", "", filter->filter);
×
4937
    filter = filter->next;
×
4938
  }
4939
}
4940

4941
static void writeLayerCompositer(FILE *stream, int indent,
7✔
4942
                                 LayerCompositer *compositor) {
4943
  indent++;
7✔
4944
  while (compositor) {
7✔
4945
    writeBlockBegin(stream, indent, "COMPOSITE");
×
4946
    writeCompositingFilter(stream, indent, compositor->filter);
×
4947
    if (compositor->comp_op != MS_COMPOP_SRC_OVER) {
×
4948

4949
      bool bFound = false;
4950
      for (int i = 0; i < SIZEOF_COMP_OPS; ++i) {
×
4951
        if (compositor->comp_op == CompOps[i].eOp) {
×
4952
          bFound = true;
4953
          writeString(stream, indent, "COMPOP", NULL, CompOps[i].pszName);
×
4954
          break;
×
4955
        }
4956
      }
4957
      if (!bFound) {
4958
        assert(0);
4959
      }
4960
    }
4961
    writeNumber(stream, indent, "OPACITY", 100, compositor->opacity);
×
4962
    writeBlockEnd(stream, indent, "COMPOSITE");
×
4963
    compositor = compositor->next;
×
4964
  }
4965
}
7✔
4966
static void writeLayer(FILE *stream, int indent, layerObj *layer) {
7✔
4967
  int i;
4968
  featureListNodeObjPtr current = NULL;
4969

4970
  if (layer->status == MS_DELETE)
7✔
4971
    return;
4972

4973
  indent++;
7✔
4974
  writeBlockBegin(stream, indent, "LAYER");
7✔
4975
  writeHashTable(stream, indent, "BINDVALS", &(layer->bindvals));
7✔
4976
  /* class - see below */
4977
  writeString(stream, indent, "CLASSGROUP", NULL, layer->classgroup);
7✔
4978
  writeString(stream, indent, "CLASSITEM", NULL, layer->classitem);
7✔
4979
  writeCluster(stream, indent, &(layer->cluster));
7✔
4980
  writeLayerCompositer(stream, indent, layer->compositer);
7✔
4981
  writeString(stream, indent, "CONNECTION", NULL, layer->connection);
7✔
4982
  writeKeyword(stream, indent, "CONNECTIONTYPE", layer->connectiontype, 12,
7✔
4983
               MS_OGR, "OGR", MS_POSTGIS, "POSTGIS", MS_WMS, "WMS",
4984
               MS_ORACLESPATIAL, "ORACLESPATIAL", MS_WFS, "WFS", MS_PLUGIN,
4985
               "PLUGIN", MS_UNION, "UNION", MS_UVRASTER, "UVRASTER", MS_CONTOUR,
4986
               "CONTOUR", MS_KERNELDENSITY, "KERNELDENSITY", MS_IDW, "IDW",
4987
               MS_FLATGEOBUF, "FLATGEOBUF");
4988
  writeHashTable(stream, indent, "CONNECTIONOPTIONS",
7✔
4989
                 &(layer->connectionoptions));
4990
  writeString(stream, indent, "DATA", NULL, layer->data);
7✔
4991
  writeNumber(stream, indent, "DEBUG", 0,
7✔
4992
              layer->debug); /* is this right? see loadLayer() */
7✔
4993
  writeString(stream, indent, "ENCODING", NULL, layer->encoding);
7✔
4994
  writeExtent(stream, indent, "EXTENT", layer->extent);
7✔
4995
  writeExpression(stream, indent, "FILTER", &(layer->filter));
7✔
4996
  writeString(stream, indent, "FILTERITEM", NULL, layer->filteritem);
7✔
4997
  writeString(stream, indent, "FOOTER", NULL, layer->footer);
7✔
4998
  writeString(stream, indent, "GROUP", NULL, layer->group);
7✔
4999

5000
  if (layer->_geomtransform.type == MS_GEOMTRANSFORM_EXPRESSION) {
7✔
5001
    writeIndent(stream, indent + 1);
×
5002
    fprintf(stream, "GEOMTRANSFORM (%s)\n", layer->_geomtransform.string);
×
5003
  }
5004

5005
  writeString(stream, indent, "HEADER", NULL, layer->header);
7✔
5006
  /* join - see below */
5007
  writeKeyword(stream, indent, "LABELCACHE", layer->labelcache, 1, MS_OFF,
7✔
5008
               "OFF");
5009
  writeString(stream, indent, "LABELITEM", NULL, layer->labelitem);
7✔
5010
  writeNumber(stream, indent, "LABELMAXSCALEDENOM", -1,
7✔
5011
              layer->labelmaxscaledenom);
5012
  writeNumber(stream, indent, "LABELMINSCALEDENOM", -1,
7✔
5013
              layer->labelminscaledenom);
5014
  writeString(stream, indent, "LABELREQUIRES", NULL, layer->labelrequires);
7✔
5015
  writeNumber(stream, indent, "MAXFEATURES", -1, layer->maxfeatures);
7✔
5016
  writeNumber(stream, indent, "MAXGEOWIDTH", -1, layer->maxgeowidth);
7✔
5017
  writeNumber(stream, indent, "MAXSCALEDENOM", -1, layer->maxscaledenom);
7✔
5018
  writeString(stream, indent, "MASK", NULL, layer->mask);
7✔
5019
  writeHashTable(stream, indent, "METADATA", &(layer->metadata));
7✔
5020
  writeNumber(stream, indent, "MINGEOWIDTH", -1, layer->mingeowidth);
7✔
5021
  writeNumber(stream, indent, "MINSCALEDENOM", -1, layer->minscaledenom);
7✔
5022
  writeNumber(stream, indent, "MINFEATURESIZE", -1, layer->minfeaturesize);
7✔
5023
  writeString(stream, indent, "NAME", NULL, layer->name);
7✔
5024
  writeColor(stream, indent, "OFFSITE", NULL, &(layer->offsite));
7✔
5025
  writeString(stream, indent, "PLUGIN", NULL, layer->plugin_library_original);
7✔
5026
  writeKeyword(stream, indent, "POSTLABELCACHE", layer->postlabelcache, 1,
7✔
5027
               MS_TRUE, "TRUE");
5028
  for (i = 0; layer->processing && layer->processing[i]; i++)
8✔
5029
    writeString(stream, indent, "PROCESSING", NULL, layer->processing[i]);
1✔
5030
  writeKeyword(stream, indent, "PROCESSING", layer->rendermode, 1,
7✔
5031
               MS_ALL_MATCHING_CLASSES, "\"RENDERMODE=ALL_MATCHING_CLASSES\"");
5032
  writeProjection(stream, indent, &(layer->projection));
7✔
5033
  writeString(stream, indent, "REQUIRES", NULL, layer->requires);
7✔
5034
  writeKeyword(stream, indent, "SIZEUNITS", layer->sizeunits, 7, MS_INCHES,
7✔
5035
               "INCHES", MS_FEET, "FEET", MS_MILES, "MILES", MS_METERS,
5036
               "METERS", MS_KILOMETERS, "KILOMETERS", MS_NAUTICALMILES,
5037
               "NAUTICALMILES", MS_DD, "DD");
5038
  writeKeyword(stream, indent, "STATUS", layer->status, 3, MS_ON, "ON", MS_OFF,
7✔
5039
               "OFF", MS_DEFAULT, "DEFAULT");
5040
  writeString(stream, indent, "STYLEITEM", NULL, layer->styleitem);
7✔
5041
  writeNumber(stream, indent, "SYMBOLSCALEDENOM", -1, layer->symbolscaledenom);
7✔
5042
  writeString(stream, indent, "TEMPLATE", NULL, layer->template);
7✔
5043
  writeString(stream, indent, "TILEINDEX", NULL, layer->tileindex);
7✔
5044
  writeString(stream, indent, "TILEITEM", NULL, layer->tileitem);
7✔
5045
  writeString(stream, indent, "TILESRS", NULL, layer->tilesrs);
7✔
5046
  writeNumber(stream, indent, "TOLERANCE", -1, layer->tolerance);
7✔
5047
  writeKeyword(stream, indent, "TOLERANCEUNITS", layer->toleranceunits, 7,
7✔
5048
               MS_INCHES, "INCHES", MS_FEET, "FEET", MS_MILES, "MILES",
5049
               MS_METERS, "METERS", MS_KILOMETERS, "KILOMETERS",
5050
               MS_NAUTICALMILES, "NAUTICALMILES", MS_DD, "DD");
5051
  writeIdentify(stream, indent, layer);
7✔
5052
  writeKeyword(stream, indent, "TRANSFORM", layer->transform, 10, MS_FALSE,
7✔
5053
               "FALSE", MS_UL, "UL", MS_UC, "UC", MS_UR, "UR", MS_CL, "CL",
5054
               MS_CC, "CC", MS_CR, "CR", MS_LL, "LL", MS_LC, "LC", MS_LR, "LR");
5055
  writeKeyword(stream, indent, "TYPE", layer->type, 9, MS_LAYER_POINT, "POINT",
7✔
5056
               MS_LAYER_LINE, "LINE", MS_LAYER_POLYGON, "POLYGON",
5057
               MS_LAYER_RASTER, "RASTER", MS_LAYER_ANNOTATION, "ANNOTATION",
5058
               MS_LAYER_QUERY, "QUERY", MS_LAYER_CIRCLE, "CIRCLE",
5059
               MS_LAYER_TILEINDEX, "TILEINDEX", MS_LAYER_CHART, "CHART");
5060
  writeKeyword(stream, indent, "UNITS", layer->units, 9, MS_INCHES, "INCHES",
7✔
5061
               MS_FEET, "FEET", MS_MILES, "MILES", MS_METERS, "METERS",
5062
               MS_KILOMETERS, "KILOMETERS", MS_NAUTICALMILES, "NAUTICALMILES",
5063
               MS_DD, "DD", MS_PIXELS, "PIXELS", MS_PERCENTAGES,
5064
               "PERCENTATGES");
5065
  writeExpression(stream, indent, "UTFDATA", &(layer->utfdata));
7✔
5066
  writeString(stream, indent, "UTFITEM", NULL, layer->utfitem);
7✔
5067
  writeHashTable(stream, indent, "VALIDATION", &(layer->validation));
7✔
5068

5069
  /* write potentially multiply occurring objects last */
5070
  for (i = 0; i < layer->numscaletokens; i++)
7✔
5071
    writeScaleToken(stream, indent, &(layer->scaletokens[i]));
×
5072
  for (i = 0; i < layer->numjoins; i++)
7✔
5073
    writeJoin(stream, indent, &(layer->joins[i]));
×
5074
  for (i = 0; i < layer->numclasses; i++)
16✔
5075
    writeClass(stream, indent, layer->class[i]);
9✔
5076

5077
  if (layer->grid && layer->connectiontype == MS_GRATICULE)
7✔
5078
    writeGrid(stream, indent, layer->grid);
×
5079
  else {
5080
    current = layer->features;
7✔
5081
    while (current != NULL) {
10✔
5082
      writeFeature(stream, indent, &(current->shape));
3✔
5083
      current = current->next;
3✔
5084
    }
5085
  }
5086

5087
  writeBlockEnd(stream, indent, "LAYER");
7✔
5088
  writeLineFeed(stream);
5089
}
5090

5091
char *msWriteLayerToString(layerObj *layer) {
×
5092
  msIOContext context;
5093
  msIOBuffer buffer;
5094

5095
  context.label = NULL;
×
5096
  context.write_channel = MS_TRUE;
×
5097
  context.readWriteFunc = msIO_bufferWrite;
×
5098
  context.cbData = &buffer;
×
5099
  buffer.data = NULL;
×
5100
  buffer.data_len = 0;
×
5101
  buffer.data_offset = 0;
×
5102

5103
  msIO_installHandlers(NULL, &context, NULL);
×
5104

5105
  writeLayer(stdout, -1, layer);
×
5106
  msIO_bufferWrite(&buffer, "", 1);
×
5107

5108
  msIO_installHandlers(NULL, NULL, NULL);
×
5109

5110
  return (char *)buffer.data;
×
5111
}
5112

5113
/*
5114
** Initialize, load and free a referenceMapObj structure
5115
*/
5116
void initReferenceMap(referenceMapObj *ref) {
3,289✔
5117
  ref->height = ref->width = 0;
3,289✔
5118
  ref->extent.minx = ref->extent.miny = ref->extent.maxx = ref->extent.maxy =
3,289✔
5119
      -1.0;
5120
  ref->image = NULL;
3,289✔
5121
  MS_INIT_COLOR(ref->color, 255, 0, 0, 255);
3,289✔
5122
  MS_INIT_COLOR(ref->outlinecolor, 0, 0, 0, 255);
3,289✔
5123
  ref->status = MS_OFF;
3,289✔
5124
  ref->marker = 0;
3,289✔
5125
  ref->markername = NULL;
3,289✔
5126
  ref->markersize = 0;
3,289✔
5127
  ref->minboxsize = 3;
3,289✔
5128
  ref->maxboxsize = 0;
3,289✔
5129
  ref->map = NULL;
3,289✔
5130
}
3,289✔
5131

5132
void freeReferenceMap(referenceMapObj *ref) {
3,241✔
5133
  msFree(ref->image);
3,241✔
5134
  msFree(ref->markername);
3,241✔
5135
}
3,241✔
5136

5137
int loadReferenceMap(referenceMapObj *ref, mapObj *map) {
×
5138
  int state;
5139

5140
  ref->map = (mapObj *)map;
×
5141

5142
  for (;;) {
5143
    switch (msyylex()) {
×
5144
    case (EOF):
×
5145
      msSetError(MS_EOFERR, NULL, "loadReferenceMap()");
×
5146
      return (-1);
×
5147
    case (END):
×
5148
      if (!ref->image) {
×
5149
        msSetError(MS_MISCERR, "No image defined for the reference map.",
×
5150
                   "loadReferenceMap()");
5151
        return (-1);
×
5152
      }
5153
      if (ref->width == 0 || ref->height == 0) {
×
5154
        msSetError(MS_MISCERR, "No image size defined for the reference map.",
×
5155
                   "loadReferenceMap()");
5156
        return (-1);
×
5157
      }
5158
      return (0);
5159
      break;
5160
    case (COLOR):
×
5161
      if (loadColor(&(ref->color), NULL) != MS_SUCCESS)
×
5162
        return (-1);
5163
      break;
5164
    case (EXTENT):
×
5165
      if (getDouble(&(ref->extent.minx), MS_NUM_CHECK_NONE, -1, -1) == -1)
×
5166
        return (-1);
5167
      if (getDouble(&(ref->extent.miny), MS_NUM_CHECK_NONE, -1, -1) == -1)
×
5168
        return (-1);
5169
      if (getDouble(&(ref->extent.maxx), MS_NUM_CHECK_NONE, -1, -1) == -1)
×
5170
        return (-1);
5171
      if (getDouble(&(ref->extent.maxy), MS_NUM_CHECK_NONE, -1, -1) == -1)
×
5172
        return (-1);
5173
      if (!MS_VALID_EXTENT(ref->extent)) {
×
5174
        msSetError(MS_MISCERR,
×
5175
                   "Given reference extent is invalid. Check that it "
5176
                   "is in the form: minx, miny, maxx, maxy",
5177
                   "loadReferenceMap()");
5178
        return (-1);
×
5179
      }
5180
      break;
5181
    case (IMAGE):
×
5182
      if (getString(&ref->image) == MS_FAILURE)
×
5183
        return (-1);
5184
      break;
5185
    case (OUTLINECOLOR):
×
5186
      if (loadColor(&(ref->outlinecolor), NULL) != MS_SUCCESS)
×
5187
        return (-1);
5188
      break;
5189
    case (SIZE):
×
5190
      if (getInteger(&(ref->width), MS_NUM_CHECK_RANGE, 5, ref->map->maxsize) ==
×
5191
          -1)
5192
        return (-1); // is 5 reasonable?
5193
      if (getInteger(&(ref->height), MS_NUM_CHECK_RANGE, 5,
×
5194
                     ref->map->maxsize) == -1)
×
5195
        return (-1);
5196
      break;
5197
    case (STATUS):
×
5198
      if ((ref->status = getSymbol(2, MS_ON, MS_OFF)) == -1)
×
5199
        return (-1);
5200
      break;
5201
    case (MARKER):
×
5202
      if ((state = getSymbol(2, MS_NUMBER, MS_STRING)) == -1)
×
5203
        return (-1);
5204

5205
      if (state == MS_NUMBER) {
×
5206
        if (msCheckNumber(msyynumber, MS_NUM_CHECK_GTE, 0, -1) == MS_FAILURE) {
×
5207
          msSetError(MS_MISCERR,
×
5208
                     "Invalid MARKER, must be greater than 0 (line %d)",
5209
                     "loadReferenceMap()", msyylineno);
5210
          return (-1);
×
5211
        }
5212
        ref->marker = (int)msyynumber;
×
5213
      } else {
5214
        if (ref->markername != NULL)
×
5215
          msFree(ref->markername);
×
5216
        ref->markername = msStrdup(msyystring_buffer);
×
5217
      }
5218
      break;
5219
    case (MARKERSIZE):
×
5220
      if (getInteger(&(ref->markersize), MS_NUM_CHECK_GT, 0, -1) == -1)
×
5221
        return (-1);
5222
      break;
5223
    case (MINBOXSIZE):
×
5224
      if (getInteger(&(ref->minboxsize), MS_NUM_CHECK_GT, 0, -1) == -1)
×
5225
        return (-1);
5226
      break;
5227
    case (MAXBOXSIZE):
×
5228
      if (getInteger(&(ref->maxboxsize), MS_NUM_CHECK_GT, 0, -1) == -1)
×
5229
        return (-1);
5230
      break;
5231
    case (REFERENCE):
5232
      break; /* for string loads */
5233
    default:
×
5234
      if (strlen(msyystring_buffer) > 0) {
×
5235
        msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
5236
                   "loadReferenceMap()", msyystring_buffer, msyylineno);
5237
        return (-1);
×
5238
      } else {
5239
        return (0); /* end of a string, not an error */
5240
      }
5241
    }
5242
  } /* next token */
5243
}
5244

5245
int msUpdateReferenceMapFromString(referenceMapObj *ref, char *string) {
×
5246
  if (!ref || !string)
×
5247
    return MS_FAILURE;
5248

5249
  msAcquireLock(TLOCK_PARSER);
×
5250

5251
  msyystate = MS_TOKENIZE_STRING;
×
5252
  msyystring = string;
×
5253
  msyylex(); /* sets things up, but doesn't process any tokens */
×
5254

5255
  msyylineno = 1; /* start at line 1 */
×
5256

5257
  if (loadReferenceMap(ref, ref->map) == -1) {
×
5258
    msReleaseLock(TLOCK_PARSER);
×
5259
    return MS_FAILURE; /* parse error */
×
5260
    ;
5261
  }
5262

5263
  msyylex_destroy();
×
5264
  msReleaseLock(TLOCK_PARSER);
×
5265
  return MS_SUCCESS;
×
5266
}
5267

5268
static void writeReferenceMap(FILE *stream, int indent, referenceMapObj *ref) {
1✔
5269
  colorObj c;
5270

5271
  if (!ref->image)
1✔
5272
    return;
1✔
5273

5274
  indent++;
×
5275
  writeBlockBegin(stream, indent, "REFERENCE");
×
5276
  MS_INIT_COLOR(c, 255, 0, 0, 255);
×
5277
  writeColor(stream, indent, "COLOR", &c, &(ref->color));
×
5278
  writeExtent(stream, indent, "EXTENT", ref->extent);
×
5279
  writeString(stream, indent, "IMAGE", NULL, ref->image);
×
5280
  MS_INIT_COLOR(c, 0, 0, 0, 255);
×
5281
  writeColor(stream, indent, "OUTLINECOLOR", &c, &(ref->outlinecolor));
×
5282
  writeDimension(stream, indent, "SIZE", ref->width, ref->height, NULL, NULL);
×
5283
  writeKeyword(stream, indent, "STATUS", ref->status, 2, MS_ON, "ON", MS_OFF,
×
5284
               "OFF");
5285
  writeNumberOrString(stream, indent, "MARKER", 0, ref->marker,
×
5286
                      ref->markername);
5287
  writeNumber(stream, indent, "MARKERSIZE", -1, ref->markersize);
×
5288
  writeNumber(stream, indent, "MAXBOXSIZE", -1, ref->maxboxsize);
×
5289
  writeNumber(stream, indent, "MINBOXSIZE", -1, ref->minboxsize);
×
5290
  writeBlockEnd(stream, indent, "REFERENCE");
×
5291
  writeLineFeed(stream);
5292
}
5293

5294
char *msWriteReferenceMapToString(referenceMapObj *ref) {
×
5295
  msIOContext context;
5296
  msIOBuffer buffer;
5297

5298
  context.label = NULL;
×
5299
  context.write_channel = MS_TRUE;
×
5300
  context.readWriteFunc = msIO_bufferWrite;
×
5301
  context.cbData = &buffer;
×
5302
  buffer.data = NULL;
×
5303
  buffer.data_len = 0;
×
5304
  buffer.data_offset = 0;
×
5305

5306
  msIO_installHandlers(NULL, &context, NULL);
×
5307

5308
  writeReferenceMap(stdout, -1, ref);
×
5309
  msIO_bufferWrite(&buffer, "", 1);
×
5310

5311
  msIO_installHandlers(NULL, NULL, NULL);
×
5312

5313
  return (char *)buffer.data;
×
5314
}
5315

5316
#define MAX_FORMATOPTIONS 100
5317

5318
static int loadOutputFormat(mapObj *map) {
1,385✔
5319
  char *name = NULL;
5320
  char *mimetype = NULL;
1,385✔
5321
  char *driver = NULL;
5322
  char *extension = NULL;
1,385✔
5323
  int imagemode = MS_NOOVERRIDE;
5324
  int transparent = MS_NOOVERRIDE;
5325
  char *formatoptions[MAX_FORMATOPTIONS];
5326
  int numformatoptions = 0;
5327
  char *value = NULL;
1,385✔
5328

5329
  for (;;) {
5330
    switch (msyylex()) {
8,808✔
5331
    case (EOF):
×
5332
      msSetError(MS_EOFERR, NULL, "loadOutputFormat()");
×
5333
      goto load_output_error;
×
5334

5335
    case (END): {
1,385✔
5336
      outputFormatObj *format;
5337

5338
      if (driver == NULL) {
1,385✔
5339
        msSetError(MS_MISCERR,
×
5340
                   "OUTPUTFORMAT clause lacks DRIVER keyword near (%s):(%d)",
5341
                   "loadOutputFormat()", msyystring_buffer, msyylineno);
5342
        goto load_output_error;
×
5343
      }
5344

5345
      format = msCreateDefaultOutputFormat(map, driver, name, NULL);
1,385✔
5346
      if (format == NULL) {
1,385✔
5347
        msSetError(MS_MISCERR,
2✔
5348
                   "OUTPUTFORMAT (%s) clause references driver (%s), but this "
5349
                   "driver isn't configured.",
5350
                   "loadOutputFormat()", name, driver);
5351
        goto load_output_error;
2✔
5352
      }
5353
      msFree(name);
1,383✔
5354
      name = NULL;
5355
      msFree(driver);
1,383✔
5356
      driver = NULL;
5357

5358
      if (transparent != MS_NOOVERRIDE)
1,383✔
5359
        format->transparent = transparent;
122✔
5360
      if (extension != NULL) {
1,383✔
5361
        msFree(format->extension);
716✔
5362
        format->extension = extension;
716✔
5363
        extension = NULL;
716✔
5364
      }
5365
      if (mimetype != NULL) {
1,383✔
5366
        msFree(format->mimetype);
1,147✔
5367
        format->mimetype = mimetype;
1,147✔
5368
        mimetype = NULL;
1,147✔
5369
      }
5370
      if (imagemode != MS_NOOVERRIDE) {
1,383✔
5371
        if (format->renderer != MS_RENDER_WITH_AGG ||
757✔
5372
            imagemode != MS_IMAGEMODE_PC256) {
5373
          /* don't force to PC256 with agg, this can happen when using mapfile
5374
           * defined GD outputformats that are now falling back to agg/png8
5375
           */
5376
          format->imagemode = imagemode;
748✔
5377
        }
5378

5379
        if (transparent == MS_NOOVERRIDE) {
757✔
5380
          if (imagemode == MS_IMAGEMODE_RGB)
679✔
5381
            format->transparent = MS_FALSE;
59✔
5382
          else if (imagemode == MS_IMAGEMODE_RGBA)
620✔
5383
            format->transparent = MS_TRUE;
24✔
5384
        }
5385
        if (format->imagemode == MS_IMAGEMODE_INT16 ||
757✔
5386
            format->imagemode == MS_IMAGEMODE_FLOAT32 ||
757✔
5387
            format->imagemode == MS_IMAGEMODE_BYTE)
5388
          format->renderer = MS_RENDER_WITH_RAWDATA;
587✔
5389
      }
5390
      while (numformatoptions--) {
3,320✔
5391
        char *key = strchr(formatoptions[numformatoptions], '=');
1,937✔
5392
        if (!key || !*(key + 1)) {
1,937✔
5393
          msSetError(
×
5394
              MS_MISCERR,
5395
              "Failed to parse FORMATOPTION, expecting \"KEY=VALUE\" syntax.",
5396
              "loadOutputFormat()");
5397
          goto load_output_error;
×
5398
        }
5399
        *key = 0;
1,937✔
5400
        key++;
1,937✔
5401
        msSetOutputFormatOption(format, formatoptions[numformatoptions], key);
1,937✔
5402
        free(formatoptions[numformatoptions]);
1,937✔
5403
      }
5404

5405
      format->inmapfile = MS_TRUE;
1,383✔
5406

5407
      msOutputFormatValidate(format, MS_FALSE);
1,383✔
5408
      return (0);
1,383✔
5409
    }
5410
    case (NAME):
1,357✔
5411
      msFree(name);
1,357✔
5412
      if ((name = getToken()) == NULL)
1,357✔
5413
        goto load_output_error;
×
5414
      break;
5415
    case (MIMETYPE):
1,149✔
5416
      if (getString(&mimetype) == MS_FAILURE)
1,149✔
5417
        goto load_output_error;
×
5418
      break;
5419
    case (DRIVER): {
1,385✔
5420
      int s;
5421
      if ((s = getSymbol(2, MS_STRING, TEMPLATE)) ==
1,385✔
5422
          -1) /* allow the template to be quoted or not in the mapfile */
5423
        goto load_output_error;
×
5424
      free(driver);
1,385✔
5425
      if (s == MS_STRING)
1,385✔
5426
        driver = msStrdup(msyystring_buffer);
1,385✔
5427
      else
5428
        driver = msStrdup("TEMPLATE");
×
5429
    } break;
5430
    case (EXTENSION):
716✔
5431
      if (getString(&extension) == MS_FAILURE)
716✔
5432
        goto load_output_error;
×
5433
      if (extension[0] == '.') {
716✔
5434
        char *temp = msStrdup(extension + 1);
×
5435
        free(extension);
×
5436
        extension = temp;
×
5437
      }
5438
      break;
5439
    case (FORMATOPTION):
1,937✔
5440
      if (getString(&value) == MS_FAILURE)
1,937✔
5441
        goto load_output_error;
×
5442
      if (numformatoptions < MAX_FORMATOPTIONS)
1,937✔
5443
        formatoptions[numformatoptions++] = value;
1,937✔
5444
      value = NULL;
1,937✔
5445
      break;
1,937✔
5446
    case (IMAGEMODE):
5447
      value = getToken();
757✔
5448
      if (strcasecmp(value, "PC256") == 0)
757✔
5449
        imagemode = MS_IMAGEMODE_PC256;
5450
      else if (strcasecmp(value, "RGB") == 0)
748✔
5451
        imagemode = MS_IMAGEMODE_RGB;
5452
      else if (strcasecmp(value, "RGBA") == 0)
642✔
5453
        imagemode = MS_IMAGEMODE_RGBA;
5454
      else if (strcasecmp(value, "INT16") == 0)
587✔
5455
        imagemode = MS_IMAGEMODE_INT16;
5456
      else if (strcasecmp(value, "FLOAT32") == 0)
328✔
5457
        imagemode = MS_IMAGEMODE_FLOAT32;
5458
      else if (strcasecmp(value, "BYTE") == 0)
307✔
5459
        imagemode = MS_IMAGEMODE_BYTE;
5460
      else if (strcasecmp(value, "FEATURE") == 0)
×
5461
        imagemode = MS_IMAGEMODE_FEATURE;
5462
      else {
5463
        msSetError(MS_IDENTERR,
×
5464
                   "Parsing error near (%s):(line %d), expected PC256, RGB, "
5465
                   "RGBA, FEATURE, BYTE, INT16, or FLOAT32 for IMAGEMODE.",
5466
                   "loadOutputFormat()", msyystring_buffer, msyylineno);
5467
        goto load_output_error;
×
5468
      }
5469
      free(value);
757✔
5470
      value = NULL;
757✔
5471
      break;
757✔
5472
    case (TRANSPARENT):
122✔
5473
      if ((transparent = getSymbol(2, MS_ON, MS_OFF)) == -1)
122✔
5474
        goto load_output_error;
×
5475
      break;
5476
    default:
×
5477
      msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
5478
                 "loadOutputFormat()", msyystring_buffer, msyylineno);
5479
      goto load_output_error;
×
5480
    }
5481
  } /* next token */
5482
load_output_error:
2✔
5483
  msFree(driver);
2✔
5484
  msFree(extension);
2✔
5485
  msFree(mimetype);
2✔
5486
  msFree(name);
2✔
5487
  msFree(value);
2✔
5488
  return -1;
2✔
5489
}
5490

5491
/*
5492
** utility function to write an output format structure to file
5493
*/
5494
static void writeOutputformatobject(FILE *stream, int indent,
1✔
5495
                                    outputFormatObj *outputformat) {
5496
  int i = 0;
5497
  if (!outputformat)
1✔
5498
    return;
5499

5500
  indent++;
1✔
5501
  writeBlockBegin(stream, indent, "OUTPUTFORMAT");
1✔
5502
  writeString(stream, indent, "NAME", NULL, outputformat->name);
1✔
5503
  writeString(stream, indent, "MIMETYPE", NULL, outputformat->mimetype);
1✔
5504
  writeString(stream, indent, "DRIVER", NULL, outputformat->driver);
1✔
5505
  writeString(stream, indent, "EXTENSION", NULL, outputformat->extension);
1✔
5506
  writeKeyword(stream, indent, "IMAGEMODE", outputformat->imagemode, 7,
1✔
5507
               MS_IMAGEMODE_PC256, "PC256", MS_IMAGEMODE_RGB, "RGB",
5508
               MS_IMAGEMODE_RGBA, "RGBA", MS_IMAGEMODE_INT16, "INT16",
5509
               MS_IMAGEMODE_FLOAT32, "FLOAT32", MS_IMAGEMODE_BYTE, "BYTE",
5510
               MS_IMAGEMODE_FEATURE, "FEATURE");
5511
  writeKeyword(stream, indent, "TRANSPARENT", outputformat->transparent, 2,
1✔
5512
               MS_TRUE, "TRUE", MS_FALSE, "FALSE");
5513
  for (i = 0; i < outputformat->numformatoptions; i++)
1✔
5514
    writeString(stream, indent, "FORMATOPTION", NULL,
×
5515
                outputformat->formatoptions[i]);
×
5516
  writeBlockEnd(stream, indent, "OUTPUTFORMAT");
1✔
5517
  writeLineFeed(stream);
5518
}
5519

5520
/*
5521
** Write the output formats to file
5522
*/
5523
static void writeOutputformat(FILE *stream, int indent, mapObj *map) {
1✔
5524
  int i = 0;
5525
  if (!map->outputformat)
1✔
5526
    return;
5527

5528
  writeOutputformatobject(stream, indent, map->outputformat);
1✔
5529
  for (i = 0; i < map->numoutputformats; i++) {
3✔
5530
    if (map->outputformatlist[i]->inmapfile == MS_TRUE &&
2✔
5531
        strcmp(map->outputformatlist[i]->name, map->outputformat->name) != 0)
1✔
5532
      writeOutputformatobject(stream, indent, map->outputformatlist[i]);
×
5533
  }
5534
}
5535

5536
/*
5537
** Initialize, load and free a legendObj structure
5538
*/
5539
void initLegend(legendObj *legend) {
3,250✔
5540
  legend->height = legend->width = 0;
3,250✔
5541
  MS_INIT_COLOR(legend->imagecolor, 255, 255, 255, 255); /* white */
3,250✔
5542
  MS_INIT_COLOR(legend->outlinecolor, -1, -1, -1, 255);
3,250✔
5543
  initLabel(&legend->label);
3,250✔
5544
  legend->label.position = MS_XY; /* override */
3,250✔
5545
  legend->keysizex = 20;
3,250✔
5546
  legend->keysizey = 10;
3,250✔
5547
  legend->keyspacingx = 5;
3,250✔
5548
  legend->keyspacingy = 5;
3,250✔
5549
  legend->status = MS_OFF;
3,250✔
5550
  legend->transparent = MS_NOOVERRIDE;
3,250✔
5551
  legend->position = MS_LL;
3,250✔
5552
  legend->postlabelcache = MS_FALSE; /* draw with labels */
3,250✔
5553
  legend->template = NULL;
3,250✔
5554
  legend->map = NULL;
3,250✔
5555
}
3,250✔
5556

5557
void freeLegend(legendObj *legend) {
3,241✔
5558
  if (legend->template)
3,241✔
5559
    free(legend->template);
1✔
5560
  freeLabel(&(legend->label));
3,241✔
5561
}
3,241✔
5562

5563
int loadLegend(legendObj *legend, mapObj *map) {
480✔
5564
  legend->map = (mapObj *)map;
480✔
5565

5566
  for (;;) {
5567
    switch (msyylex()) {
1,037✔
5568
    case (EOF):
×
5569
      msSetError(MS_EOFERR, NULL, "loadLegend()");
×
5570
      return (-1);
×
5571
    case (END):
480✔
5572
      legend->label.position = MS_XY; /* overrides go here */
480✔
5573
      return (0);
480✔
5574
      break;
5575
    case (IMAGECOLOR):
423✔
5576
      if (loadColor(&(legend->imagecolor), NULL) != MS_SUCCESS)
423✔
5577
        return (-1);
5578
      break;
5579
    case (KEYSIZE):
4✔
5580
      if (getInteger(&(legend->keysizex), MS_NUM_CHECK_RANGE,
4✔
5581
                     MS_LEGEND_KEYSIZE_MIN, MS_LEGEND_KEYSIZE_MAX) == -1)
5582
        return (-1);
5583
      if (getInteger(&(legend->keysizey), MS_NUM_CHECK_RANGE,
4✔
5584
                     MS_LEGEND_KEYSIZE_MIN, MS_LEGEND_KEYSIZE_MAX) == -1)
5585
        return (-1);
5586
      break;
5587
    case (KEYSPACING):
4✔
5588
      if (getInteger(&(legend->keyspacingx), MS_NUM_CHECK_RANGE,
4✔
5589
                     MS_LEGEND_KEYSPACING_MIN, MS_LEGEND_KEYSPACING_MAX) == -1)
5590
        return (-1);
5591
      if (getInteger(&(legend->keyspacingy), MS_NUM_CHECK_RANGE,
4✔
5592
                     MS_LEGEND_KEYSPACING_MIN, MS_LEGEND_KEYSPACING_MAX) == -1)
5593
        return (-1);
5594
      break;
5595
    case (LABEL):
78✔
5596
      if (loadLabel(&(legend->label)) == -1)
78✔
5597
        return (-1);
5598
      legend->label.angle = 0; /* force */
78✔
5599
      break;
78✔
5600
    case (LEGEND):
5601
      break; /* for string loads */
5602
    case (OUTLINECOLOR):
×
5603
      if (loadColor(&(legend->outlinecolor), NULL) != MS_SUCCESS)
×
5604
        return (-1);
5605
      break;
5606
    case (POSITION):
16✔
5607
      if ((legend->position =
16✔
5608
               getSymbol(6, MS_UL, MS_UR, MS_LL, MS_LR, MS_UC, MS_LC)) == -1)
16✔
5609
        return (-1);
5610
      break;
5611
    case (POSTLABELCACHE):
8✔
5612
      if ((legend->postlabelcache = getSymbol(2, MS_TRUE, MS_FALSE)) == -1)
8✔
5613
        return (-1);
5614
      break;
5615
    case (STATUS):
16✔
5616
      if ((legend->status = getSymbol(3, MS_ON, MS_OFF, MS_EMBED)) == -1)
16✔
5617
        return (-1);
5618
      break;
5619
    case (TRANSPARENT):
8✔
5620
      if ((legend->transparent = getSymbol(2, MS_ON, MS_OFF)) == -1)
8✔
5621
        return (-1);
5622
      break;
5623
    case (TEMPLATE):
×
5624
      if (getString(&legend->template) == MS_FAILURE)
×
5625
        return (-1);
5626
      break;
5627
    default:
×
5628
      if (strlen(msyystring_buffer) > 0) {
×
5629
        msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
5630
                   "loadLegend()", msyystring_buffer, msyylineno);
5631
        return (-1);
×
5632
      } else {
5633
        return (0); /* end of a string, not an error */
5634
      }
5635
    }
5636
  } /* next token */
5637
}
5638

5639
int msUpdateLegendFromString(legendObj *legend, char *string) {
×
5640
  if (!legend || !string)
×
5641
    return MS_FAILURE;
5642

5643
  msAcquireLock(TLOCK_PARSER);
×
5644

5645
  msyystate = MS_TOKENIZE_STRING;
×
5646
  msyystring = string;
×
5647
  msyylex(); /* sets things up, but doesn't process any tokens */
×
5648

5649
  msyylineno = 1; /* start at line 1 */
×
5650

5651
  if (loadLegend(legend, legend->map) == -1) {
×
5652
    msReleaseLock(TLOCK_PARSER);
×
5653
    return MS_FAILURE; /* parse error */
×
5654
    ;
5655
  }
5656

5657
  msyylex_destroy();
×
5658
  msReleaseLock(TLOCK_PARSER);
×
5659
  return MS_SUCCESS;
×
5660
}
5661

5662
static void writeLegend(FILE *stream, int indent, legendObj *legend) {
1✔
5663
  colorObj c;
5664

5665
  indent++;
1✔
5666
  writeBlockBegin(stream, indent, "LEGEND");
1✔
5667
  MS_INIT_COLOR(c, 255, 255, 255, 255);
1✔
5668
  writeColor(stream, indent, "IMAGECOLOR", &c, &(legend->imagecolor));
1✔
5669
  writeDimension(stream, indent, "KEYSIZE", legend->keysizex, legend->keysizey,
1✔
5670
                 NULL, NULL);
5671
  writeDimension(stream, indent, "KEYSPACING", legend->keyspacingx,
1✔
5672
                 legend->keyspacingy, NULL, NULL);
1✔
5673
  writeLabel(stream, indent, &(legend->label));
1✔
5674
  writeColor(stream, indent, "OUTLINECOLOR", NULL, &(legend->outlinecolor));
1✔
5675
  if (legend->status == MS_EMBED)
1✔
5676
    writeKeyword(stream, indent, "POSITION", legend->position, 6, MS_LL, "LL",
×
5677
                 MS_UL, "UL", MS_UR, "UR", MS_LR, "LR", MS_UC, "UC", MS_LC,
5678
                 "LC");
5679
  writeKeyword(stream, indent, "POSTLABELCACHE", legend->postlabelcache, 1,
1✔
5680
               MS_TRUE, "TRUE");
5681
  writeKeyword(stream, indent, "STATUS", legend->status, 3, MS_ON, "ON", MS_OFF,
1✔
5682
               "OFF", MS_EMBED, "EMBED");
5683
  writeKeyword(stream, indent, "TRANSPARENT", legend->transparent, 2, MS_TRUE,
1✔
5684
               "TRUE", MS_FALSE, "FALSE");
5685
  writeString(stream, indent, "TEMPLATE", NULL, legend->template);
1✔
5686
  writeBlockEnd(stream, indent, "LEGEND");
1✔
5687
  writeLineFeed(stream);
5688
}
1✔
5689

5690
char *msWriteLegendToString(legendObj *legend) {
×
5691
  msIOContext context;
5692
  msIOBuffer buffer;
5693

5694
  context.label = NULL;
×
5695
  context.write_channel = MS_TRUE;
×
5696
  context.readWriteFunc = msIO_bufferWrite;
×
5697
  context.cbData = &buffer;
×
5698
  buffer.data = NULL;
×
5699
  buffer.data_len = 0;
×
5700
  buffer.data_offset = 0;
×
5701

5702
  msIO_installHandlers(NULL, &context, NULL);
×
5703

5704
  writeLegend(stdout, -1, legend);
×
5705
  msIO_bufferWrite(&buffer, "", 1);
×
5706

5707
  msIO_installHandlers(NULL, NULL, NULL);
×
5708

5709
  return (char *)buffer.data;
×
5710
}
5711

5712
/*
5713
** Initialize, load and free a scalebarObj structure
5714
*/
5715
void initScalebar(scalebarObj *scalebar) {
3,289✔
5716
  MS_INIT_COLOR(scalebar->imagecolor, -1, -1, -1, 255);
3,289✔
5717
  scalebar->width = 200;
3,289✔
5718
  scalebar->height = 3;
3,289✔
5719
  scalebar->style = 0; /* only 2 styles at this point */
3,289✔
5720
  scalebar->intervals = 4;
3,289✔
5721
  initLabel(&scalebar->label);
3,289✔
5722
  scalebar->label.position = MS_XY; /* override */
3,289✔
5723
  MS_INIT_COLOR(scalebar->backgroundcolor, -1, -1, -1,
3,289✔
5724
                255); /* if not set, scalebar creation needs to set this to
5725
                         match the background color */
5726
  MS_INIT_COLOR(scalebar->color, 0, 0, 0, 255); /* default to black */
3,289✔
5727
  MS_INIT_COLOR(scalebar->outlinecolor, -1, -1, -1, 255);
3,289✔
5728
  scalebar->units = MS_MILES;
3,289✔
5729
  scalebar->status = MS_OFF;
3,289✔
5730
  scalebar->position = MS_LL;
3,289✔
5731
  scalebar->transparent = MS_NOOVERRIDE; /* no transparency */
3,289✔
5732
  scalebar->postlabelcache = MS_FALSE;   /* draw with labels */
3,289✔
5733
  scalebar->align = MS_ALIGN_CENTER;
3,289✔
5734
  scalebar->offsetx = 0;
3,289✔
5735
  scalebar->offsety = 0;
3,289✔
5736
}
3,289✔
5737

5738
void freeScalebar(scalebarObj *scalebar) { freeLabel(&(scalebar->label)); }
3,241✔
5739

5740
int loadScalebar(scalebarObj *scalebar) {
414✔
5741
  for (;;) {
5742
    switch (msyylex()) {
4,934✔
5743
    case (ALIGN):
×
5744
      if ((scalebar->align = getSymbol(3, MS_ALIGN_LEFT, MS_ALIGN_CENTER,
×
5745
                                       MS_ALIGN_RIGHT)) == -1)
5746
        return (-1);
5747
      break;
5748
    case (BACKGROUNDCOLOR):
412✔
5749
      if (loadColor(&(scalebar->backgroundcolor), NULL) != MS_SUCCESS)
412✔
5750
        return (-1);
5751
      break;
5752
    case (COLOR):
412✔
5753
      if (loadColor(&(scalebar->color), NULL) != MS_SUCCESS)
412✔
5754
        return (-1);
5755
      break;
5756
    case (EOF):
×
5757
      msSetError(MS_EOFERR, NULL, "loadScalebar()");
×
5758
      return (-1);
×
5759
    case (END):
5760
      return (0);
5761
      break;
5762
    case (IMAGECOLOR):
402✔
5763
      if (loadColor(&(scalebar->imagecolor), NULL) != MS_SUCCESS)
402✔
5764
        return (-1);
5765
      break;
5766
    case (INTERVALS):
10✔
5767
      if (getInteger(&(scalebar->intervals), MS_NUM_CHECK_RANGE,
10✔
5768
                     MS_SCALEBAR_INTERVALS_MIN,
5769
                     MS_SCALEBAR_INTERVALS_MAX) == -1)
5770
        return (-1);
5771
      break;
5772
    case (LABEL):
412✔
5773
      if (loadLabel(&(scalebar->label)) == -1)
412✔
5774
        return (-1);
5775
      scalebar->label.angle = 0;
412✔
5776
      break;
412✔
5777
    case (OUTLINECOLOR):
10✔
5778
      if (loadColor(&(scalebar->outlinecolor), NULL) != MS_SUCCESS)
10✔
5779
        return (-1);
5780
      break;
5781
    case (POSITION):
402✔
5782
      if ((scalebar->position =
402✔
5783
               getSymbol(6, MS_UL, MS_UR, MS_LL, MS_LR, MS_UC, MS_LC)) == -1)
402✔
5784
        return (-1);
5785
      break;
5786
    case (POSTLABELCACHE):
402✔
5787
      if ((scalebar->postlabelcache = getSymbol(2, MS_TRUE, MS_FALSE)) == -1)
402✔
5788
        return (-1);
5789
      break;
5790
    case (SCALEBAR):
5791
      break; /* for string loads */
5792
    case (SIZE):
414✔
5793
      if (getInteger(&(scalebar->width), MS_NUM_CHECK_RANGE,
414✔
5794
                     MS_SCALEBAR_WIDTH_MIN, MS_SCALEBAR_WIDTH_MAX) == -1)
5795
        return (-1);
5796
      if (getInteger(&(scalebar->height), MS_NUM_CHECK_RANGE,
414✔
5797
                     MS_SCALEBAR_HEIGHT_MIN, MS_SCALEBAR_HEIGHT_MAX) == -1)
5798
        return (-1);
5799
      break;
5800
    case (STATUS):
414✔
5801
      if ((scalebar->status = getSymbol(3, MS_ON, MS_OFF, MS_EMBED)) == -1)
414✔
5802
        return (-1);
5803
      break;
5804
    case (STYLE):
402✔
5805
      if (getInteger(&(scalebar->style), MS_NUM_CHECK_RANGE, 0, 1) == -1)
402✔
5806
        return (-1); // only 2 styles: 0 and 1
5807
      break;
5808
    case (TRANSPARENT):
412✔
5809
      if ((scalebar->transparent = getSymbol(2, MS_ON, MS_OFF)) == -1)
412✔
5810
        return (-1);
5811
      break;
5812
    case (UNITS):
414✔
5813
      if ((scalebar->units =
414✔
5814
               getSymbol(6, MS_INCHES, MS_FEET, MS_MILES, MS_METERS,
414✔
5815
                         MS_KILOMETERS, MS_NAUTICALMILES)) == -1)
5816
        return (-1);
5817
      break;
5818
    case (OFFSET):
2✔
5819
      if (getInteger(&(scalebar->offsetx), MS_NUM_CHECK_RANGE,
2✔
5820
                     MS_SCALEBAR_OFFSET_MIN, MS_SCALEBAR_OFFSET_MAX) == -1)
5821
        return (-1);
5822
      if (getInteger(&(scalebar->offsety), MS_NUM_CHECK_RANGE,
2✔
5823
                     MS_SCALEBAR_OFFSET_MIN, MS_SCALEBAR_OFFSET_MAX) == -1)
5824
        return (-1);
5825
      break;
5826
    default:
×
5827
      if (strlen(msyystring_buffer) > 0) {
×
5828
        msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
5829
                   "loadScalebar()", msyystring_buffer, msyylineno);
5830
        return (-1);
×
5831
      } else {
5832
        return (0); /* end of a string, not an error */
5833
      }
5834
    }
5835
  } /* next token */
5836
}
5837

5838
int msUpdateScalebarFromString(scalebarObj *scalebar, char *string) {
×
5839
  if (!scalebar || !string)
×
5840
    return MS_FAILURE;
5841

5842
  msAcquireLock(TLOCK_PARSER);
×
5843

5844
  msyystate = MS_TOKENIZE_STRING;
×
5845
  msyystring = string;
×
5846
  msyylex(); /* sets things up, but doesn't process any tokens */
×
5847

5848
  msyylineno = 1; /* start at line 1 */
×
5849

5850
  if (loadScalebar(scalebar) == -1) {
×
5851
    msReleaseLock(TLOCK_PARSER);
×
5852
    return MS_FAILURE; /* parse error */
×
5853
    ;
5854
  }
5855

5856
  msyylex_destroy();
×
5857
  msReleaseLock(TLOCK_PARSER);
×
5858
  return MS_SUCCESS;
×
5859
}
5860

5861
static void writeScalebar(FILE *stream, int indent, scalebarObj *scalebar) {
1✔
5862
  colorObj c;
5863

5864
  indent++;
1✔
5865
  writeBlockBegin(stream, indent, "SCALEBAR");
1✔
5866
  writeKeyword(stream, indent, "ALIGN", scalebar->align, 2, MS_ALIGN_LEFT,
1✔
5867
               "LEFT", MS_ALIGN_RIGHT, "RIGHT");
5868
  writeColor(stream, indent, "BACKGROUNDCOLOR", NULL,
1✔
5869
             &(scalebar->backgroundcolor));
5870
  MS_INIT_COLOR(c, 0, 0, 0, 255);
1✔
5871
  writeColor(stream, indent, "COLOR", &c, &(scalebar->color));
1✔
5872
  writeColor(stream, indent, "IMAGECOLOR", NULL, &(scalebar->imagecolor));
1✔
5873
  writeNumber(stream, indent, "INTERVALS", -1, scalebar->intervals);
1✔
5874
  writeLabel(stream, indent, &(scalebar->label));
1✔
5875
  writeColor(stream, indent, "OUTLINECOLOR", NULL, &(scalebar->outlinecolor));
1✔
5876
  if (scalebar->status == MS_EMBED)
1✔
5877
    writeKeyword(stream, indent, "POSITION", scalebar->position, 6, MS_LL, "LL",
1✔
5878
                 MS_UL, "UL", MS_UR, "UR", MS_LR, "LR", MS_UC, "UC", MS_LC,
5879
                 "LC");
5880
  writeKeyword(stream, indent, "POSTLABELCACHE", scalebar->postlabelcache, 1,
1✔
5881
               MS_TRUE, "TRUE");
5882
  writeDimension(stream, indent, "SIZE", scalebar->width, scalebar->height,
1✔
5883
                 NULL, NULL);
5884
  writeKeyword(stream, indent, "STATUS", scalebar->status, 3, MS_ON, "ON",
1✔
5885
               MS_OFF, "OFF", MS_EMBED, "EMBED");
5886
  writeNumber(stream, indent, "STYLE", 0, scalebar->style);
1✔
5887
  writeKeyword(stream, indent, "TRANSPARENT", scalebar->transparent, 2, MS_TRUE,
1✔
5888
               "TRUE", MS_FALSE, "FALSE");
5889
  writeKeyword(stream, indent, "UNITS", scalebar->units, 6, MS_INCHES, "INCHES",
1✔
5890
               MS_FEET, "FEET", MS_MILES, "MILES", MS_METERS, "METERS",
5891
               MS_KILOMETERS, "KILOMETERS", MS_NAUTICALMILES, "NAUTICALMILES");
5892
  writeBlockEnd(stream, indent, "SCALEBAR");
1✔
5893
  writeLineFeed(stream);
5894
}
1✔
5895

5896
char *msWriteScalebarToString(scalebarObj *scalebar) {
×
5897
  msIOContext context;
5898
  msIOBuffer buffer;
5899

5900
  context.label = NULL;
×
5901
  context.write_channel = MS_TRUE;
×
5902
  context.readWriteFunc = msIO_bufferWrite;
×
5903
  context.cbData = &buffer;
×
5904
  buffer.data = NULL;
×
5905
  buffer.data_len = 0;
×
5906
  buffer.data_offset = 0;
×
5907

5908
  msIO_installHandlers(NULL, &context, NULL);
×
5909

5910
  writeScalebar(stdout, -1, scalebar);
×
5911
  msIO_bufferWrite(&buffer, "", 1);
×
5912

5913
  msIO_installHandlers(NULL, NULL, NULL);
×
5914

5915
  return (char *)buffer.data;
×
5916
}
5917

5918
/*
5919
** Initialize a queryMapObj structure
5920
*/
5921
void initQueryMap(queryMapObj *querymap) {
3,250✔
5922
  querymap->width = querymap->height = -1;
3,250✔
5923
  querymap->style = MS_HILITE;
3,250✔
5924
  querymap->status = MS_OFF;
3,250✔
5925
  MS_INIT_COLOR(querymap->color, 255, 255, 0, 255); /* yellow */
3,250✔
5926
}
3,250✔
5927

5928
int loadQueryMap(queryMapObj *querymap, mapObj *map) {
165✔
5929
  querymap->map = (mapObj *)map;
165✔
5930

5931
  for (;;) {
5932
    switch (msyylex()) {
406✔
5933
    case (QUERYMAP):
5934
      break; /* for string loads */
5935
    case (COLOR):
154✔
5936
      if (loadColor(&(querymap->color), NULL) != MS_SUCCESS)
154✔
5937
        return MS_FAILURE;
5938
      break;
5939
    case (EOF):
×
5940
      msSetError(MS_EOFERR, NULL, "loadQueryMap()");
×
5941
      return (-1);
×
5942
    case (END):
5943
      return (0);
5944
      break;
5945
    case (SIZE):
29✔
5946
      /*
5947
       ** we do -1 (and avoid 0) here to maintain backwards compatibility as
5948
       *older versions write "SIZE -1 -1" when saving a mapfile
5949
       */
5950
      if (getInteger(&(querymap->width), MS_NUM_CHECK_RANGE, -1,
29✔
5951
                     querymap->map->maxsize) == -1 ||
29✔
5952
          querymap->width == 0) {
29✔
5953
        msSetError(MS_MISCERR, "Invalid SIZE value (line %d)", "loadQueryMap()",
×
5954
                   msyylineno);
5955
        return (-1);
×
5956
      }
5957
      if (getInteger(&(querymap->height), MS_NUM_CHECK_RANGE, -1,
29✔
5958
                     querymap->map->maxsize) == -1 ||
29✔
5959
          querymap->height == 0) {
29✔
5960
        msSetError(MS_MISCERR, "Invalid SIZE value (line %d)", "loadQueryMap()",
×
5961
                   msyylineno);
5962
        return (-1);
×
5963
      }
5964
      break;
5965
    case (STATUS):
29✔
5966
      if ((querymap->status = getSymbol(2, MS_ON, MS_OFF)) == -1)
29✔
5967
        return (-1);
5968
      break;
5969
    case (STYLE):
29✔
5970
    case (TYPE):
5971
      if ((querymap->style = getSymbol(3, MS_NORMAL, MS_HILITE, MS_SELECTED)) ==
29✔
5972
          -1)
5973
        return (-1);
5974
      break;
5975
    default:
×
5976
      if (strlen(msyystring_buffer) > 0) {
×
5977
        msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
5978
                   "loadQueryMap()", msyystring_buffer, msyylineno);
5979
        return (-1);
×
5980
      } else {
5981
        return (0); /* end of a string, not an error */
5982
      }
5983
    }
5984
  }
5985
}
5986

5987
int msUpdateQueryMapFromString(queryMapObj *querymap, char *string) {
×
5988
  if (!querymap || !string)
×
5989
    return MS_FAILURE;
5990

5991
  msAcquireLock(TLOCK_PARSER);
×
5992

5993
  msyystate = MS_TOKENIZE_STRING;
×
5994
  msyystring = string;
×
5995
  msyylex(); /* sets things up, but doesn't process any tokens */
×
5996

5997
  msyylineno = 1; /* start at line 1 */
×
5998

5999
  if (loadQueryMap(querymap, querymap->map) == -1) {
×
6000
    msReleaseLock(TLOCK_PARSER);
×
6001
    return MS_FAILURE; /* parse error */
×
6002
    ;
6003
  }
6004

6005
  msyylex_destroy();
×
6006
  msReleaseLock(TLOCK_PARSER);
×
6007
  return MS_SUCCESS;
×
6008
}
6009

6010
static void writeQueryMap(FILE *stream, int indent, queryMapObj *querymap) {
1✔
6011
  colorObj c;
6012

6013
  indent++;
1✔
6014
  writeBlockBegin(stream, indent, "QUERYMAP");
1✔
6015
  MS_INIT_COLOR(c, 255, 255, 0, 255);
1✔
6016
  writeColor(stream, indent, "COLOR", &c, &(querymap->color));
1✔
6017
  if (querymap->width != -1 &&
1✔
6018
      querymap->height != -1) // don't write SIZE if not explicitly set
×
6019
    writeDimension(stream, indent, "SIZE", querymap->width, querymap->height,
×
6020
                   NULL, NULL);
6021
  writeKeyword(stream, indent, "STATUS", querymap->status, 2, MS_ON, "ON",
1✔
6022
               MS_OFF, "OFF");
6023
  writeKeyword(stream, indent, "STYLE", querymap->style, 3, MS_NORMAL, "NORMAL",
1✔
6024
               MS_HILITE, "HILITE", MS_SELECTED, "SELECTED");
6025
  writeBlockEnd(stream, indent, "QUERYMAP");
1✔
6026
  writeLineFeed(stream);
6027
}
1✔
6028

6029
char *msWriteQueryMapToString(queryMapObj *querymap) {
×
6030
  msIOContext context;
6031
  msIOBuffer buffer;
6032

6033
  context.label = NULL;
×
6034
  context.write_channel = MS_TRUE;
×
6035
  context.readWriteFunc = msIO_bufferWrite;
×
6036
  context.cbData = &buffer;
×
6037
  buffer.data = NULL;
×
6038
  buffer.data_len = 0;
×
6039
  buffer.data_offset = 0;
×
6040

6041
  msIO_installHandlers(NULL, &context, NULL);
×
6042

6043
  writeQueryMap(stdout, -1, querymap);
×
6044
  msIO_bufferWrite(&buffer, "", 1);
×
6045

6046
  msIO_installHandlers(NULL, NULL, NULL);
×
6047

6048
  return (char *)buffer.data;
×
6049
}
6050

6051
/*
6052
** Initialize a webObj structure
6053
*/
6054
void initWeb(webObj *web) {
3,250✔
6055
  web->template = NULL;
3,250✔
6056
  web->header = web->footer = NULL;
3,250✔
6057
  web->error = web->empty = NULL;
3,250✔
6058
  web->mintemplate = web->maxtemplate = NULL;
3,250✔
6059
  web->minscaledenom = web->maxscaledenom = -1;
3,250✔
6060
  web->imagepath = msStrdup("");
3,250✔
6061
  web->temppath = NULL;
3,250✔
6062
  web->imageurl = msStrdup("");
3,250✔
6063

6064
  initHashTable(&(web->metadata));
3,250✔
6065
  initHashTable(&(web->validation));
3,250✔
6066

6067
  web->map = NULL;
3,250✔
6068
  web->queryformat = msStrdup("text/html");
3,250✔
6069
  web->legendformat = msStrdup("text/html");
3,250✔
6070
  web->browseformat = msStrdup("text/html");
3,250✔
6071
}
3,250✔
6072

6073
void freeWeb(webObj *web) {
3,241✔
6074
  msFree(web->template);
3,241✔
6075
  msFree(web->header);
3,241✔
6076
  msFree(web->footer);
3,241✔
6077
  msFree(web->error);
3,241✔
6078
  msFree(web->empty);
3,241✔
6079
  msFree(web->maxtemplate);
3,241✔
6080
  msFree(web->mintemplate);
3,241✔
6081
  msFree(web->imagepath);
3,241✔
6082
  msFree(web->temppath);
3,241✔
6083
  msFree(web->imageurl);
3,241✔
6084
  msFree(web->queryformat);
3,241✔
6085
  msFree(web->legendformat);
3,241✔
6086
  msFree(web->browseformat);
3,241✔
6087
  msFreeHashItems(&(web->metadata));
3,241✔
6088
  msFreeHashItems(&(web->validation));
3,241✔
6089
}
3,241✔
6090

6091
static void writeWeb(FILE *stream, int indent, webObj *web) {
1✔
6092
  indent++;
1✔
6093
  writeBlockBegin(stream, indent, "WEB");
1✔
6094
  writeString(stream, indent, "BROWSEFORMAT", "text/html", web->browseformat);
1✔
6095
  writeString(stream, indent, "EMPTY", NULL, web->empty);
1✔
6096
  writeString(stream, indent, "ERROR", NULL, web->error);
1✔
6097
  writeString(stream, indent, "FOOTER", NULL, web->footer);
1✔
6098
  writeString(stream, indent, "HEADER", NULL, web->header);
1✔
6099
  writeString(stream, indent, "IMAGEPATH", "", web->imagepath);
1✔
6100
  writeString(stream, indent, "TEMPPATH", NULL, web->temppath);
1✔
6101
  writeString(stream, indent, "IMAGEURL", "", web->imageurl);
1✔
6102
  writeString(stream, indent, "LEGENDFORMAT", "text/html", web->legendformat);
1✔
6103
  writeNumber(stream, indent, "MAXSCALEDENOM", -1, web->maxscaledenom);
1✔
6104
  writeString(stream, indent, "MAXTEMPLATE", NULL, web->maxtemplate);
1✔
6105
  writeHashTable(stream, indent, "METADATA", &(web->metadata));
1✔
6106
  writeNumber(stream, indent, "MINSCALEDENOM", -1, web->minscaledenom);
1✔
6107
  writeString(stream, indent, "MINTEMPLATE", NULL, web->mintemplate);
1✔
6108
  writeString(stream, indent, "QUERYFORMAT", "text/html", web->queryformat);
1✔
6109
  writeString(stream, indent, "TEMPLATE", NULL, web->template);
1✔
6110
  writeHashTable(stream, indent, "VALIDATION", &(web->validation));
1✔
6111
  writeBlockEnd(stream, indent, "WEB");
1✔
6112
  writeLineFeed(stream);
6113
}
1✔
6114

6115
char *msWriteWebToString(webObj *web) {
×
6116
  msIOContext context;
6117
  msIOBuffer buffer;
6118

6119
  context.label = NULL;
×
6120
  context.write_channel = MS_TRUE;
×
6121
  context.readWriteFunc = msIO_bufferWrite;
×
6122
  context.cbData = &buffer;
×
6123
  buffer.data = NULL;
×
6124
  buffer.data_len = 0;
×
6125
  buffer.data_offset = 0;
×
6126

6127
  msIO_installHandlers(NULL, &context, NULL);
×
6128

6129
  writeWeb(stdout, -1, web);
×
6130
  msIO_bufferWrite(&buffer, "", 1);
×
6131

6132
  msIO_installHandlers(NULL, NULL, NULL);
×
6133

6134
  return (char *)buffer.data;
×
6135
}
6136

6137
int loadWeb(webObj *web, mapObj *map) {
2,470✔
6138
  web->map = (mapObj *)map;
2,470✔
6139

6140
  for (;;) {
6141
    switch (msyylex()) {
8,234✔
6142
    case (BROWSEFORMAT): /* change to use validation in 6.0 */
×
6143
      free(web->browseformat);
×
6144
      web->browseformat = NULL; /* there is a default */
×
6145
      if (getString(&web->browseformat) == MS_FAILURE)
×
6146
        return (-1);
6147
      break;
6148
    case (EMPTY):
38✔
6149
      if (getString(&web->empty) == MS_FAILURE)
38✔
6150
        return (-1);
6151
      break;
6152
    case (WEB):
6153
      break; /* for string loads */
6154
    case (EOF):
×
6155
      msSetError(MS_EOFERR, NULL, "loadWeb()");
×
6156
      return (-1);
×
6157
    case (END):
6158
      return (0);
6159
      break;
6160
    case (ERROR):
×
6161
      if (getString(&web->error) == MS_FAILURE)
×
6162
        return (-1);
6163
      break;
6164
    case (FOOTER):
50✔
6165
      if (getString(&web->footer) == MS_FAILURE)
50✔
6166
        return (-1); /* getString() cleans up previously allocated string */
6167
      break;
6168
    case (HEADER):
50✔
6169
      if (getString(&web->header) == MS_FAILURE)
50✔
6170
        return (-1); /* getString() cleans up previously allocated string */
6171
      break;
6172
    case (IMAGEPATH):
1,444✔
6173
      if (getString(&web->imagepath) == MS_FAILURE)
1,444✔
6174
        return (-1);
6175
      break;
6176
    case (TEMPPATH):
×
6177
      if (getString(&web->temppath) == MS_FAILURE)
×
6178
        return (-1);
6179
      break;
6180
    case (IMAGEURL):
1,385✔
6181
      if (getString(&web->imageurl) == MS_FAILURE)
1,385✔
6182
        return (-1);
6183
      break;
6184
    case (LEGENDFORMAT): /* change to use validation in 6.0 */
×
6185
      free(web->legendformat);
×
6186
      web->legendformat = NULL; /* there is a default */
×
6187
      if (getString(&web->legendformat) == MS_FAILURE)
×
6188
        return (-1);
6189
      break;
6190
    case (MAXSCALEDENOM):
144✔
6191
      if (getDouble(&web->maxscaledenom, MS_NUM_CHECK_GTE, 0, -1) == -1)
144✔
6192
        return (-1);
6193
      break;
6194
    case (MAXTEMPLATE):
×
6195
      if (getString(&web->maxtemplate) == MS_FAILURE)
×
6196
        return (-1);
6197
      break;
6198
    case (METADATA):
2,357✔
6199
      if (loadHashTable(&(web->metadata)) != MS_SUCCESS)
2,357✔
6200
        return (-1);
6201
      break;
6202
    case (MINSCALEDENOM):
144✔
6203
      if (getDouble(&web->minscaledenom, MS_NUM_CHECK_GTE, 0, -1) == -1)
144✔
6204
        return (-1);
6205
      break;
6206
    case (MINTEMPLATE):
×
6207
      if (getString(&web->mintemplate) == MS_FAILURE)
×
6208
        return (-1);
6209
      break;
6210
    case (QUERYFORMAT): /* change to use validation in 6.0 */
75✔
6211
      free(web->queryformat);
75✔
6212
      web->queryformat = NULL; /* there is a default */
75✔
6213
      if (getString(&web->queryformat) == MS_FAILURE)
75✔
6214
        return (-1);
6215
      break;
6216
    case (TEMPLATE):
×
6217
      if (getString(&web->template) == MS_FAILURE)
×
6218
        return (-1); /* getString() cleans up previously allocated string */
6219
      break;
6220
    case (VALIDATION):
77✔
6221
      if (loadHashTable(&(web->validation)) != MS_SUCCESS)
77✔
6222
        return (-1);
6223
      break;
6224
    default:
×
6225
      if (strlen(msyystring_buffer) > 0) {
×
6226
        msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
6227
                   "loadWeb()", msyystring_buffer, msyylineno);
6228
        return (-1);
×
6229
      } else {
6230
        return (0); /* end of a string, not an error */
6231
      }
6232
    }
6233
  }
6234
}
6235

6236
int msUpdateWebFromString(webObj *web, char *string) {
×
6237
  if (!web || !string)
×
6238
    return MS_FAILURE;
6239

6240
  msAcquireLock(TLOCK_PARSER);
×
6241

6242
  msyystate = MS_TOKENIZE_STRING;
×
6243
  msyystring = string;
×
6244
  msyylex(); /* sets things up, but doesn't process any tokens */
×
6245

6246
  msyylineno = 1; /* start at line 1 */
×
6247

6248
  if (loadWeb(web, web->map) == -1) {
×
6249
    msReleaseLock(TLOCK_PARSER);
×
6250
    return MS_FAILURE; /* parse error */
×
6251
    ;
6252
  }
6253

6254
  msyylex_destroy();
×
6255
  msReleaseLock(TLOCK_PARSER);
×
6256
  return MS_SUCCESS;
×
6257
}
6258

6259
/*
6260
** Initialize, load and free a mapObj structure
6261
**
6262
** This really belongs in mapobject.c, but currently it also depends on
6263
** lots of other init methods in this file.
6264
*/
6265

6266
int initMap(mapObj *map) {
3,250✔
6267
  int i = 0;
6268
  MS_REFCNT_INIT(map);
3,250✔
6269

6270
  map->debug = (int)msGetGlobalDebugLevel();
3,250✔
6271

6272
  /* Set maxlayers = 0, layers[] and layerorder[] will be allocated as needed,
6273
   * on the first call to msGrowMapLayers()
6274
   */
6275
  map->numlayers = 0;
3,250✔
6276
  map->maxlayers = 0;
3,250✔
6277
  map->layers = NULL;
3,250✔
6278
  map->layerorder =
3,250✔
6279
      NULL; /* used to modify the order in which the layers are drawn */
6280

6281
  map->status = MS_ON;
3,250✔
6282
  map->name = msStrdup("MS");
3,250✔
6283
  map->extent.minx = map->extent.miny = map->extent.maxx = map->extent.maxy =
3,250✔
6284
      -1.0;
6285

6286
  map->scaledenom = -1.0;
3,250✔
6287
  map->resolution = MS_DEFAULT_RESOLUTION;    /* pixels per inch */
3,250✔
6288
  map->defresolution = MS_DEFAULT_RESOLUTION; /* pixels per inch */
3,250✔
6289

6290
  map->height = map->width = -1;
3,250✔
6291
  map->maxsize = MS_MAXIMAGESIZE_DEFAULT;
3,250✔
6292

6293
  map->gt.need_geotransform = MS_FALSE;
3,250✔
6294
  map->gt.rotation_angle = 0.0;
3,250✔
6295

6296
  map->units = MS_METERS;
3,250✔
6297
  map->cellsize = 0;
3,250✔
6298
  map->shapepath = NULL;
3,250✔
6299
  map->mappath = NULL;
3,250✔
6300
  map->sldurl = NULL;
3,250✔
6301

6302
  MS_INIT_COLOR(map->imagecolor, 255, 255, 255, 255); /* white */
3,250✔
6303

6304
  map->numoutputformats = 0;
3,250✔
6305
  map->outputformatlist = NULL;
3,250✔
6306
  map->outputformat = NULL;
3,250✔
6307

6308
  /* map->configoptions = msCreateHashTable();; */
6309
  initHashTable(&(map->configoptions));
3,250✔
6310

6311
  map->imagetype = NULL;
3,250✔
6312

6313
  map->palette.numcolors = 0;
3,250✔
6314

6315
  for (i = 0; i < MS_MAX_LABEL_PRIORITY; i++) {
35,750✔
6316
    map->labelcache.slots[i].labels =
32,500✔
6317
        NULL; /* cache is initialize at draw time */
6318
    map->labelcache.slots[i].cachesize = 0;
32,500✔
6319
    map->labelcache.slots[i].numlabels = 0;
32,500✔
6320
    map->labelcache.slots[i].markers = NULL;
32,500✔
6321
    map->labelcache.slots[i].markercachesize = 0;
32,500✔
6322
    map->labelcache.slots[i].nummarkers = 0;
32,500✔
6323
  }
6324

6325
  map->fontset.filename = NULL;
3,250✔
6326
  map->fontset.numfonts = 0;
3,250✔
6327

6328
  /* map->fontset.fonts = NULL; */
6329
  initHashTable(&(map->fontset.fonts));
3,250✔
6330

6331
  msInitSymbolSet(&map->symbolset);
3,250✔
6332
  map->symbolset.fontset = &(map->fontset);
3,250✔
6333
  map->symbolset.map = map;
3,250✔
6334

6335
  initLegend(&map->legend);
3,250✔
6336
  initScalebar(&map->scalebar);
3,250✔
6337
  initWeb(&map->web);
3,250✔
6338
  initReferenceMap(&map->reference);
3,250✔
6339
  initQueryMap(&map->querymap);
3,250✔
6340

6341
  map->projContext = msProjectionContextGetFromPool();
3,250✔
6342

6343
  if (msInitProjection(&(map->projection)) == -1)
3,250✔
6344
    return (-1);
6345
  if (msInitProjection(&(map->latlon)) == -1)
3,250✔
6346
    return (-1);
6347

6348
  msProjectionSetContext(&(map->projection), map->projContext);
3,250✔
6349
  msProjectionSetContext(&(map->latlon), map->projContext);
3,250✔
6350

6351
  /* initialize a default "geographic" projection */
6352
  map->latlon.numargs = 2;
3,250✔
6353
  map->latlon.args[0] = msStrdup("proj=latlong");
3,250✔
6354
  map->latlon.args[1] =
6,500✔
6355
      msStrdup("ellps=WGS84"); /* probably want a different ellipsoid */
3,250✔
6356
  if (msProcessProjection(&(map->latlon)) == -1)
3,250✔
6357
    return (-1);
6358

6359
  map->templatepattern = map->datapattern = NULL;
3,250✔
6360

6361
  /* Encryption key information - see mapcrypto.c */
6362
  map->encryption_key_loaded = MS_FALSE;
3,250✔
6363

6364
  msInitQuery(&(map->query));
3,250✔
6365

6366
#ifdef USE_V8_MAPSCRIPT
6367
  map->v8context = NULL;
6368
#endif
6369

6370
  map->config = NULL;
3,250✔
6371

6372
  return (0);
3,250✔
6373
}
6374

6375
/*
6376
** Ensure there is at least one free entry in the layers and layerorder
6377
** arrays of this mapObj. Grow the allocated layers/layerorder arrays if
6378
** necessary and allocate a new layer for layers[numlayers] if there is
6379
** not already one, setting its contents to all zero bytes (i.e. does not
6380
** call initLayer() on it).
6381
**
6382
** This function is safe to use for the initial allocation of the layers[]
6383
** and layerorder[] arrays as well (i.e. when maxlayers==0 and layers==NULL)
6384
**
6385
** Returns a reference to the new layerObj on success, NULL on error.
6386
*/
6387
layerObj *msGrowMapLayers(mapObj *map) {
13,605✔
6388
  /* Do we need to increase the size of layers/layerorder by
6389
   * MS_LAYER_ALLOCSIZE?
6390
   */
6391
  if (map->numlayers == map->maxlayers) {
13,605✔
6392
    layerObj **newLayersPtr;
6393
    int *newLayerorderPtr;
6394
    int i, newsize;
6395

6396
    newsize = map->maxlayers + MS_LAYER_ALLOCSIZE;
3,199✔
6397

6398
    /* Alloc/realloc layers */
6399
    newLayersPtr =
6400
        (layerObj **)realloc(map->layers, newsize * sizeof(layerObj *));
3,199✔
6401
    MS_CHECK_ALLOC(newLayersPtr, newsize * sizeof(layerObj *), NULL);
3,199✔
6402

6403
    map->layers = newLayersPtr;
3,199✔
6404

6405
    /* Alloc/realloc layerorder */
6406
    newLayerorderPtr = (int *)realloc(map->layerorder, newsize * sizeof(int));
3,199✔
6407
    MS_CHECK_ALLOC(newLayerorderPtr, newsize * sizeof(int), NULL);
3,199✔
6408

6409
    map->layerorder = newLayerorderPtr;
3,199✔
6410

6411
    map->maxlayers = newsize;
3,199✔
6412
    for (i = map->numlayers; i < map->maxlayers; i++) {
207,935✔
6413
      map->layers[i] = NULL;
204,736✔
6414
      map->layerorder[i] = 0;
204,736✔
6415
    }
6416
  }
6417

6418
  if (map->layers[map->numlayers] == NULL) {
13,605✔
6419
    map->layers[map->numlayers] = (layerObj *)calloc(1, sizeof(layerObj));
13,605✔
6420
    MS_CHECK_ALLOC(map->layers[map->numlayers], sizeof(layerObj), NULL);
13,605✔
6421
  }
6422

6423
  return map->layers[map->numlayers];
13,605✔
6424
}
6425

6426
int msFreeLabelCacheSlot(labelCacheSlotObj *cacheslot) {
44,640✔
6427
  int i, j;
6428

6429
  /* free the labels */
6430
  if (cacheslot->labels) {
44,640✔
6431
    for (i = 0; i < cacheslot->numlabels; i++) {
16,893✔
6432

6433
      for (j = 0; j < cacheslot->labels[i].numtextsymbols; j++) {
14,714✔
6434
        freeTextSymbol(cacheslot->labels[i].textsymbols[j]);
10,051✔
6435
        free(cacheslot->labels[i].textsymbols[j]);
10,051✔
6436
      }
6437
      msFree(cacheslot->labels[i].textsymbols);
4,663✔
6438

6439
      if (cacheslot->labels[i].leaderline) {
4,663✔
6440
        msFree(cacheslot->labels[i].leaderline->point);
32✔
6441
        msFree(cacheslot->labels[i].leaderline);
32✔
6442
        msFree(cacheslot->labels[i].leaderbbox);
32✔
6443
      }
6444
    }
6445
  }
6446
  msFree(cacheslot->labels);
44,640✔
6447
  cacheslot->labels = NULL;
44,640✔
6448
  cacheslot->cachesize = 0;
44,640✔
6449
  cacheslot->numlabels = 0;
44,640✔
6450

6451
  msFree(cacheslot->markers);
44,640✔
6452
  cacheslot->markers = NULL;
44,640✔
6453
  cacheslot->markercachesize = 0;
44,640✔
6454
  cacheslot->nummarkers = 0;
44,640✔
6455

6456
  return (MS_SUCCESS);
44,640✔
6457
}
6458

6459
int msFreeLabelCache(labelCacheObj *cache) {
4,464✔
6460
  int p;
6461

6462
  for (p = 0; p < MS_MAX_LABEL_PRIORITY; p++) {
49,104✔
6463
    if (msFreeLabelCacheSlot(&(cache->slots[p])) != MS_SUCCESS)
44,640✔
6464
      return MS_FAILURE;
6465
  }
6466

6467
  cache->num_allocated_rendered_members = cache->num_rendered_members = 0;
4,464✔
6468
  msFree(cache->rendered_text_symbols);
4,464✔
6469

6470
  return MS_SUCCESS;
4,464✔
6471
}
6472

6473
int msInitLabelCacheSlot(labelCacheSlotObj *cacheslot) {
12,230✔
6474

6475
  if (cacheslot->labels || cacheslot->markers)
12,230✔
6476
    msFreeLabelCacheSlot(cacheslot);
×
6477

6478
  cacheslot->labels = (labelCacheMemberObj *)malloc(
12,230✔
6479
      sizeof(labelCacheMemberObj) * MS_LABELCACHEINITSIZE);
6480
  MS_CHECK_ALLOC(cacheslot->labels,
12,230✔
6481
                 sizeof(labelCacheMemberObj) * MS_LABELCACHEINITSIZE,
6482
                 MS_FAILURE);
6483

6484
  cacheslot->cachesize = MS_LABELCACHEINITSIZE;
12,230✔
6485
  cacheslot->numlabels = 0;
12,230✔
6486

6487
  cacheslot->markers = (markerCacheMemberObj *)malloc(
12,230✔
6488
      sizeof(markerCacheMemberObj) * MS_LABELCACHEINITSIZE);
6489
  MS_CHECK_ALLOC(cacheslot->markers,
12,230✔
6490
                 sizeof(markerCacheMemberObj) * MS_LABELCACHEINITSIZE,
6491
                 MS_FAILURE);
6492

6493
  cacheslot->markercachesize = MS_LABELCACHEINITSIZE;
12,230✔
6494
  cacheslot->nummarkers = 0;
12,230✔
6495

6496
  return (MS_SUCCESS);
12,230✔
6497
}
6498

6499
int msInitLabelCache(labelCacheObj *cache) {
1,223✔
6500
  int p;
6501

6502
  for (p = 0; p < MS_MAX_LABEL_PRIORITY; p++) {
13,453✔
6503
    if (msInitLabelCacheSlot(&(cache->slots[p])) != MS_SUCCESS)
12,230✔
6504
      return MS_FAILURE;
6505
  }
6506
  cache->gutter = 0;
1,223✔
6507
  cache->num_allocated_rendered_members = cache->num_rendered_members = 0;
1,223✔
6508
  cache->rendered_text_symbols = NULL;
1,223✔
6509

6510
  return MS_SUCCESS;
1,223✔
6511
}
6512

6513
static void writeMap(FILE *stream, int indent, mapObj *map) {
1✔
6514
  int i;
6515
  colorObj c;
6516

6517
  writeBlockBegin(stream, indent, "MAP");
1✔
6518
  writeNumber(stream, indent, "ANGLE", 0, map->gt.rotation_angle);
1✔
6519
  writeHashTableInline(stream, indent, "CONFIG", &(map->configoptions));
1✔
6520
  writeNumber(stream, indent, "DEBUG", 0, map->debug);
1✔
6521
  writeNumber(stream, indent, "DEFRESOLUTION", 72.0, map->defresolution);
1✔
6522
  writeExtent(stream, indent, "EXTENT", map->extent);
1✔
6523
  writeString(stream, indent, "FONTSET", NULL, map->fontset.filename);
1✔
6524
  MS_INIT_COLOR(c, 255, 255, 255, 255);
1✔
6525
  writeColor(stream, indent, "IMAGECOLOR", &c, &(map->imagecolor));
1✔
6526
  writeString(stream, indent, "IMAGETYPE", NULL, map->imagetype);
1✔
6527
  writeNumber(stream, indent, "MAXSIZE", MS_MAXIMAGESIZE_DEFAULT, map->maxsize);
1✔
6528
  writeString(stream, indent, "NAME", NULL, map->name);
1✔
6529
  writeNumber(stream, indent, "RESOLUTION", 72.0, map->resolution);
1✔
6530
  writeString(stream, indent, "SHAPEPATH", NULL, map->shapepath);
1✔
6531
  writeDimension(stream, indent, "SIZE", map->width, map->height, NULL, NULL);
1✔
6532
  writeKeyword(stream, indent, "STATUS", map->status, 2, MS_ON, "ON", MS_OFF,
1✔
6533
               "OFF");
6534
  writeString(stream, indent, "SYMBOLSET", NULL, map->symbolset.filename);
1✔
6535
  writeKeyword(stream, indent, "UNITS", map->units, 7, MS_INCHES, "INCHES",
1✔
6536
               MS_FEET, "FEET", MS_MILES, "MILES", MS_METERS, "METERS",
6537
               MS_KILOMETERS, "KILOMETERS", MS_NAUTICALMILES, "NAUTICALMILES",
6538
               MS_DD, "DD");
6539
  writeLineFeed(stream);
6540

6541
  writeOutputformat(stream, indent, map);
1✔
6542

6543
  /* write symbol with INLINE tag in mapfile */
6544
  for (i = 0; i < map->symbolset.numsymbols; i++) {
5✔
6545
    if (map->symbolset.symbol[i]->inmapfile)
4✔
6546
      writeSymbol(map->symbolset.symbol[i], stream);
×
6547
  }
6548

6549
  writeProjection(stream, indent, &(map->projection));
1✔
6550

6551
  writeLegend(stream, indent, &(map->legend));
1✔
6552
  writeQueryMap(stream, indent, &(map->querymap));
1✔
6553
  writeReferenceMap(stream, indent, &(map->reference));
1✔
6554
  writeScalebar(stream, indent, &(map->scalebar));
1✔
6555
  writeWeb(stream, indent, &(map->web));
1✔
6556

6557
  for (i = 0; i < map->numlayers; i++)
8✔
6558
    writeLayer(stream, indent, GET_LAYER(map, map->layerorder[i]));
7✔
6559

6560
  writeBlockEnd(stream, indent, "MAP");
1✔
6561
}
1✔
6562

6563
char *msWriteMapToString(mapObj *map) {
×
6564
  msIOContext context;
6565
  msIOBuffer buffer;
6566

6567
  context.label = NULL;
×
6568
  context.write_channel = MS_TRUE;
×
6569
  context.readWriteFunc = msIO_bufferWrite;
×
6570
  context.cbData = &buffer;
×
6571
  buffer.data = NULL;
×
6572
  buffer.data_len = 0;
×
6573
  buffer.data_offset = 0;
×
6574

6575
  msIO_installHandlers(NULL, &context, NULL);
×
6576

6577
  writeMap(stdout, 0, map);
×
6578
  msIO_bufferWrite(&buffer, "", 1);
×
6579

6580
  msIO_installHandlers(NULL, NULL, NULL);
×
6581

6582
  return (char *)buffer.data;
×
6583
}
6584

6585
int msSaveMap(mapObj *map, char *filename) {
1✔
6586
  FILE *stream;
6587
  char szPath[MS_MAXPATHLEN];
6588

6589
  if (!map) {
1✔
6590
    msSetError(MS_MISCERR, "Map is undefined.", "msSaveMap()");
×
6591
    return (-1);
×
6592
  }
6593

6594
  if (!filename) {
1✔
6595
    msSetError(MS_MISCERR, "Filename is undefined.", "msSaveMap()");
×
6596
    return (-1);
×
6597
  }
6598

6599
  stream = fopen(msBuildPath(szPath, map->mappath, filename), "w");
1✔
6600
  if (!stream) {
1✔
6601
    msSetErrorWithStatus(MS_IOERR, MS_HTTP_500_INTERNAL_SERVER_ERROR, "(%s)",
×
6602
                         "msSaveMap()", filename);
6603
    return (-1);
×
6604
  }
6605

6606
  writeMap(stream, 0, map);
1✔
6607
  fclose(stream);
1✔
6608

6609
  return (0);
1✔
6610
}
6611

6612
static void writeConfig(FILE *stream, int indent, configObj *config) {
×
6613
  writeBlockBegin(stream, indent, "CONFIG");
×
6614
  writeHashTable(stream, indent, "ENV", &(config->env));
×
6615
  writeHashTable(stream, indent, "MAPS", &(config->maps));
×
6616
  writeBlockEnd(stream, indent, "CONFIG");
×
6617
}
×
6618

6619
int msSaveConfig(configObj *config, const char *filename) {
×
6620
  FILE *stream;
6621

6622
  if (!config) {
×
6623
    msSetError(MS_MISCERR, "Config is undefined.", "msSaveConfigMap()");
×
6624
    return (-1);
×
6625
  }
6626

6627
  if (!filename) {
×
6628
    msSetError(MS_MISCERR, "Filename is undefined.", "msSaveConfigMap()");
×
6629
    return (-1);
×
6630
  }
6631

6632
  stream = fopen(filename, "w");
×
6633
  if (!stream) {
×
6634
    msSetErrorWithStatus(MS_IOERR, MS_HTTP_500_INTERNAL_SERVER_ERROR, "(%s)",
×
6635
                         "msSaveConfig()", filename);
6636
    return (-1);
×
6637
  }
6638

6639
  writeConfig(stream, 0, config);
×
6640
  fclose(stream);
×
6641

6642
  return (0);
×
6643
}
6644

6645
static int loadMapInternal(mapObj *map) {
3,145✔
6646
  int foundMapToken = MS_FALSE;
6647
  int foundBomToken = MS_FALSE;
6648
  int token;
6649

6650
  for (;;) {
6651

6652
    token = msyylex();
48,976✔
6653

6654
    if (!foundBomToken && token == BOM) {
48,976✔
6655
      foundBomToken = MS_TRUE;
6656
      if (!foundMapToken) {
×
6657
        continue; /*skip a leading bom*/
×
6658
      }
6659
    }
6660
    if (!foundMapToken && token != MAP) {
48,976✔
6661
      msSetError(MS_IDENTERR,
2✔
6662
                 "First token must be MAP, this doesn't look like a mapfile.",
6663
                 "msLoadMap()");
6664
      return (MS_FAILURE);
2✔
6665
    }
6666

6667
    switch (token) {
48,974✔
6668

6669
    case (CONFIG): {
88✔
6670
      char *key = NULL, *value = NULL;
88✔
6671

6672
      if (getString(&key) == MS_FAILURE)
88✔
6673
        return MS_FAILURE;
×
6674

6675
      if (getString(&value) == MS_FAILURE) {
88✔
6676
        free(key);
×
6677
        return MS_FAILURE;
×
6678
      }
6679

6680
      if (msSetConfigOption(map, key, value) == MS_FAILURE) {
88✔
6681
        free(key);
×
6682
        free(value);
×
6683
        return MS_FAILURE;
×
6684
      }
6685

6686
      free(key);
88✔
6687
      free(value);
88✔
6688
    } break;
88✔
6689
    case (DEBUG):
108✔
6690
      if ((map->debug = getSymbol(3, MS_ON, MS_OFF, MS_NUMBER)) == -1)
108✔
6691
        return MS_FAILURE;
6692
      if (map->debug == MS_NUMBER) {
108✔
6693
        if (msCheckNumber(msyynumber, MS_NUM_CHECK_RANGE, 0, 5) == MS_FAILURE) {
38✔
6694
          msSetError(MS_MISCERR,
×
6695
                     "Invalid DEBUG level, must be between 0 and 5 (line %d)",
6696
                     "msLoadMap()", msyylineno);
6697
          return (-1);
×
6698
        }
6699
        map->debug = (int)msyynumber;
38✔
6700
      }
6701
      break;
6702
    case (END):
3,141✔
6703
      if (msyyin) {
3,141✔
6704
        fclose(msyyin);
3,138✔
6705
        msyyin = NULL;
3,138✔
6706
      }
6707

6708
      /*** Make config options current ***/
6709
      msApplyMapConfigOptions(map);
3,141✔
6710

6711
      /*** Compute rotated extent info if applicable ***/
6712
      msMapComputeGeotransform(map);
3,141✔
6713

6714
      /*** OUTPUTFORMAT related setup ***/
6715
      if (msPostMapParseOutputFormatSetup(map) == MS_FAILURE)
3,141✔
6716
        return MS_FAILURE;
6717

6718
      if (loadSymbolSet(&(map->symbolset), map) == -1)
3,141✔
6719
        return MS_FAILURE;
6720

6721
      if (resolveSymbolNames(map) == MS_FAILURE)
3,141✔
6722
        return MS_FAILURE;
6723

6724
      if (msLoadFontSet(&(map->fontset), map) == -1)
3,141✔
6725
        return MS_FAILURE;
6726

6727
      return MS_SUCCESS;
6728
      break;
6729
    case (EOF):
×
6730
      msSetError(MS_EOFERR, NULL, "msLoadMap()");
×
6731
      return MS_FAILURE;
×
6732
    case (EXTENT): {
2,978✔
6733
      if (getDouble(&(map->extent.minx), MS_NUM_CHECK_NONE, -1, -1) == -1)
2,978✔
6734
        return MS_FAILURE;
6735
      if (getDouble(&(map->extent.miny), MS_NUM_CHECK_NONE, -1, -1) == -1)
2,978✔
6736
        return MS_FAILURE;
6737
      if (getDouble(&(map->extent.maxx), MS_NUM_CHECK_NONE, -1, -1) == -1)
2,978✔
6738
        return MS_FAILURE;
6739
      if (getDouble(&(map->extent.maxy), MS_NUM_CHECK_NONE, -1, -1) == -1)
2,978✔
6740
        return MS_FAILURE;
6741
      if (!MS_VALID_EXTENT(map->extent)) {
2,978✔
6742
        msSetError(MS_MISCERR,
×
6743
                   "Given map extent is invalid. Check that it "
6744
                   "is in the form: minx, miny, maxx, maxy",
6745
                   "loadMapInternal()");
6746
        return MS_FAILURE;
×
6747
      }
6748
    } break;
6749
    case (ANGLE): {
8✔
6750
      double rotation_angle;
6751
      if (getDouble(&(rotation_angle), MS_NUM_CHECK_RANGE, -360, 360) == -1)
8✔
6752
        return MS_FAILURE;
×
6753
      msMapSetRotation(map, rotation_angle);
8✔
6754
    } break;
8✔
6755
    case (FONTSET):
1,891✔
6756
      if (getString(&map->fontset.filename) == MS_FAILURE)
1,891✔
6757
        return MS_FAILURE;
6758
      break;
6759
    case (IMAGECOLOR):
2,114✔
6760
      if (loadColor(&(map->imagecolor), NULL) != MS_SUCCESS)
2,114✔
6761
        return MS_FAILURE;
6762
      break;
6763
    case (IMAGETYPE):
1,442✔
6764
      msFree(map->imagetype);
1,442✔
6765
      map->imagetype = getToken();
1,442✔
6766
      break;
1,442✔
6767
    case (LATLON):
×
NEW
6768
      if (loadProjection(&map->latlon, map) == -1)
×
6769
        return MS_FAILURE;
6770
      break;
6771
    case (LAYER):
13,235✔
6772
      if (msGrowMapLayers(map) == NULL)
13,235✔
6773
        return MS_FAILURE;
6774
      if (initLayer((GET_LAYER(map, map->numlayers)), map) == -1)
13,235✔
6775
        return MS_FAILURE;
6776
      if (loadLayer((GET_LAYER(map, map->numlayers)), map) == -1)
13,235✔
6777
        return MS_FAILURE;
6778
      GET_LAYER(map, map->numlayers)->index =
13,235✔
6779
          map->numlayers; /* save the index */
6780
      /* Update the layer order list with the layer's index. */
6781
      map->layerorder[map->numlayers] = map->numlayers;
13,235✔
6782
      map->numlayers++;
13,235✔
6783
      break;
13,235✔
6784
    case (OUTPUTFORMAT):
1,385✔
6785
      if (loadOutputFormat(map) == -1)
1,385✔
6786
        return MS_FAILURE;
6787
      break;
6788
    case (LEGEND):
480✔
6789
      if (loadLegend(&(map->legend), map) == -1)
480✔
6790
        return MS_FAILURE;
6791
      break;
6792
    case (MAP):
6793
      foundMapToken = MS_TRUE;
6794
      break;
6795
    case (MAXSIZE):
269✔
6796
      if (getInteger(&(map->maxsize), MS_NUM_CHECK_GT, 0, -1) == -1)
269✔
6797
        return MS_FAILURE;
6798
      break;
6799
    case (NAME):
2,702✔
6800
      free(map->name);
2,702✔
6801
      map->name = NULL; /* erase default */
2,702✔
6802
      if (getString(&map->name) == MS_FAILURE)
2,702✔
6803
        return MS_FAILURE;
6804
      break;
6805
    case (PROJECTION):
2,208✔
6806
      if (loadProjection(&map->projection, map) == -1)
2,208✔
6807
        return MS_FAILURE;
6808
      break;
6809
    case (QUERYMAP):
165✔
6810
      if (loadQueryMap(&(map->querymap), map) == -1)
165✔
6811
        return MS_FAILURE;
6812
      break;
6813
    case (REFERENCE):
×
6814
      if (loadReferenceMap(&(map->reference), map) == -1)
×
6815
        return MS_FAILURE;
6816
      break;
6817
    case (RESOLUTION):
25✔
6818
      if (getDouble(&(map->resolution), MS_NUM_CHECK_RANGE, MS_RESOLUTION_MIN,
25✔
6819
                    MS_RESOLUTION_MAX) == -1)
6820
        return MS_FAILURE;
6821
      break;
6822
    case (DEFRESOLUTION):
15✔
6823
      if (getDouble(&(map->defresolution), MS_NUM_CHECK_RANGE,
15✔
6824
                    MS_RESOLUTION_MIN, MS_RESOLUTION_MAX) == -1)
6825
        return MS_FAILURE;
6826
      break;
6827
    case (SCALE):
×
6828
    case (SCALEDENOM):
6829
      if (getDouble(&(map->scaledenom), MS_NUM_CHECK_GTE, 1, -1) == -1)
×
6830
        return MS_FAILURE;
6831
      break;
6832
    case (SCALEBAR):
414✔
6833
      if (loadScalebar(&(map->scalebar)) == -1)
414✔
6834
        return MS_FAILURE;
6835
      break;
6836
    case (SHAPEPATH):
1,812✔
6837
      if (getString(&map->shapepath) == MS_FAILURE)
1,812✔
6838
        return MS_FAILURE;
6839
      break;
6840
    case (SIZE):
3,011✔
6841
      if (getInteger(&(map->width), MS_NUM_CHECK_RANGE, 1, map->maxsize) == -1)
3,011✔
6842
        return MS_FAILURE;
6843
      if (getInteger(&(map->height), MS_NUM_CHECK_RANGE, 1, map->maxsize) == -1)
3,011✔
6844
        return MS_FAILURE;
6845
      break;
6846
    case (STATUS):
2,145✔
6847
      if ((map->status = getSymbol(2, MS_ON, MS_OFF)) == -1)
2,145✔
6848
        return MS_FAILURE;
6849
      break;
6850
    case (SYMBOL):
583✔
6851
      if (msGrowSymbolSet(&(map->symbolset)) == NULL)
583✔
6852
        return MS_FAILURE;
6853
      if ((loadSymbol(map->symbolset.symbol[map->symbolset.numsymbols],
583✔
6854
                      map->mappath) == -1)) {
6855
        msFreeSymbol(map->symbolset.symbol[map->symbolset.numsymbols]);
×
6856
        free(map->symbolset.symbol[map->symbolset.numsymbols]);
×
6857
        map->symbolset.symbol[map->symbolset.numsymbols] = NULL;
×
6858
        return MS_FAILURE;
×
6859
      }
6860
      map->symbolset.symbol[map->symbolset.numsymbols]->inmapfile = MS_TRUE;
583✔
6861
      map->symbolset.numsymbols++;
583✔
6862
      break;
583✔
6863
    case (SYMBOLSET):
1,624✔
6864
      if (getString(&map->symbolset.filename) == MS_FAILURE)
1,624✔
6865
        return MS_FAILURE;
6866
      break;
6867
    case (UNITS):
1,518✔
6868
      if ((int)(map->units =
1,518✔
6869
                    getSymbol(7, MS_INCHES, MS_FEET, MS_MILES, MS_METERS,
1,518✔
6870
                              MS_KILOMETERS, MS_NAUTICALMILES, MS_DD)) == -1)
6871
        return MS_FAILURE;
6872
      break;
6873
    case (WEB):
2,470✔
6874
      if (loadWeb(&(map->web), map) == -1)
2,470✔
6875
        return MS_FAILURE;
6876
      break;
6877
    default:
×
6878
      msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
6879
                 "msLoadMap()", msyystring_buffer, msyylineno);
6880
      return MS_FAILURE;
×
6881
    }
6882
  } /* next token */
6883
}
6884

6885
static bool msGetCWD(char *szBuffer, size_t nBufferSize,
3,145✔
6886
                     const char *pszFunctionName) {
6887
  if (NULL == getcwd(szBuffer, nBufferSize)) {
3,145✔
6888
#ifndef _WIN32
6889
    if (errno == EACCES)
×
6890
      msSetError(MS_MISCERR,
×
6891
                 "getcwd() failed with EACCES: you may need to force the "
6892
                 "current directory in the mapserver launcher "
6893
                 "(e.g -d option of spawn-fcgi)",
6894
                 pszFunctionName);
6895
    else if (errno == ENAMETOOLONG)
×
6896
      msSetError(MS_MISCERR, "getcwd() returned a too long path",
×
6897
                 pszFunctionName);
6898
    else
6899
      msSetError(MS_MISCERR, "getcwd() failed with errno code %d",
×
6900
                 pszFunctionName, errno);
6901
#else
6902
    msSetError(MS_MISCERR, "getcwd() returned a too long path",
6903
               pszFunctionName);
6904
#endif
6905
    return FALSE;
×
6906
  }
6907
  return TRUE;
6908
}
6909

6910
/*
6911
 * Apply any SLD styles referenced in a LAYER's STYLEITEM
6912
 */
6913
static void applyStyleItemToLayer(mapObj *map) {
3,141✔
6914

6915
  // applying SLD can create cloned layers so store the original layer count
6916
  int layerCount = map->numlayers;
3,141✔
6917

6918
  for (int i = 0; i < layerCount; i++) {
16,376✔
6919
    layerObj *layer = GET_LAYER(map, i);
13,235✔
6920

6921
    if (layer->styleitem && STARTS_WITH_CI(layer->styleitem, "sld://")) {
13,235✔
6922
      const char *filename = layer->styleitem + strlen("sld://");
123✔
6923

6924
      if (*filename == '\0') {
123✔
6925
        msSetErrorWithStatus(MS_IOERR, MS_HTTP_500_INTERNAL_SERVER_ERROR,
×
6926
                             "Empty SLD filename: \"%s\".",
6927
                             "applyLayerDefaultSubstitutions()",
6928
                             layer->styleitem);
6929
      } else {
6930
        msSLDApplyFromFile(map, layer, filename);
123✔
6931
      }
6932
    }
6933
  }
6934
}
3,141✔
6935

6936
/*
6937
** Sets up string-based mapfile loading and calls loadMapInternal to do the
6938
*work.
6939
*/
6940
mapObj *msLoadMapFromString(char *buffer, char *new_mappath,
3✔
6941
                            const configObj *config) {
6942
  mapObj *map;
6943
  struct mstimeval starttime = {0}, endtime = {0};
3✔
6944
  char szPath[MS_MAXPATHLEN], szCWDPath[MS_MAXPATHLEN];
6945
  char *mappath = NULL;
6946
  int debuglevel;
6947

6948
  debuglevel = (int)msGetGlobalDebugLevel();
3✔
6949

6950
  if (debuglevel >= MS_DEBUGLEVEL_TUNING) {
3✔
6951
    /* In debug mode, track time spent loading/parsing mapfile. */
6952
    msGettimeofday(&starttime, NULL);
×
6953
  }
6954

6955
  if (!buffer) {
3✔
6956
    msSetError(MS_MISCERR, "No buffer to load.", "msLoadMapFromString()");
×
6957
    return (NULL);
×
6958
  }
6959

6960
  /*
6961
  ** Allocate mapObj structure
6962
  */
6963
  map = (mapObj *)calloc(1, sizeof(mapObj));
3✔
6964
  MS_CHECK_ALLOC(map, sizeof(mapObj), NULL);
3✔
6965

6966
  if (initMap(map) == -1) { /* initialize this map */
3✔
6967
    msFreeMap(map);
×
6968
    return (NULL);
×
6969
  }
6970

6971
  map->config = config; // create a read-only reference
3✔
6972

6973
  msAcquireLock(TLOCK_PARSER); /* might need to move this lock a bit higher, yup
3✔
6974
                                  (bug 2108) */
6975

6976
  msyystate = MS_TOKENIZE_STRING;
3✔
6977
  msyystring = buffer;
3✔
6978
  msyylex(); /* sets things up, but doesn't process any tokens */
3✔
6979

6980
  msyylineno = 1; /* start at line 1 (do lines mean anything here?) */
3✔
6981

6982
  /* If new_mappath is provided then use it, otherwise use the CWD */
6983
  if (!msGetCWD(szCWDPath, MS_MAXPATHLEN, "msLoadMapFromString()")) {
3✔
6984
    msFreeMap(map);
×
6985
    msReleaseLock(TLOCK_PARSER);
×
6986
    return (NULL);
×
6987
  }
6988
  if (new_mappath) {
3✔
6989
    mappath = msStrdup(new_mappath);
×
6990
    map->mappath = msStrdup(msBuildPath(szPath, szCWDPath, mappath));
×
6991
  } else
6992
    map->mappath = msStrdup(szCWDPath);
3✔
6993

6994
  msyybasepath = map->mappath; /* for INCLUDEs */
3✔
6995

6996
  if (loadMapInternal(map) != MS_SUCCESS) {
3✔
6997
    msFreeMap(map);
×
6998
    msReleaseLock(TLOCK_PARSER);
×
6999
    if (mappath != NULL)
×
7000
      free(mappath);
×
7001
    return NULL;
×
7002
  }
7003

7004
  if (mappath != NULL)
3✔
7005
    free(mappath);
×
7006
  msyylex_destroy();
3✔
7007

7008
  msReleaseLock(TLOCK_PARSER);
3✔
7009

7010
  applyStyleItemToLayer(map);
3✔
7011

7012
  if (debuglevel >= MS_DEBUGLEVEL_TUNING) {
3✔
7013
    /* In debug mode, report time spent loading/parsing mapfile. */
7014
    msGettimeofday(&endtime, NULL);
×
7015
    msDebug("msLoadMapFromString(): %.3fs\n",
×
7016
            (endtime.tv_sec + endtime.tv_usec / 1.0e6) -
×
7017
                (starttime.tv_sec + starttime.tv_usec / 1.0e6));
×
7018
  }
7019

7020
  if (resolveSymbolNames(map) == MS_FAILURE)
3✔
7021
    return NULL;
7022

7023
  return map;
7024
}
7025

7026
/*
7027
** Sets up file-based mapfile loading and calls loadMapInternal to do the work.
7028
*/
7029
mapObj *msLoadMap(const char *filename, const char *new_mappath,
3,150✔
7030
                  const configObj *config) {
7031
  mapObj *map;
7032
  struct mstimeval starttime = {0}, endtime = {0};
3,150✔
7033
  char szPath[MS_MAXPATHLEN], szCWDPath[MS_MAXPATHLEN];
7034
  int debuglevel;
7035

7036
  debuglevel = (int)msGetGlobalDebugLevel();
3,150✔
7037

7038
  if (debuglevel >= MS_DEBUGLEVEL_TUNING) {
3,150✔
7039
    /* In debug mode, track time spent loading/parsing mapfile. */
7040
    msGettimeofday(&starttime, NULL);
×
7041
  }
7042

7043
  if (!filename) {
3,150✔
7044
    msSetError(MS_MISCERR, "Filename is undefined.", "msLoadMap()");
×
7045
    return (NULL);
×
7046
  }
7047

7048
  const char *ms_mapfile_pattern =
7049
      CPLGetConfigOption("MS_MAPFILE_PATTERN", MS_DEFAULT_MAPFILE_PATTERN);
3,150✔
7050
  if (msEvalRegex(ms_mapfile_pattern, filename) != MS_TRUE) {
3,150✔
7051
    msSetError(MS_REGEXERR, "Filename validation failed.", "msLoadMap()");
×
7052
    return (NULL);
×
7053
  }
7054

7055
  /*
7056
  ** Allocate mapObj structure
7057
  */
7058
  map = (mapObj *)calloc(1, sizeof(mapObj));
3,150✔
7059
  MS_CHECK_ALLOC(map, sizeof(mapObj), NULL);
3,150✔
7060

7061
  if (initMap(map) == -1) { /* initialize this map */
3,150✔
7062
    msFreeMap(map);
×
7063
    return (NULL);
×
7064
  }
7065

7066
  map->config = config; // create a read-only reference
3,150✔
7067

7068
  msAcquireLock(TLOCK_PARSER); /* Steve: might need to move this lock a bit
3,150✔
7069
                                  higher; Umberto: done */
7070

7071
#ifdef USE_XMLMAPFILE
7072
  /* If the mapfile is an xml mapfile, transform it */
7073
  const char *ms_xmlmapfile_xslt =
7074
      CPLGetConfigOption("MS_XMLMAPFILE_XSLT", NULL);
7075
  if (ms_xmlmapfile_xslt &&
7076
      (msEvalRegex(MS_DEFAULT_XMLMAPFILE_PATTERN, filename) == MS_TRUE)) {
7077

7078
    msyyin = tmpfile();
7079
    if (msyyin == NULL) {
7080
      msSetErrorWithStatus(MS_IOERR, MS_HTTP_500_INTERNAL_SERVER_ERROR,
7081
                           "tmpfile() failed to create temporary file",
7082
                           "msLoadMap()");
7083
      msReleaseLock(TLOCK_PARSER);
7084
      msFreeMap(map);
7085
      return NULL;
7086
    }
7087

7088
    if (msTransformXmlMapfile(ms_xmlmapfile_xslt, filename, msyyin) !=
7089
        MS_SUCCESS) {
7090
      fclose(msyyin);
7091
      msFreeMap(map);
7092
      return NULL;
7093
    }
7094
    fseek(msyyin, 0, SEEK_SET);
7095
  } else {
7096
#endif
7097
    if ((msyyin = fopen(filename, "r")) == NULL) {
3,150✔
7098
      msSetErrorWithStatus(MS_IOERR, MS_HTTP_500_INTERNAL_SERVER_ERROR, "(%s)",
8✔
7099
                           "msLoadMap()", filename);
7100
      msReleaseLock(TLOCK_PARSER);
8✔
7101
      msFreeMap(map);
8✔
7102
      return NULL;
8✔
7103
    }
7104
#ifdef USE_XMLMAPFILE
7105
  }
7106
#endif
7107

7108
  msyystate = MS_TOKENIZE_FILE;
3,142✔
7109
  msyylex(); /* sets things up, but doesn't process any tokens */
3,142✔
7110

7111
  msyyrestart(msyyin); /* start at line beginning, line 1 */
3,142✔
7112
  msyylineno = 1;
3,142✔
7113

7114
  /* If new_mappath is provided then use it, otherwise use the location */
7115
  /* of the mapfile as the default path */
7116
  if (!msGetCWD(szCWDPath, MS_MAXPATHLEN, "msLoadMap()")) {
3,142✔
7117
    msReleaseLock(TLOCK_PARSER);
×
7118
    msFreeMap(map);
×
7119
    return NULL;
×
7120
  }
7121

7122
  if (new_mappath)
3,142✔
7123
    map->mappath = msStrdup(msBuildPath(szPath, szCWDPath, new_mappath));
×
7124
  else {
7125
    char *path = msGetPath(filename);
3,142✔
7126
    map->mappath = msStrdup(msBuildPath(szPath, szCWDPath, path));
3,142✔
7127
    free(path);
3,142✔
7128
  }
7129

7130
  msyybasepath = map->mappath; /* for INCLUDEs */
3,142✔
7131

7132
  if (loadMapInternal(map) != MS_SUCCESS) {
3,142✔
7133
    msFreeMap(map);
4✔
7134
    if (msyyin) {
4✔
7135
      msyycleanup_includes();
4✔
7136
      fclose(msyyin);
4✔
7137
      msyyin = NULL;
4✔
7138
    }
7139
    msReleaseLock(TLOCK_PARSER);
4✔
7140
    return NULL;
4✔
7141
  }
7142
  msReleaseLock(TLOCK_PARSER);
3,138✔
7143

7144
  applyStyleItemToLayer(map);
3,138✔
7145

7146
  if (debuglevel >= MS_DEBUGLEVEL_TUNING) {
3,138✔
7147
    /* In debug mode, report time spent loading/parsing mapfile. */
7148
    msGettimeofday(&endtime, NULL);
×
7149
    msDebug("msLoadMap(): %.3fs\n",
×
7150
            (endtime.tv_sec + endtime.tv_usec / 1.0e6) -
×
7151
                (starttime.tv_sec + starttime.tv_usec / 1.0e6));
×
7152
  }
7153

7154
  return map;
7155
}
7156

7157
static void hashTableSubstituteString(hashTableObj *hash, const char *from,
473✔
7158
                                      const char *to) {
7159
  const char *key, *val;
7160
  char *new_val;
7161
  key = msFirstKeyFromHashTable(hash);
473✔
7162
  while (key != NULL) {
1,466✔
7163
    val = msLookupHashTable(hash, key);
993✔
7164
    if (strcasestr(val, from)) {
993✔
7165
      new_val = msCaseReplaceSubstring(msStrdup(val), from, to);
70✔
7166
      msInsertHashTable(hash, key, new_val);
70✔
7167
      msFree(new_val);
70✔
7168
    }
7169
    key = msNextKeyFromHashTable(hash, key);
993✔
7170
  }
7171
}
473✔
7172

7173
static void classSubstituteString(classObj *class, const char *from,
164✔
7174
                                  const char *to) {
7175
  if (class->expression.string)
164✔
7176
    class->expression.string =
16✔
7177
        msCaseReplaceSubstring(class->expression.string, from, to);
16✔
7178
  if (class->text.string)
164✔
7179
    class->text.string = msCaseReplaceSubstring(class->text.string, from, to);
×
7180
  if (class->title)
164✔
7181
    class->title = msCaseReplaceSubstring(class->title, from, to);
×
7182
}
164✔
7183

7184
static void layerSubstituteString(layerObj *layer, const char *from,
214✔
7185
                                  const char *to) {
7186
  int c;
7187
  if (layer->data)
214✔
7188
    layer->data = msCaseReplaceSubstring(layer->data, from, to);
212✔
7189
  if (layer->tileindex)
214✔
7190
    layer->tileindex = msCaseReplaceSubstring(layer->tileindex, from, to);
×
7191
  if (layer->connection)
214✔
7192
    layer->connection = msCaseReplaceSubstring(layer->connection, from, to);
10✔
7193
  if (layer->filter.string)
214✔
7194
    layer->filter.string =
2✔
7195
        msCaseReplaceSubstring(layer->filter.string, from, to);
2✔
7196
  if (layer->mask)
214✔
7197
    layer->mask = msCaseReplaceSubstring(layer->mask, from, to); // new for 8.0
×
7198

7199
  /* The bindvalues are most useful when able to substitute values from the URL
7200
   */
7201
  hashTableSubstituteString(&layer->bindvals, from, to);
214✔
7202
  hashTableSubstituteString(&layer->metadata, from, to);
214✔
7203
  msLayerSubstituteProcessing(layer, from, to);
214✔
7204
  for (c = 0; c < layer->numclasses; c++) {
334✔
7205
    classSubstituteString(layer->class[c], from, to);
120✔
7206
  }
7207
}
214✔
7208

7209
static void mapSubstituteString(mapObj *map, const char *from, const char *to) {
19✔
7210
  int l;
7211
  for (l = 0; l < map->numlayers; l++) {
83✔
7212
    layerSubstituteString(GET_LAYER(map, l), from, to);
64✔
7213
  }
7214

7215
  /* output formats (#3751) */
7216
  for (l = 0; l < map->numoutputformats; l++) {
39✔
7217
    int o;
7218
    for (o = 0; o < map->outputformatlist[l]->numformatoptions; o++) {
26✔
7219
      map->outputformatlist[l]->formatoptions[o] = msCaseReplaceSubstring(
6✔
7220
          map->outputformatlist[l]->formatoptions[o], from, to);
6✔
7221
    }
7222
  }
7223

7224
  hashTableSubstituteString(&map->web.metadata, from, to);
19✔
7225
  if (map->web.template)
19✔
7226
    map->web.template = msCaseReplaceSubstring(map->web.template, from, to);
×
7227
}
19✔
7228

7229
static void applyOutputFormatDefaultSubstitutions(outputFormatObj *format,
7,240✔
7230
                                                  const char *option,
7231
                                                  hashTableObj *table) {
7232
  const char *filename;
7233

7234
  filename = msGetOutputFormatOption(format, option, NULL);
7,240✔
7235
  if (filename && strlen(filename) > 0) {
7,240✔
7236
    char *tmpfilename = msStrdup(filename);
535✔
7237
    const char *default_key = msFirstKeyFromHashTable(table);
535✔
7238
    while (default_key) {
547✔
7239
      if (!strncasecmp(default_key, "default_", 8)) {
12✔
7240
        char *new_filename = NULL;
7241
        size_t buffer_size = (strlen(default_key) - 5);
6✔
7242
        char *tag = (char *)msSmallMalloc(buffer_size);
6✔
7243
        snprintf(tag, buffer_size, "%%%s%%", &(default_key[8]));
6✔
7244

7245
        new_filename = msStrdup(tmpfilename);
6✔
7246
        new_filename = msCaseReplaceSubstring(
6✔
7247
            new_filename, tag, msLookupHashTable(table, default_key));
7248
        free(tag);
6✔
7249

7250
        msSetOutputFormatOption(format, option, new_filename);
6✔
7251
        free(new_filename);
6✔
7252
      }
7253
      default_key = msNextKeyFromHashTable(table, default_key);
12✔
7254
    }
7255
    msFree(tmpfilename);
535✔
7256
  }
7257
  return;
7,240✔
7258
}
7259

7260
static void applyClassDefaultSubstitutions(classObj *class,
11,714✔
7261
                                           hashTableObj *table) {
7262
  const char *default_key = msFirstKeyFromHashTable(table);
11,714✔
7263
  while (default_key) {
11,727✔
7264
    if (!strncasecmp(default_key, "default_", 8)) {
13✔
7265
      size_t buffer_size = (strlen(default_key) - 5);
×
7266
      char *tag = (char *)msSmallMalloc(buffer_size);
×
7267
      snprintf(tag, buffer_size, "%%%s%%", &(default_key[8]));
×
7268

7269
      classSubstituteString(class, tag, msLookupHashTable(table, default_key));
×
7270
      free(tag);
×
7271
    }
7272
    default_key = msNextKeyFromHashTable(table, default_key);
13✔
7273
  }
7274
  return;
11,714✔
7275
}
7276

7277
static void applyLayerDefaultSubstitutions(layerObj *layer,
20,098✔
7278
                                           hashTableObj *table) {
7279
  int i;
7280
  const char *default_key = msFirstKeyFromHashTable(table);
20,098✔
7281
  while (default_key) {
21,094✔
7282
    if (!strncasecmp(default_key, "default_", 8)) {
996✔
7283
      size_t buffer_size = (strlen(default_key) - 5);
108✔
7284
      const char *to = msLookupHashTable(table, default_key);
108✔
7285
      char *tag = (char *)msSmallMalloc(buffer_size);
108✔
7286
      snprintf(tag, buffer_size, "%%%s%%", &(default_key[8]));
108✔
7287

7288
      for (i = 0; i < layer->numclasses; i++) {
150✔
7289
        classSubstituteString(layer->class[i], tag, to);
42✔
7290
      }
7291
      layerSubstituteString(layer, tag, to);
108✔
7292
      free(tag);
108✔
7293
    }
7294
    default_key = msNextKeyFromHashTable(table, default_key);
996✔
7295
  }
7296

7297
  return;
20,098✔
7298
}
7299

7300
static void applyHashTableDefaultSubstitutions(hashTableObj *hashTab,
2,629✔
7301
                                               hashTableObj *table) {
7302
  const char *default_key = msFirstKeyFromHashTable(table);
2,629✔
7303
  while (default_key) {
2,768✔
7304
    if (!strncasecmp(default_key, "default_", 8)) {
139✔
7305
      size_t buffer_size = (strlen(default_key) - 5);
26✔
7306
      const char *to = msLookupHashTable(table, default_key);
26✔
7307
      char *tag = (char *)msSmallMalloc(buffer_size);
26✔
7308
      snprintf(tag, buffer_size, "%%%s%%", &(default_key[8]));
26✔
7309

7310
      hashTableSubstituteString(hashTab, tag, to);
26✔
7311
      free(tag);
26✔
7312
    }
7313
    default_key = msNextKeyFromHashTable(table, default_key);
139✔
7314
  }
7315
  return;
2,629✔
7316
}
7317

7318
/*
7319
** Loop through layer metadata for keys that have a default_%key% pattern to
7320
*replace
7321
** remaining %key% entries by their default value.
7322
*/
7323
void msApplyDefaultSubstitutions(mapObj *map) {
2,629✔
7324
  int i, j;
7325

7326
  /* output formats (#3751) */
7327
  for (i = 0; i < map->numoutputformats; i++) {
6,249✔
7328
    applyOutputFormatDefaultSubstitutions(map->outputformatlist[i], "filename",
3,620✔
7329
                                          &(map->web.validation));
7330
    applyOutputFormatDefaultSubstitutions(map->outputformatlist[i], "JSONP",
3,620✔
7331
                                          &(map->web.validation));
7332
  }
7333

7334
  for (i = 0; i < map->numlayers; i++) {
12,678✔
7335
    layerObj *layer = GET_LAYER(map, i);
10,049✔
7336

7337
    for (j = 0; j < layer->numclasses;
21,763✔
7338
         j++) { /* class settings take precedence...  */
11,714✔
7339
      classObj *class = GET_CLASS(map, i, j);
11,714✔
7340
      applyClassDefaultSubstitutions(class, &(class->validation));
11,714✔
7341
    }
7342

7343
    applyLayerDefaultSubstitutions(
10,049✔
7344
        layer, &(layer->validation)); /* ...then layer settings... */
7345
    applyLayerDefaultSubstitutions(
10,049✔
7346
        layer, &(map->web.validation)); /* ...and finally web settings */
7347
  }
7348
  applyHashTableDefaultSubstitutions(&map->web.metadata,
2,629✔
7349
                                     &(map->web.validation));
7350
}
2,629✔
7351

7352
char *_get_param_value(const char *key, char **names, char **values,
529✔
7353
                       int npairs) {
7354
  if (npairs <= 0)
529✔
7355
    return NULL; // bail, no point searching
7356

7357
  if (getenv(key)) { /* environment override */
523✔
7358
    return getenv(key);
1✔
7359
  }
7360
  while (npairs) {
2,578✔
7361
    npairs--;
2,127✔
7362
    if (strcasecmp(key, names[npairs]) == 0) {
2,127✔
7363
      return values[npairs];
71✔
7364
    }
7365
  }
7366
  return NULL;
7367
}
7368

7369
void msApplySubstitutions(mapObj *map, char **names, char **values,
2,028✔
7370
                          int npairs) {
7371
  int l;
7372
  const char *key, *value, *validation;
7373
  char *tag;
7374
  for (l = 0; l < map->numlayers; l++) {
10,388✔
7375
    int c;
7376
    layerObj *lp = GET_LAYER(map, l);
8,360✔
7377
    for (c = 0; c < lp->numclasses; c++) {
18,349✔
7378
      classObj *cp = lp->class[c];
9,989✔
7379
      key = NULL;
7380
      while ((key = msNextKeyFromHashTable(&cp->validation, key))) {
10,002✔
7381
        value = _get_param_value(key, names, values, npairs);
13✔
7382
        if (!value)
13✔
7383
          continue; /*parameter was not in url*/
11✔
7384
        validation = msLookupHashTable(&cp->validation, key);
2✔
7385
        if (msEvalRegex(validation, value)) {
2✔
7386
          /* we've found a substitution and it validates correctly, now let's
7387
           * apply it */
7388
          tag = msSmallMalloc(strlen(key) + 3);
2✔
7389
          sprintf(tag, "%%%s%%", key);
7390
          classSubstituteString(cp, tag, value);
2✔
7391
          free(tag);
2✔
7392
        } else {
7393
          msSetError(MS_REGEXERR, "Parameter pattern validation failed.",
×
7394
                     "msApplySubstitutions()");
7395
          if (map->debug || lp->debug) {
×
7396
            msDebug("layer (%s), class %d: failed to validate (%s=%s) for "
×
7397
                    "regex (%s)\n",
7398
                    lp->name, c, key, value, validation);
7399
          }
7400
        }
7401
      }
7402
    }
7403
    key = NULL;
7404
    while ((key = msNextKeyFromHashTable(&lp->validation, key))) {
8,737✔
7405
      value = _get_param_value(key, names, values, npairs);
377✔
7406
      if (!value)
377✔
7407
        continue; /*parameter was not in url*/
333✔
7408
      validation = msLookupHashTable(&lp->validation, key);
44✔
7409
      if (msEvalRegex(validation, value)) {
44✔
7410
        /* we've found a substitution and it validates correctly, now let's
7411
         * apply it */
7412
        tag = msSmallMalloc(strlen(key) + 3);
42✔
7413
        sprintf(tag, "%%%s%%", key);
7414
        layerSubstituteString(lp, tag, value);
42✔
7415
        free(tag);
42✔
7416
      } else {
7417
        msSetError(MS_REGEXERR, "Parameter pattern validation failed.",
2✔
7418
                   "msApplySubstitutions()");
7419
        if (map->debug || lp->debug) {
2✔
7420
          msDebug("layer (%s): failed to validate (%s=%s) for regex (%s)\n",
×
7421
                  lp->name, key, value, validation);
7422
        }
7423
      }
7424
    }
7425
  }
7426
  key = NULL;
7427
  while ((key = msNextKeyFromHashTable(&map->web.validation, key))) {
2,167✔
7428
    value = _get_param_value(key, names, values, npairs);
139✔
7429
    if (!value)
139✔
7430
      continue; /*parameter was not in url*/
113✔
7431
    validation = msLookupHashTable(&map->web.validation, key);
26✔
7432
    if (msEvalRegex(validation, value)) {
26✔
7433
      /* we've found a substitution and it validates correctly, now let's apply
7434
       * it */
7435
      tag = msSmallMalloc(strlen(key) + 3);
19✔
7436
      sprintf(tag, "%%%s%%", key);
7437
      mapSubstituteString(map, tag, value);
19✔
7438
      free(tag);
19✔
7439
    } else {
7440
      msSetError(MS_REGEXERR, "Parameter pattern validation failed.",
7✔
7441
                 "msApplySubstitutions()");
7442
      if (map->debug) {
7✔
7443
        msDebug("failed to validate (%s=%s) for regex (%s)\n", key, value,
×
7444
                validation);
7445
      }
7446
    }
7447
  }
7448
}
2,028✔
7449

7450
/*
7451
** Returns an array with one entry per mapfile token.  Useful to manipulate
7452
** mapfiles in MapScript.
7453
**
7454
** The returned array should be freed using msFreeCharArray().
7455
*/
7456
static char **tokenizeMapInternal(char *filename, int *ret_numtokens) {
×
7457
  char **tokens = NULL;
7458
  int numtokens = 0, numtokens_allocated = 0;
7459
  size_t buffer_size = 0;
7460

7461
  *ret_numtokens = 0;
×
7462

7463
  if (!filename) {
×
7464
    msSetError(MS_MISCERR, "Filename is undefined.", "msTokenizeMap()");
×
7465
    return NULL;
×
7466
  }
7467

7468
  /*
7469
  ** Check map filename to make sure it's legal
7470
  */
7471
  const char *ms_mapfile_pattern =
7472
      CPLGetConfigOption("MS_MAPFILE_PATTERN", MS_DEFAULT_MAPFILE_PATTERN);
×
7473
  if (msEvalRegex(ms_mapfile_pattern, filename) != MS_TRUE) {
×
7474
    msSetError(MS_REGEXERR, "Filename validation failed.", "msTokenizeMap()");
×
7475
    return (NULL);
×
7476
  }
7477

7478
  if ((msyyin = fopen(filename, "r")) == NULL) {
×
7479
    msSetErrorWithStatus(MS_IOERR, MS_HTTP_500_INTERNAL_SERVER_ERROR, "(%s)",
×
7480
                         "msTokenizeMap()", filename);
7481
    return NULL;
×
7482
  }
7483

7484
  msyystate = MS_TOKENIZE_FILE; /* restore lexer state to INITIAL, and do return
×
7485
                                   comments */
7486
  msyylex();
×
7487
  msyyreturncomments = 1; /* want all tokens, including comments */
×
7488

7489
  msyyrestart(msyyin); /* start at line beginning, line 1 */
×
7490
  msyylineno = 1;
×
7491

7492
  numtokens = 0;
7493
  numtokens_allocated = 256;
7494
  tokens = (char **)malloc(numtokens_allocated * sizeof(char *));
×
7495
  if (tokens == NULL) {
×
7496
    msSetError(MS_MEMERR, NULL, "msTokenizeMap()");
×
7497
    fclose(msyyin);
×
7498
    return NULL;
×
7499
  }
7500

7501
  for (;;) {
7502

7503
    if (numtokens_allocated <= numtokens) {
×
7504
      numtokens_allocated *=
×
7505
          2; /* double size of the array every time we reach the limit */
7506
      char **tokensNew =
7507
          (char **)realloc(tokens, numtokens_allocated * sizeof(char *));
×
7508
      if (tokensNew == NULL) {
×
7509
        msSetError(MS_MEMERR, "Realloc() error.", "msTokenizeMap()");
×
7510
        fclose(msyyin);
×
7511
        for (int i = 0; i < numtokens; i++)
×
7512
          msFree(tokens[i]);
×
7513
        msFree(tokens);
×
7514
        return NULL;
×
7515
      }
7516
      tokens = tokensNew;
7517
    }
7518

7519
    switch (msyylex()) {
×
7520
    case (EOF): /* This is the normal way out... cleanup and exit */
×
7521
      fclose(msyyin);
×
7522
      *ret_numtokens = numtokens;
×
7523
      return (tokens);
×
7524
      break;
7525
    case (MS_STRING):
×
7526
      buffer_size = strlen(msyystring_buffer) + 2 + 1;
×
7527
      tokens[numtokens] = (char *)msSmallMalloc(buffer_size);
×
7528
      snprintf(tokens[numtokens], buffer_size, "\"%s\"", msyystring_buffer);
×
7529
      break;
7530
    case (MS_EXPRESSION):
×
7531
      buffer_size = strlen(msyystring_buffer) + 2 + 1;
×
7532
      tokens[numtokens] = (char *)msSmallMalloc(buffer_size);
×
7533
      snprintf(tokens[numtokens], buffer_size, "(%s)", msyystring_buffer);
×
7534
      break;
7535
    case (MS_REGEX):
×
7536
      buffer_size = strlen(msyystring_buffer) + 2 + 1;
×
7537
      tokens[numtokens] = (char *)msSmallMalloc(buffer_size);
×
7538
      snprintf(tokens[numtokens], buffer_size, "/%s/", msyystring_buffer);
×
7539
      break;
7540
    default:
×
7541
      tokens[numtokens] = msStrdup(msyystring_buffer);
×
7542
      break;
×
7543
    }
7544

7545
    if (tokens[numtokens] == NULL) {
×
7546
      int i;
7547
      msSetError(MS_MEMERR, NULL, "msTokenizeMap()");
×
7548
      fclose(msyyin);
×
7549
      for (i = 0; i < numtokens; i++)
×
7550
        msFree(tokens[i]);
×
7551
      msFree(tokens);
×
7552
      return NULL;
×
7553
    }
7554

7555
    numtokens++;
×
7556
  }
7557

7558
  return NULL; /* should never get here */
7559
}
7560

7561
/*
7562
** Wraps tokenizeMapInternal
7563
*/
7564
char **msTokenizeMap(char *filename, int *numtokens) {
×
7565
  char **tokens;
7566

7567
  msAcquireLock(TLOCK_PARSER);
×
7568
  tokens = tokenizeMapInternal(filename, numtokens);
×
7569
  msReleaseLock(TLOCK_PARSER);
×
7570

7571
  return tokens;
×
7572
}
7573

7574
void msCloseConnections(mapObj *map) {
3,241✔
7575
  int i;
7576
  layerObj *lp;
7577

7578
  for (i = 0; i < map->numlayers; i++) {
16,850✔
7579
    lp = (GET_LAYER(map, i));
13,609✔
7580

7581
    /* If the vtable is null, then the layer is never accessed or used -> skip
7582
     * it
7583
     */
7584
    if (lp->vtable) {
13,609✔
7585
      lp->vtable->LayerCloseConnection(lp);
3,415✔
7586
    }
7587
  }
7588
}
3,241✔
7589

7590
void initResultCache(resultCacheObj *resultcache) {
1,901✔
7591
  if (resultcache) {
1,901✔
7592
    resultcache->results = NULL;
1,901✔
7593
    resultcache->numresults = 0;
1,901✔
7594
    resultcache->hasnext = MS_UNKNOWN;
1,901✔
7595
    resultcache->cachesize = 0;
1,901✔
7596
    resultcache->bounds.minx = resultcache->bounds.miny =
1,901✔
7597
        resultcache->bounds.maxx = resultcache->bounds.maxy = -1;
1,901✔
7598
    resultcache->previousBounds = resultcache->bounds;
1,901✔
7599
  }
7600
}
1,901✔
7601

7602
void cleanupResultCache(resultCacheObj *resultcache) {
982✔
7603
  if (resultcache) {
982✔
7604
    if (resultcache->results) {
982✔
7605
      int i;
7606
      for (i = 0; i < resultcache->numresults; i++) {
5,032✔
7607
        if (resultcache->results[i].shape) {
4,292✔
7608
          msFreeShape(resultcache->results[i].shape);
12✔
7609
          msFree(resultcache->results[i].shape);
12✔
7610
        }
7611
      }
7612
      free(resultcache->results);
740✔
7613
    }
7614
    resultcache->results = NULL;
982✔
7615
    initResultCache(resultcache);
982✔
7616
  }
7617
}
982✔
7618

7619
static int resolveSymbolNames(mapObj *map) {
3,144✔
7620
  int i, j;
7621

7622
  /* step through layers and classes to resolve symbol names */
7623
  for (i = 0; i < map->numlayers; i++) {
16,381✔
7624
    for (j = 0; j < GET_LAYER(map, i)->numclasses; j++) {
26,305✔
7625
      if (classResolveSymbolNames(GET_LAYER(map, i)->class[j]) != MS_SUCCESS)
13,068✔
7626
        return MS_FAILURE;
7627
    }
7628
  }
7629

7630
  return MS_SUCCESS;
7631
}
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