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

MapServer / MapServer / 25968262348

16 May 2026 05:24PM UTC coverage: 42.534% (+0.1%) from 42.439%
25968262348

Pull #7507

github

web-flow
Merge 4053aab2f into 9e1ae01f6
Pull Request #7507: Implementation for MS RFC 142 (scalebar geodesic measurement)

178 of 199 new or added lines in 4 files covered. (89.45%)

25425 existing lines in 4 files now uncovered.

64905 of 152594 relevant lines covered (42.53%)

27351.34 hits per line

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

69.56
/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) {
2,022✔
121
  ms_regex_t re;
122
  int errcode = ms_regcomp(&re, e, MS_REG_EXTENDED | MS_REG_NOSUB);
2,022✔
123
  if (errcode != 0) {
2,022✔
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);
2,022✔
131
  return MS_TRUE;
2,022✔
132
}
133

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

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

140
  int errcode = ms_regcomp(&re, e, MS_REG_EXTENDED | MS_REG_NOSUB);
7,728✔
141
  if (errcode != 0) {
7,728✔
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,727✔
150
    ms_regfree(&re);
2,035✔
151
    return (MS_FALSE);
2,035✔
152
  }
153
  ms_regfree(&re);
5,692✔
154

155
  return (MS_TRUE);
5,692✔
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) {
166,512✔
190
  int i;
191
  if (!array)
166,512✔
192
    return;
193
  for (i = 0; i < num_items; i++)
737,013✔
194
    msFree(array[i]);
588,009✔
195
  msFree(array);
149,004✔
196
}
197

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

207
  symbol = msyylex();
122,790✔
208

209
  va_start(argp, n);
122,790✔
210
  while (i < n) { /* check each symbol in the list */
202,366✔
211
    if (symbol == va_arg(argp, int)) {
202,366✔
212
      va_end(argp);
122,790✔
213
      return (symbol);
122,790✔
214
    }
215
    i++;
79,576✔
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();
758✔
254
  return msStrdup(msyystring_buffer);
3,586✔
255
}
256

257
/*
258
** Load a string from the map file. A "string" is defined in lexer.l.
259
*/
260
int getString(char **s) {
189,908✔
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) {
189,908✔
265
    if (*s)
189,908✔
266
      free(*s); /* avoid leak */
3,165✔
267
    *s = msStrdup(msyystring_buffer);
189,908✔
268
    return (MS_SUCCESS);
189,908✔
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,
154,146✔
277
                  double value2) {
278
  if (num_check_type == MS_NUM_CHECK_NONE) {
154,146✔
279
    return MS_SUCCESS;
280
  } else if (num_check_type == MS_NUM_CHECK_RANGE && number >= value1 &&
78,484✔
281
             number <= value2) {
282
    return MS_SUCCESS;
283
  } else if (num_check_type == MS_NUM_CHECK_GT && number > value1) {
17,330✔
284
    return MS_SUCCESS;
285
  } else if (num_check_type == MS_NUM_CHECK_GTE && number >= value1) {
8,583✔
286
    return MS_SUCCESS;
8,583✔
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) {
76,009✔
296
  if (msyylex() == MS_NUMBER) {
76,009✔
297
    if (msCheckNumber(msyynumber, num_check_type, value1, value2) ==
76,009✔
298
        MS_SUCCESS) {
299
      *d = msyynumber;
76,009✔
300
      return (0);
76,009✔
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,569✔
313
  if (msyylex() == MS_NUMBER) {
61,569✔
314
    if (msCheckNumber(msyynumber, num_check_type, value1, value2) ==
61,569✔
315
        MS_SUCCESS) {
316
      *i = (int)msyynumber;
61,569✔
317
      return (0);
61,569✔
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,308✔
409
                     int try_addimage_if_notfound) {
410
  int i;
411

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

415
  /* symbol 0 has no name */
416
  for (i = 1; i < symbols->numsymbols; i++) {
10,483✔
417
    if (symbols->symbol[i]->name)
10,180✔
418
      if (strcasecmp(symbols->symbol[i]->name, name) == 0)
9,385✔
419
        return (i);
3,005✔
420
  }
421

422
  if (try_addimage_if_notfound)
303✔
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,147✔
433
  int i;
434

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

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

447
int loadColor(colorObj *color, attributeBindingObj *binding) {
24,621✔
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,621✔
458
    if ((symbol = getSymbol(3, MS_NUMBER, MS_BINDING, MS_STRING)) == -1)
20,541✔
459
      return MS_FAILURE;
460
  } else {
461
    if ((symbol = getSymbol(2, MS_NUMBER, MS_STRING)) == -1)
4,080✔
462
      return MS_FAILURE;
463
  }
464

465
  color->alpha = 255;
24,621✔
466
  if (symbol == MS_NUMBER) {
24,621✔
467
    if (msyynumber >= -255 && msyynumber <= 255) {
24,185✔
468
      color->red = (int)msyynumber;
24,185✔
469
    } else {
470
      return MS_FAILURE;
471
    }
472
    if (getInteger(&(color->green), MS_NUM_CHECK_RANGE, -255, 255) == -1)
24,185✔
473
      return MS_FAILURE;
474
    if (getInteger(&(color->blue), MS_NUM_CHECK_RANGE, -255, 255) == -1)
24,185✔
475
      return MS_FAILURE;
476
  } else if (symbol == MS_STRING) {
436✔
477
    int len = strlen(msyystring_buffer);
413✔
478
    if (msyystring_buffer[0] == '#' &&
413✔
479
        (len == 7 || len == 9)) { /* got a hex color w/optional alpha */
413✔
480
      hex[0] = msyystring_buffer[1];
413✔
481
      hex[1] = msyystring_buffer[2];
413✔
482
      color->red = msHexToInt(hex);
413✔
483
      hex[0] = msyystring_buffer[3];
413✔
484
      hex[1] = msyystring_buffer[4];
413✔
485
      color->green = msHexToInt(hex);
413✔
486
      hex[0] = msyystring_buffer[5];
413✔
487
      hex[1] = msyystring_buffer[6];
413✔
488
      color->blue = msHexToInt(hex);
413✔
489
      if (len == 9) {
413✔
490
        hex[0] = msyystring_buffer[7];
45✔
491
        hex[1] = msyystring_buffer[8];
45✔
492
        color->alpha = msHexToInt(hex);
45✔
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"); }
87✔
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++)
1,020✔
574
    msIO_fprintf(stream, "%s", str);
708✔
575
}
576

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

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

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

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

609
static void writeDimension(FILE *stream, int indent, const char *name, double x,
23✔
610
                           double y, char *bind_x, char *bind_y) {
611
  writeIndent(stream, ++indent);
612
  if (bind_x)
23✔
613
    msIO_fprintf(stream, "%s [%s] ", name, bind_x);
×
614
  else
615
    msIO_fprintf(stream, "%s %.15g ", name, x);
23✔
616
  if (bind_y)
23✔
617
    msIO_fprintf(stream, "[%s]\n", bind_y);
×
618
  else
619
    msIO_fprintf(stream, "%.15g\n", y);
23✔
620
}
23✔
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,
402✔
638
                        double defaultNumber, double number) {
639
  if (number == defaultNumber)
402✔
640
    return; /* don't output default */
641
  writeIndent(stream, ++indent);
642
  msIO_fprintf(stream, "%s %.15g\n", name, number);
29✔
643
}
644

645
static void writeCharacter(FILE *stream, int indent, const char *name,
8✔
646
                           const char defaultCharacter, char character) {
647
  if (defaultCharacter == character)
8✔
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,
257✔
684
                        const char *defaultString, const char *string) {
685
  if (!string)
257✔
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,
27✔
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);
27✔
713
  while (j < size) { /* check each value/keyword mapping in the list */
62✔
714
    i = va_arg(argp, int);
35✔
715
    s = va_arg(argp, const char *);
35✔
716
    if (value == i) {
35✔
717
      writeIndent(stream, ++indent);
718
      msIO_fprintf(stream, "%s %s\n", name, s);
×
719
      va_end(argp);
×
720
      return;
×
721
    }
722
    j++;
35✔
723
  }
724
  va_end(argp);
27✔
725

726
  writeNumber(stream, indent, name, defaultNumber, number);
27✔
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,
69✔
750
                       colorObj *defaultColor, colorObj *color) {
751
  if (!defaultColor && !MS_VALID_COLOR(*color))
69✔
752
    return;
753
  else if (defaultColor && MS_COMPARE_COLOR(*defaultColor, *color))
14✔
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,342✔
949
                                        shapeObj *shape) {
950
  featureListNodeObjPtr node;
951

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

954
  msInitShape(&(node->shape));
3,342✔
955
  if (msCopyShape(shape, &(node->shape)) == -1) {
3,342✔
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,342✔
966
  node->next = NULL;
3,342✔
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,342✔
971
    *list = node;
2,609✔
972
  } else {
973
    if ((*list)->tailifhead !=
733✔
974
        NULL) /* this should never be NULL, but just in case */
975
      (*list)->tailifhead->next =
733✔
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,342✔
983

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

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

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

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

1008
  while (ret < 0) {
2,966✔
1009
    switch (msyylex()) {
7,526✔
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,560✔
1018
      if (points->numpoints == buffer_size) { /* just add it to the end */
4,560✔
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,560✔
1035
      if (getDouble(&(points->point[points->numpoints].y), MS_NUM_CHECK_NONE,
4,560✔
1036
                    -1, -1) == -1) {
1037
        ret = MS_FAILURE;
1038
        break;
1039
      }
1040
      points->numpoints++;
4,560✔
1041
      break;
4,560✔
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,966✔
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,978✔
1059
  int status = MS_SUCCESS;
1060
  featureListNodeObjPtr *list = &(player->features);
2,978✔
1061
  multipointObj points = {0, NULL};
2,978✔
1062
  shapeObj *shape = NULL;
1063

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

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

1070
  for (;;) {
1071
    switch (msyylex()) {
7,092✔
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,978✔
1078
      if (player->features != NULL && player->features->tailifhead != NULL)
2,978✔
1079
        shape->index = player->features->tailifhead->shape.index + 1;
488✔
1080
      else
1081
        shape->index = 0;
2,490✔
1082
      if (insertFeatureList(list, shape) == NULL)
2,978✔
1083
        status = MS_FAILURE;
1084

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

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

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

1102
      if (status == MS_FAILURE) {
2,966✔
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) {
12,308✔
1282
  p->gt.need_geotransform = MS_FALSE;
12,308✔
1283

1284
  if (p->proj != NULL || p->numargs != 0) {
12,308✔
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()) {
24,951✔
1294
    case (EOF):
×
1295
      msSetError(MS_EOFERR, NULL, "loadProjection()");
×
1296
      return (-1);
×
1297
    case (END):
12,308✔
1298
      if (p->numargs == 1 && strstr(p->args[0], "+") != NULL) {
12,308✔
1299
        char *one_line_def = p->args[0];
1300
        int result;
1301

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

1325
          return msProcessProjection(p);
4,520✔
1326
        } else
1327
          return 0;
1328
      }
1329
      break;
1330
    case (MS_STRING):
12,643✔
1331
    case (MS_AUTO):
1332
      if (p->numargs == MS_MAXPROJARGS) {
12,643✔
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);
12,643✔
1340
      p->numargs++;
12,643✔
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,
6,421✔
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);
6,421✔
1364
  if (strncasecmp(value, pszPrefix, prefix_len) != 0)
6,421✔
1365
    return -1;
1366

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

1372
  buffer_size = 10 + strlen(code) + 1;
5,320✔
1373
  init_string = (char *)msSmallMalloc(buffer_size);
5,320✔
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,320✔
1379
  p->args[0] = init_string;
5,320✔
1380
  p->numargs = 1;
5,320✔
1381

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

1387
  return 0;
1388
}
1389

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

1394
static int msLoadProjectionStringCRSLike(projectionObj *p, const char *value,
442✔
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);
442✔
1402
  if (strncasecmp(value, pszPrefix, prefix_len) != 0)
442✔
1403
    return -1;
1404

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

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

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

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

1428
  return 0;
16✔
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,406✔
1439
  assert(p);
1440

1441
  msFreeProjectionExceptContext(p);
3,406✔
1442

1443
  p->gt.need_geotransform = MS_FALSE;
3,406✔
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,406✔
1455
    return msProcessProjection(p);
3,360✔
1456
  }
1457

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

1461
/* Extract AUTHORITY<sep>version<sep>CODE from a string, to
1462
** create AUTHORITY:CODE and pas this to msLoadProjectionString().
1463
** For example:
1464
**   ESRI/0/53009 to ESRI:53009
1465
**   ESRI::53009  to ESRI:53009
1466
** sep_char is '/' for URIs and ':' for URNs.
1467
** Returns 0 on success, -1 if the string could not be parsed. */
1468
static int msLoadProjectionStringGenericCRS(projectionObj *p,
14✔
1469
                                            const char *p_auth, char sep_char) {
1470
  const char *sep = strchr(p_auth, sep_char);
14✔
1471
  if (sep == NULL)
14✔
1472
    return -1;
1473

1474
  char auth[64] = {0};
13✔
1475
  size_t authlen = sep - p_auth;
13✔
1476
  if (authlen == 0 || authlen >= sizeof(auth))
13✔
1477
    return -1;
1478

1479
  strlcpy(auth, p_auth, authlen + 1);
13✔
1480

1481
  /* skip version field (as it may be empty, e.g. ESRI::53009 or ESRI/0/53009)
1482
   */
1483
  const char *p_code = sep + 1;
13✔
1484
  sep = strchr(p_code, sep_char);
13✔
1485
  if (sep != NULL)
13✔
1486
    p_code = sep + 1;
13✔
1487

1488
  if (*p_code == '\0')
13✔
1489
    return -1;
1490

1491
  char authcode[128] = {0};
13✔
1492
  snprintf(authcode, sizeof(authcode), "%s:%s", auth, p_code);
1493
  return msLoadProjectionString(p, authcode);
13✔
1494
}
1495

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

1500
  msFreeProjectionExceptContext(p);
4,012✔
1501

1502
  /* Known OGC/ISO authorities that PROJ 6+ supports directly without
1503
  ** init= prefix. Anything not in this list (e.g. custom init files
1504
  ** like epsg2, nad27) will use the init= prefix instead. */
1505
  static const char *const validAuthorities[] = {
1506
      "EPSG", "ESRI", "IAU_2015", "IGNF", "NKG", "OGC", NULL};
1507

1508
  if (value[0] == '+') {
4,012✔
1509
    /* Handle new style definitions e.g. "+proj=utm +zone=11 +ellps=WGS84" */
1510
    char *trimmed;
1511
    int i, i_out = 0;
1512

1513
    trimmed = msStrdup(value + 1);
1,907✔
1514
    for (i = 1; value[i] != '\0'; i++) {
50,333✔
1515
      if (!isspace(value[i]))
48,426✔
1516
        trimmed[i_out++] = value[i];
46,340✔
1517
    }
1518
    trimmed[i_out] = '\0';
1,907✔
1519

1520
    p->args = msStringSplit(trimmed, '+', &p->numargs);
1,907✔
1521
    free(trimmed);
1,907✔
1522
  } else if (strncasecmp(value, "AUTO:", 5) == 0 ||
2,105✔
1523
             strncasecmp(value, "AUTO2:", 6) == 0) {
2,103✔
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 *));
4✔
1529
    p->args[0] = msStrdup(value);
4✔
1530
    p->numargs = 1;
4✔
1531
  } else if (msLoadProjectionStringEPSGLike(p, value, "EPSG:", MS_FALSE) == 0) {
2,101✔
1532
    /* Assume long/lat ordering. Use msLoadProjectionStringEPSG() if wanting to
1533
     * follow EPSG axis */
1534
  } else if (msLoadProjectionStringEPSGLike(
260✔
1535
                 p, value, "urn:ogc:def:crs:EPSG:", MS_TRUE) == 0) {
1536
  } else if (msLoadProjectionStringEPSGLike(
179✔
1537
                 p, value, "urn:EPSG:geographicCRS:", MS_TRUE) == 0) {
1538
  } else if (msLoadProjectionStringEPSGLike(
174✔
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:") ==
173✔
1545
             0) {
1546
  } else if (msLoadProjectionStringEPSGLike(
165✔
1547
                 p, value, "http://www.opengis.net/def/crs/EPSG/", MS_TRUE) ==
1548
             0) {
1549
    /* URI projection support. Kept separate from the generic URI handler
1550
    ** below because MS_TRUE enforces correct EPSG axis ordering. */
1551
  } else if (msLoadProjectionStringCRSLike(
137✔
1552
                 p, value, "http://www.opengis.net/def/crs/OGC/") == 0) {
1553
    /* Mandatory support for this URI format specified in WFS1.1 (also in 1.0?)
1554
     */
1555
  } else if (strncasecmp(value, "http://www.opengis.net/def/crs/", 31) == 0) {
137✔
1556
    /* Generic URI handler for non-EPSG/OGC authorities
1557
    ** in the format http://www.opengis.net/def/crs/AUTHORITY/version/CODE
1558
    ** e.g. http://www.opengis.net/def/crs/ESRI/0/53009
1559
    ** EPSG and OGC are handled above due to axis order and CRS identifier
1560
    ** special cases.
1561
    */
1562
    return msLoadProjectionStringGenericCRS(p, value + 31, '/');
1✔
1563
  } else if (msLoadProjectionStringEPSGLike(
136✔
1564
                 p, value, "http://www.opengis.net/gml/srs/epsg.xml#",
1565
                 MS_FALSE) == 0) {
1566
    /* We assume always long/lat ordering, as that is what GeoServer does...  */
1567
  } else if (msLoadProjectionStringCRSLike(p, value, "CRS:") == 0) {
132✔
1568
  } else if (strncasecmp(value, "urn:ogc:def:crs:", 16) == 0) {
124✔
1569
    /* Generic URN handler for non-EPSG/OGC authorities e.g.
1570
     *urn:ogc:def:crs:ESRI::53009
1571
     ** Format is urn:ogc:def:crs:AUTHORITY:version:CODE where version may be
1572
     *empty
1573
     */
1574
    return msLoadProjectionStringGenericCRS(p, value + 16, ':');
13✔
1575
  } else if (strchr(value, ':') != NULL &&
111✔
1576
             strncasecmp(value, "init=", 5) != 0) {
159✔
1577
    /* Handle AUTHORITY:CODE pattern e.g. ESRI:54030, IAU:2015:30100,
1578
    ** epsg2:42304. Check if authority is in the known list - if so pass
1579
    ** directly to PROJ 6+, otherwise prepend init= for file-based
1580
    ** references and legacy PROJ 4. */
1581
    const char *sep = strchr(value, ':');
1582
    char authUpper[64] = {0};
49✔
1583
    size_t authlen = sep - value;
49✔
1584
    bool knownAuthority = false;
1585
    if (authlen < sizeof(authUpper)) {
49✔
1586
      strlcpy(authUpper, value, authlen + 1);
49✔
1587
      for (size_t i = 0; authUpper[i]; i++)
265✔
1588
        authUpper[i] = (char)toupper((unsigned char)authUpper[i]);
216✔
1589
      for (int i = 0; validAuthorities[i] != NULL; i++) {
203✔
1590
        if (strcmp(authUpper, validAuthorities[i]) == 0) {
184✔
1591
          knownAuthority = true;
1592
          break;
1593
        }
1594
      }
1595
    }
1596
    if (knownAuthority) {
49✔
1597
      /* PROJ 6+ handles known authorities directly */
1598
      p->args = (char **)msSmallMalloc(sizeof(char *));
30✔
1599
      p->args[0] = msStrdup(value);
30✔
1600
      p->numargs = 1;
30✔
1601
    } else {
1602
      /* Prepend init= for file-based or legacy PROJ 4 references */
1603
      const size_t buffer_size = strlen("init=") + strlen(value) + 1;
19✔
1604
      char *init_string = (char *)msSmallMalloc(buffer_size);
19✔
1605
      snprintf(init_string, buffer_size, "init=%s", value);
1606
      p->args = (char **)msSmallMalloc(sizeof(char *));
19✔
1607
      p->args[0] = init_string;
19✔
1608
      p->numargs = 1;
19✔
1609
    }
1610
  } else {
1611
    /* Handle old style comma delimited e.g. "proj=utm,zone=11,ellps=WGS84" */
1612
    p->args = msStringSplit(value, ',', &p->numargs);
62✔
1613
  }
1614

1615
  return msProcessProjection(p);
3,998✔
1616
}
1617

1618
static void writeProjection(FILE *stream, int indent, projectionObj *p) {
8✔
1619
  int i;
1620

1621
  if (!p || p->numargs <= 0)
8✔
1622
    return;
1623
  indent++;
8✔
1624
  writeBlockBegin(stream, indent, "PROJECTION");
8✔
1625
  for (i = 0; i < p->numargs; i++)
16✔
1626
    writeString(stream, indent, NULL, NULL, p->args[i]);
8✔
1627
  writeBlockEnd(stream, indent, "PROJECTION");
8✔
1628
}
1629

1630
void initLeader(labelLeaderObj *leader) {
16✔
1631
  leader->gridstep = 5;
16✔
1632
  leader->maxdistance = 0;
16✔
1633

1634
  /* Set maxstyles = 0, styles[] will be allocated as needed on first call
1635
   * to msGrowLabelLeaderStyles()
1636
   */
1637
  leader->numstyles = leader->maxstyles = 0;
16✔
1638
  leader->styles = NULL;
16✔
1639
}
16✔
1640

1641
/*
1642
** Initialize, load and free a labelObj structure
1643
*/
1644
void initLabel(labelObj *label) {
19,412✔
1645
  int i;
1646

1647
  MS_REFCNT_INIT(label);
19,412✔
1648

1649
  label->align = MS_ALIGN_DEFAULT;
19,412✔
1650
  MS_INIT_COLOR(label->color, 0, 0, 0, 255);
19,412✔
1651
  MS_INIT_COLOR(label->outlinecolor, -1, -1, -1, 255); /* don't use it */
19,412✔
1652
  label->outlinewidth = 1;
19,412✔
1653

1654
  MS_INIT_COLOR(label->shadowcolor, -1, -1, -1, 255); /* don't use it */
19,412✔
1655
  label->shadowsizex = label->shadowsizey = 1;
19,412✔
1656

1657
  label->font = NULL;
19,412✔
1658
  label->size = MS_MEDIUM;
19,412✔
1659

1660
  label->position = MS_CC;
19,412✔
1661
  label->angle = 0;
19,412✔
1662
  label->anglemode = MS_NONE;
19,412✔
1663
  label->minsize = MS_MINFONTSIZE;
19,412✔
1664
  label->maxsize = MS_MAXFONTSIZE;
19,412✔
1665
  label->buffer = 0;
19,412✔
1666
  label->offsetx = label->offsety = 0;
19,412✔
1667
  label->minscaledenom = -1;
19,412✔
1668
  label->maxscaledenom = -1;
19,412✔
1669
  label->minfeaturesize = -1; /* no limit */
19,412✔
1670
  label->autominfeaturesize = MS_FALSE;
19,412✔
1671
  label->mindistance = -1;       /* no limit */
19,412✔
1672
  label->repeatdistance = 0;     /* no repeat */
19,412✔
1673
  label->maxoverlapangle = 22.5; /* default max overlap angle */
19,412✔
1674
  label->partials = MS_FALSE;
19,412✔
1675
  label->wrap = '\0';
19,412✔
1676
  label->maxlength = 0;
19,412✔
1677
  label->space_size_10 = 0.0;
19,412✔
1678

1679
  label->encoding = NULL;
19,412✔
1680

1681
  label->force = MS_OFF;
19,412✔
1682
  label->priority = MS_DEFAULT_LABEL_PRIORITY;
19,412✔
1683

1684
  /* Set maxstyles = 0, styles[] will be allocated as needed on first call
1685
   * to msGrowLabelStyles()
1686
   */
1687
  label->numstyles = label->maxstyles = 0;
19,412✔
1688
  label->styles = NULL;
19,412✔
1689

1690
  label->numbindings = 0;
19,412✔
1691
  label->nexprbindings = 0;
19,412✔
1692
  for (i = 0; i < MS_LABEL_BINDING_LENGTH; i++) {
252,356✔
1693
    label->bindings[i].item = NULL;
232,944✔
1694
    label->bindings[i].index = -1;
232,944✔
1695
    msInitExpression(&(label->exprBindings[i]));
232,944✔
1696
  }
1697

1698
  msInitExpression(&(label->expression));
19,412✔
1699
  msInitExpression(&(label->text));
19,412✔
1700

1701
  label->leader = NULL;
19,412✔
1702

1703
  label->sizeunits = MS_INHERIT;
19,412✔
1704
  label->scalefactor = 1.0;
19,412✔
1705

1706
  return;
19,412✔
1707
}
1708

1709
int freeLabelLeader(labelLeaderObj *leader) {
16✔
1710
  int i;
1711
  for (i = 0; i < leader->numstyles; i++) {
32✔
1712
    if (freeStyle(leader->styles[i]) == MS_SUCCESS) {
16✔
1713
      msFree(leader->styles[i]);
16✔
1714
    }
1715
  }
1716
  msFree(leader->styles);
16✔
1717

1718
  return MS_SUCCESS;
16✔
1719
}
1720
int freeLabel(labelObj *label) {
22,918✔
1721
  int i;
1722

1723
  if (MS_REFCNT_DECR_IS_NOT_ZERO(label)) {
22,918✔
1724
    return MS_FAILURE;
1725
  }
1726

1727
  msFree(label->font);
19,335✔
1728
  msFree(label->encoding);
19,335✔
1729

1730
  for (i = 0; i < label->numstyles; i++) { /* each style */
25,152✔
1731
    if (label->styles[i] != NULL) {
5,817✔
1732
      if (freeStyle(label->styles[i]) == MS_SUCCESS) {
5,817✔
1733
        msFree(label->styles[i]);
5,813✔
1734
      }
1735
    }
1736
  }
1737
  msFree(label->styles);
19,335✔
1738

1739
  for (i = 0; i < MS_LABEL_BINDING_LENGTH; i++) {
251,355✔
1740
    msFree(label->bindings[i].item);
232,020✔
1741
    msFreeExpression(&(label->exprBindings[i]));
232,020✔
1742
  }
1743

1744
  msFreeExpression(&(label->expression));
19,335✔
1745
  msFreeExpression(&(label->text));
19,335✔
1746

1747
  if (label->leader) {
19,335✔
1748
    freeLabelLeader(label->leader);
×
1749
    msFree(label->leader);
×
1750
    label->leader = NULL;
×
1751
  }
1752

1753
  return MS_SUCCESS;
1754
}
1755

1756
static int loadLeader(labelLeaderObj *leader) {
15✔
1757
  for (;;) {
1758
    switch (msyylex()) {
53✔
1759
    case (END):
1760
      return (0);
1761
      break;
1762
    case (EOF):
×
1763
      msSetError(MS_EOFERR, NULL, "loadLeader()");
×
1764
      return (-1);
×
1765
    case GRIDSTEP:
8✔
1766
      if (getInteger(&(leader->gridstep), MS_NUM_CHECK_GT, 0, -1) == -1)
8✔
1767
        return (-1);
1768
      break;
1769
    case MAXDISTANCE:
15✔
1770
      if (getInteger(&(leader->maxdistance), MS_NUM_CHECK_GT, 0, -1) == -1)
15✔
1771
        return (-1);
1772
      break;
1773
    case STYLE:
15✔
1774
      if (msGrowLeaderStyles(leader) == NULL)
15✔
1775
        return (-1);
1776
      initStyle(leader->styles[leader->numstyles]);
15✔
1777
      if (loadStyle(leader->styles[leader->numstyles]) != MS_SUCCESS) {
15✔
1778
        freeStyle(leader->styles[leader->numstyles]);
×
1779
        free(leader->styles[leader->numstyles]);
×
1780
        leader->styles[leader->numstyles] = NULL;
×
1781
        return -1;
×
1782
      }
1783
      leader->numstyles++;
15✔
1784
      break;
15✔
1785
    default:
×
1786
      if (strlen(msyystring_buffer) > 0) {
×
1787
        msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
1788
                   "loadLeader()", msyystring_buffer, msyylineno);
1789
        return (-1);
×
1790
      } else {
1791
        return (0); /* end of a string, not an error */
1792
      }
1793
    }
1794
  }
1795
}
1796

1797
static int loadLabel(labelObj *label) {
3,623✔
1798
  int symbol;
1799

1800
  for (;;) {
1801
    switch (msyylex()) {
20,995✔
1802
    case (ANGLE):
380✔
1803
      if ((symbol = getSymbol(5, MS_NUMBER, MS_AUTO, MS_AUTO2, MS_FOLLOW,
380✔
1804
                              MS_BINDING)) == -1)
1805
        return (-1);
1806

1807
      if (symbol == MS_NUMBER) {
380✔
1808
        if (msCheckNumber(msyynumber, MS_NUM_CHECK_RANGE, -360.0, 360.0) ==
290✔
1809
            MS_FAILURE) {
1810
          msSetError(MS_MISCERR,
×
1811
                     "Invalid ANGLE, must be between -360 and 360 (line %d)",
1812
                     "loadLabel()", msyylineno);
1813
          return (MS_FAILURE);
×
1814
        }
1815
        label->angle = (double)msyynumber;
290✔
1816
      } else if (symbol == MS_BINDING) {
90✔
1817
        if (label->bindings[MS_LABEL_BINDING_ANGLE].item != NULL)
×
1818
          msFree(label->bindings[MS_LABEL_BINDING_ANGLE].item);
×
1819
        label->bindings[MS_LABEL_BINDING_ANGLE].item =
×
1820
            msStrdup(msyystring_buffer);
×
1821
        label->numbindings++;
×
1822
      } else {
1823
        label->anglemode = symbol;
90✔
1824
      }
1825
      break;
1826
    case (ALIGN):
196✔
1827
      if ((symbol = getSymbol(4, MS_ALIGN_LEFT, MS_ALIGN_CENTER, MS_ALIGN_RIGHT,
196✔
1828
                              MS_BINDING)) == -1)
1829
        return (-1);
1830
      if ((symbol == MS_ALIGN_LEFT) || (symbol == MS_ALIGN_CENTER) ||
196✔
1831
          (symbol == MS_ALIGN_RIGHT)) {
1832
        label->align = symbol;
194✔
1833
      } else {
1834
        if (label->bindings[MS_LABEL_BINDING_ALIGN].item != NULL)
2✔
1835
          msFree(label->bindings[MS_LABEL_BINDING_ALIGN].item);
×
1836
        label->bindings[MS_LABEL_BINDING_ALIGN].item =
2✔
1837
            msStrdup(msyystring_buffer);
2✔
1838
        label->numbindings++;
2✔
1839
      }
1840
      break;
1841
    case (ANTIALIAS): /*ignore*/
4✔
1842
      msyylex();
4✔
1843
      break;
4✔
1844
    case (BUFFER):
677✔
1845
      if (getInteger(&(label->buffer), MS_NUM_CHECK_NONE, -1, -1) == -1)
677✔
1846
        return (-1);
1847
      break;
1848
    case (COLOR):
3,473✔
1849
      if (loadColor(&(label->color),
3,473✔
1850
                    &(label->bindings[MS_LABEL_BINDING_COLOR])) != MS_SUCCESS)
1851
        return (-1);
1852
      if (label->bindings[MS_LABEL_BINDING_COLOR].item)
3,473✔
1853
        label->numbindings++;
3✔
1854
      break;
1855
    case (ENCODING):
4✔
1856
      if ((getString(&label->encoding)) == MS_FAILURE)
4✔
1857
        return (-1);
1858
      break;
1859
    case (END):
1860
      return (0);
1861
      break;
1862
    case (EOF):
×
1863
      msSetError(MS_EOFERR, NULL, "loadLabel()");
×
1864
      return (-1);
×
1865
    case (EXPRESSION):
112✔
1866
      if (loadExpression(&(label->expression)) == -1)
112✔
1867
        return (-1); /* loadExpression() cleans up previously allocated
1868
                        expression */
1869
      break;
1870
    case (FONT):
2,051✔
1871
      if ((symbol = getSymbol(2, MS_STRING, MS_BINDING)) == -1)
2,051✔
1872
        return (-1);
1873

1874
      if (symbol == MS_STRING) {
2,051✔
1875
        if (label->font != NULL)
2,051✔
1876
          msFree(label->font);
×
1877
        label->font = msStrdup(msyystring_buffer);
2,051✔
1878
      } else {
1879
        if (label->bindings[MS_LABEL_BINDING_FONT].item != NULL)
×
1880
          msFree(label->bindings[MS_LABEL_BINDING_FONT].item);
×
1881
        label->bindings[MS_LABEL_BINDING_FONT].item =
×
1882
            msStrdup(msyystring_buffer);
×
1883
        label->numbindings++;
×
1884
      }
1885
      break;
1886
    case (FORCE):
200✔
1887
      switch (msyylex()) {
200✔
1888
      case MS_ON:
72✔
1889
        label->force = MS_ON;
72✔
1890
        break;
72✔
1891
      case MS_OFF:
×
1892
        label->force = MS_OFF;
×
1893
        break;
×
1894
      case GROUP:
128✔
1895
        label->force = MS_LABEL_FORCE_GROUP;
128✔
1896
        break;
128✔
1897
      default:
×
1898
        msSetError(MS_MISCERR,
×
1899
                   "Invalid FORCE, must be ON,OFF,or GROUP (line %d)",
1900
                   "loadLabel()", msyylineno);
1901
        return (-1);
×
1902
      }
1903
      break;
1904
    case (LABEL):
1905
      break; /* for string loads */
1906
    case (LEADER):
×
1907
      msSetError(MS_MISCERR,
×
1908
                 "LABEL LEADER not implemented. LEADER goes at the CLASS level "
1909
                 "(line %d)",
1910
                 "loadLabel()", msyylineno);
1911
      return (-1);
×
1912
    case (MAXSIZE):
×
1913
      if (getInteger(&(label->maxsize), MS_NUM_CHECK_GT, 0, -1) == -1)
×
1914
        return (-1);
1915
      break;
1916
    case (MAXSCALEDENOM):
×
1917
      if (getDouble(&(label->maxscaledenom), MS_NUM_CHECK_GTE, 0, -1) == -1)
×
1918
        return (-1);
1919
      break;
1920
    case (MAXLENGTH):
266✔
1921
      if (getInteger(&(label->maxlength), MS_NUM_CHECK_GT, 0, -1) == -1)
266✔
1922
        return (-1);
1923
      break;
1924
    case (MINDISTANCE):
20✔
1925
      if (getInteger(&(label->mindistance), MS_NUM_CHECK_GT, 0, -1) == -1)
20✔
1926
        return (-1);
1927
      break;
1928
    case (REPEATDISTANCE):
12✔
1929
      if (getInteger(&(label->repeatdistance), MS_NUM_CHECK_GT, 0, -1) == -1)
12✔
1930
        return (-1);
1931
      break;
1932
    case (MAXOVERLAPANGLE):
43✔
1933
      if (getDouble(&(label->maxoverlapangle), MS_NUM_CHECK_RANGE, 0, 360) ==
43✔
1934
          -1)
1935
        return (-1);
1936
      break;
1937
    case (MINFEATURESIZE):
×
1938
      if ((symbol = getSymbol(2, MS_NUMBER, MS_AUTO)) == -1)
×
1939
        return (-1);
1940
      if (symbol == MS_NUMBER) {
×
1941
        if (msCheckNumber(msyynumber, MS_NUM_CHECK_GT, 0, -1) == MS_FAILURE) {
×
1942
          msSetError(MS_MISCERR,
×
1943
                     "Invalid MINFEATURESIZE, must be greater than 0 (line %d)",
1944
                     "loadLabel()", msyylineno);
1945
          return (MS_FAILURE);
×
1946
        }
1947
        label->minfeaturesize = (int)msyynumber;
×
1948
      } else
1949
        label->autominfeaturesize = MS_TRUE;
×
1950
      break;
1951
    case (MINSCALEDENOM):
×
1952
      if (getDouble(&(label->minscaledenom), MS_NUM_CHECK_GTE, 0, -1) == -1)
×
1953
        return (-1);
1954
      break;
1955
    case (MINSIZE):
×
1956
      if (getInteger(&(label->minsize), MS_NUM_CHECK_GT, 0, -1) == -1)
×
1957
        return (-1);
1958
      break;
1959
    case (OFFSET):
98✔
1960
      if ((symbol = getSymbol(2, MS_NUMBER, MS_BINDING)) == -1)
98✔
1961
        return (MS_FAILURE);
1962
      if (symbol == MS_NUMBER)
98✔
1963
        label->offsetx = (int)msyynumber; // any integer ok
96✔
1964
      else {
1965
        if (label->bindings[MS_LABEL_BINDING_OFFSET_X].item != NULL)
2✔
1966
          msFree(label->bindings[MS_LABEL_BINDING_OFFSET_X].item);
×
1967
        label->bindings[MS_LABEL_BINDING_OFFSET_X].item =
2✔
1968
            msStrdup(msyystring_buffer);
2✔
1969
        label->numbindings++;
2✔
1970
      }
1971

1972
      if ((symbol = getSymbol(2, MS_NUMBER, MS_BINDING)) == -1)
98✔
1973
        return (MS_FAILURE);
1974
      if (symbol == MS_NUMBER)
98✔
1975
        label->offsety = (int)msyynumber; // any integer ok
96✔
1976
      else {
1977
        if (label->bindings[MS_LABEL_BINDING_OFFSET_Y].item != NULL)
2✔
1978
          msFree(label->bindings[MS_LABEL_BINDING_OFFSET_Y].item);
×
1979
        label->bindings[MS_LABEL_BINDING_OFFSET_Y].item =
2✔
1980
            msStrdup(msyystring_buffer);
2✔
1981
        label->numbindings++;
2✔
1982
      }
1983
      break;
1984
    case (OUTLINECOLOR):
580✔
1985
      if (loadColor(&(label->outlinecolor),
580✔
1986
                    &(label->bindings[MS_LABEL_BINDING_OUTLINECOLOR])) !=
1987
          MS_SUCCESS)
1988
        return (-1);
1989
      if (label->bindings[MS_LABEL_BINDING_OUTLINECOLOR].item)
580✔
1990
        label->numbindings++;
×
1991
      break;
1992
    case (OUTLINEWIDTH):
142✔
1993
      if (getInteger(&(label->outlinewidth), MS_NUM_CHECK_GT, 0, -1) == -1)
142✔
1994
        return (-1);
1995
      break;
1996
    case (PARTIALS):
647✔
1997
      if ((label->partials = getSymbol(2, MS_TRUE, MS_FALSE)) == -1)
647✔
1998
        return (-1);
1999
      break;
2000
    case (POSITION):
1,486✔
2001
      if ((label->position =
1,486✔
2002
               getSymbol(11, MS_UL, MS_UC, MS_UR, MS_CL, MS_CC, MS_CR, MS_LL,
1,486✔
2003
                         MS_LC, MS_LR, MS_AUTO, MS_BINDING)) == -1)
2004
        return (-1);
2005
      if (label->position == MS_BINDING) {
1,486✔
2006
        if (label->bindings[MS_LABEL_BINDING_POSITION].item != NULL)
×
2007
          msFree(label->bindings[MS_LABEL_BINDING_POSITION].item);
×
2008
        label->bindings[MS_LABEL_BINDING_POSITION].item =
×
2009
            msStrdup(msyystring_buffer);
×
2010
        label->numbindings++;
×
2011
      }
2012
      break;
2013
    case (PRIORITY):
20✔
2014
      if (label->exprBindings[MS_LABEL_BINDING_PRIORITY].string) {
20✔
2015
        msFreeExpression(&label->exprBindings[MS_LABEL_BINDING_PRIORITY]);
3✔
2016
        label->nexprbindings--;
3✔
2017
      }
2018

2019
      if ((symbol = getSymbol(3, MS_EXPRESSION, MS_NUMBER, MS_BINDING)) == -1)
20✔
2020
        return (-1);
2021
      if (symbol == MS_NUMBER) {
20✔
2022
        if (msCheckNumber(msyynumber, MS_NUM_CHECK_RANGE, 1,
11✔
2023
                          MS_MAX_LABEL_PRIORITY) == MS_FAILURE) {
2024
          msSetError(
×
2025
              MS_MISCERR,
2026
              "Invalid PRIORITY, must be an integer between 1 and %d (line %d)",
2027
              "loadLabel()", MS_MAX_LABEL_PRIORITY, msyylineno);
2028
          return (-1);
×
2029
        }
2030
        label->priority = (int)msyynumber;
11✔
2031
      } else if (symbol == MS_EXPRESSION) {
9✔
2032
        msFree(label->exprBindings[MS_LABEL_BINDING_PRIORITY].string);
6✔
2033
        label->exprBindings[MS_LABEL_BINDING_PRIORITY].string =
6✔
2034
            msStrdup(msyystring_buffer);
6✔
2035
        label->exprBindings[MS_LABEL_BINDING_PRIORITY].type = MS_EXPRESSION;
6✔
2036
        label->nexprbindings++;
6✔
2037
      } else {
2038
        if (label->bindings[MS_LABEL_BINDING_PRIORITY].item != NULL)
3✔
2039
          msFree(label->bindings[MS_LABEL_BINDING_PRIORITY].item);
×
2040
        label->bindings[MS_LABEL_BINDING_PRIORITY].item =
3✔
2041
            msStrdup(msyystring_buffer);
3✔
2042
        label->numbindings++;
3✔
2043
      }
2044
      break;
2045
    case (SHADOWCOLOR):
×
2046
      if (loadColor(&(label->shadowcolor), NULL) != MS_SUCCESS)
×
2047
        return (-1);
2048
      break;
2049
    case (SHADOWSIZE):
×
2050
      if ((symbol = getSymbol(2, MS_NUMBER, MS_BINDING)) == -1)
×
2051
        return (-1);
2052
      if (symbol == MS_NUMBER) {
×
2053
        label->shadowsizex = (int)msyynumber; // x offset, any int ok
×
2054
      } else {
2055
        if (label->bindings[MS_LABEL_BINDING_SHADOWSIZEX].item != NULL)
×
2056
          msFree(label->bindings[MS_LABEL_BINDING_SHADOWSIZEX].item);
×
2057
        label->bindings[MS_LABEL_BINDING_SHADOWSIZEX].item =
×
2058
            msStrdup(msyystring_buffer);
×
2059
        label->numbindings++;
×
2060
      }
2061

2062
      if ((symbol = getSymbol(2, MS_NUMBER, MS_BINDING)) == -1)
×
2063
        return (-1);
2064
      if (symbol == MS_NUMBER) {
×
2065
        label->shadowsizey = (int)msyynumber; // y offset, any int ok
×
2066
      } else {
2067
        if (label->bindings[MS_LABEL_BINDING_SHADOWSIZEY].item != NULL)
×
2068
          msFree(label->bindings[MS_LABEL_BINDING_SHADOWSIZEY].item);
×
2069
        label->bindings[MS_LABEL_BINDING_SHADOWSIZEY].item =
×
2070
            msStrdup(msyystring_buffer);
×
2071
        label->numbindings++;
×
2072
      }
2073
      break;
2074
    case (SIZE):
2,696✔
2075
      if (label->bindings[MS_LABEL_BINDING_SIZE].item) {
2,696✔
2076
        msFree(label->bindings[MS_LABEL_BINDING_SIZE].item);
×
2077
        label->bindings[MS_LABEL_BINDING_SIZE].item = NULL;
×
2078
        label->numbindings--;
×
2079
      }
2080
      if (label->exprBindings[MS_LABEL_BINDING_SIZE].string) {
2,696✔
2081
        msFreeExpression(&label->exprBindings[MS_LABEL_BINDING_SIZE]);
×
2082
        label->nexprbindings--;
×
2083
      }
2084

2085
      if ((symbol = getSymbol(8, MS_EXPRESSION, MS_NUMBER, MS_BINDING, MS_TINY,
2,696✔
2086
                              MS_SMALL, MS_MEDIUM, MS_LARGE, MS_GIANT)) == -1)
2087
        return (-1);
2088

2089
      if (symbol == MS_NUMBER) {
2,696✔
2090
        if (msCheckNumber(msyynumber, MS_NUM_CHECK_GT, 0, -1) == MS_FAILURE) {
2,182✔
2091
          msSetError(MS_MISCERR,
×
2092
                     "Invalid SIZE, must be greater than 0 (line %d)",
2093
                     "loadLabel()", msyylineno);
2094
          return (-1);
×
2095
        }
2096
        label->size = (double)msyynumber;
2,182✔
2097
      } else if (symbol == MS_BINDING) {
514✔
2098
        label->bindings[MS_LABEL_BINDING_SIZE].item =
3✔
2099
            msStrdup(msyystring_buffer);
3✔
2100
        label->numbindings++;
3✔
2101
      } else if (symbol == MS_EXPRESSION) {
511✔
2102
        msFree(label->exprBindings[MS_LABEL_BINDING_SIZE].string);
2✔
2103
        label->exprBindings[MS_LABEL_BINDING_SIZE].string =
2✔
2104
            msStrdup(msyystring_buffer);
2✔
2105
        label->exprBindings[MS_LABEL_BINDING_SIZE].type = MS_EXPRESSION;
2✔
2106
        label->nexprbindings++;
2✔
2107
      } else
2108
        label->size = symbol;
509✔
2109
      break;
2110
    case (STYLE):
1,166✔
2111
      if (msGrowLabelStyles(label) == NULL)
1,166✔
2112
        return (-1);
2113
      initStyle(label->styles[label->numstyles]);
1,166✔
2114
      if (loadStyle(label->styles[label->numstyles]) != MS_SUCCESS) {
1,166✔
2115
        freeStyle(label->styles[label->numstyles]);
×
2116
        free(label->styles[label->numstyles]);
×
2117
        label->styles[label->numstyles] = NULL;
×
2118
        return (-1);
×
2119
      }
2120
      if (label->styles[label->numstyles]->_geomtransform.type ==
1,166✔
2121
          MS_GEOMTRANSFORM_NONE)
2122
        label->styles[label->numstyles]->_geomtransform.type =
98✔
2123
            MS_GEOMTRANSFORM_LABELPOINT; /* set a default, a marker? */
2124
      label->numstyles++;
1,166✔
2125
      break;
1,166✔
2126
    case (TEXT):
320✔
2127
      if (loadExpression(&(label->text)) == -1)
320✔
2128
        return (-1); /* loadExpression() cleans up previously allocated
2129
                        expression */
2130
      if ((label->text.type != MS_STRING) &&
320✔
2131
          (label->text.type != MS_EXPRESSION)) {
2132
        msSetError(
×
2133
            MS_MISCERR,
2134
            "Text expressions support constant or tagged replacement strings.",
2135
            "loadLabel()");
2136
        return (-1);
×
2137
      }
2138
      break;
2139
    case (TYPE):
2,522✔
2140
      if (getSymbol(2, MS_TRUETYPE, MS_BITMAP) == -1)
2,522✔
2141
        return (-1); /* ignore TYPE */
2142
      break;
2143
    case (WRAP):
257✔
2144
      if (getCharacter(&(label->wrap)) == -1)
257✔
2145
        return (-1);
2146
      break;
2147
    default:
×
2148
      if (strlen(msyystring_buffer) > 0) {
×
2149
        msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
2150
                   "loadLabel()", msyystring_buffer, msyylineno);
2151
        return (-1);
×
2152
      } else {
2153
        return (0); /* end of a string, not an error */
2154
      }
2155
    }
2156
  } /* next token */
2157
}
2158

2159
int msUpdateLabelFromString(labelObj *label, char *string) {
×
2160
  if (!label || !string)
×
2161
    return MS_FAILURE;
2162

2163
  msAcquireLock(TLOCK_PARSER);
×
2164

2165
  msyystate = MS_TOKENIZE_STRING;
×
2166
  msyystring = string;
×
2167
  msyylex(); /* sets things up, but doesn't process any tokens */
×
2168

2169
  msyylineno = 1; /* start at line 1 */
×
2170

2171
  if (loadLabel(label) == -1) {
×
2172
    msReleaseLock(TLOCK_PARSER);
×
2173
    return MS_FAILURE; /* parse error */
×
2174
    ;
2175
  }
2176

2177
  msyylex_destroy();
×
2178
  msReleaseLock(TLOCK_PARSER);
×
2179
  return MS_SUCCESS;
×
2180
}
2181

2182
static void writeLeader(FILE *stream, int indent, labelLeaderObj *leader) {
×
2183
  int i;
2184
  if (leader->maxdistance == 0 && leader->numstyles == 0) {
×
2185
    return;
2186
  }
2187
  indent++;
×
2188
  writeBlockBegin(stream, indent, "LEADER");
×
2189
  writeNumber(stream, indent, "MAXDISTANCE", 0, leader->maxdistance);
×
2190
  writeNumber(stream, indent, "GRIDSTEP", 5, leader->gridstep);
×
2191
  for (i = 0; i < leader->numstyles; i++)
×
2192
    writeStyle(stream, indent, leader->styles[i]);
×
2193

2194
  writeBlockEnd(stream, indent, "LEADER");
×
2195
}
2196

2197
static void writeLabel(FILE *stream, int indent, labelObj *label) {
8✔
2198
  int i;
2199
  colorObj c;
2200

2201
  if (label->size == -1)
8✔
2202
    return; /* there is no default label anymore */
×
2203

2204
  indent++;
8✔
2205
  writeBlockBegin(stream, indent, "LABEL");
8✔
2206

2207
  if (label->numbindings > 0 && label->bindings[MS_LABEL_BINDING_ANGLE].item)
8✔
2208
    writeAttributeBinding(stream, indent, "ANGLE",
×
2209
                          &(label->bindings[MS_LABEL_BINDING_ANGLE]));
2210
  else
2211
    writeNumberOrKeyword(stream, indent, "ANGLE", 0, label->angle,
8✔
2212
                         label->anglemode, 3, MS_FOLLOW, "FOLLOW", MS_AUTO,
8✔
2213
                         "AUTO", MS_AUTO2, "AUTO2");
2214

2215
  writeExpression(stream, indent, "EXPRESSION", &(label->expression));
8✔
2216

2217
  if (label->numbindings > 0 && label->bindings[MS_LABEL_BINDING_FONT].item)
8✔
2218
    writeAttributeBinding(stream, indent, "FONT",
×
2219
                          &(label->bindings[MS_LABEL_BINDING_FONT]));
2220
  else
2221
    writeString(stream, indent, "FONT", NULL, label->font);
8✔
2222

2223
  writeNumber(stream, indent, "MAXSIZE", MS_MAXFONTSIZE, label->maxsize);
8✔
2224
  writeNumber(stream, indent, "MINSIZE", MS_MINFONTSIZE, label->minsize);
8✔
2225

2226
  if (label->numbindings > 0 && label->bindings[MS_LABEL_BINDING_SIZE].item)
8✔
2227
    writeAttributeBinding(stream, indent, "SIZE",
×
2228
                          &(label->bindings[MS_LABEL_BINDING_SIZE]));
2229
  else
2230
    writeNumber(stream, indent, "SIZE", -1, label->size);
8✔
2231

2232
  writeKeyword(stream, indent, "ALIGN", label->align, 3, MS_ALIGN_LEFT, "LEFT",
8✔
2233
               MS_ALIGN_CENTER, "CENTER", MS_ALIGN_RIGHT, "RIGHT");
2234
  writeNumber(stream, indent, "BUFFER", 0, label->buffer);
8✔
2235

2236
  if (label->numbindings > 0 && label->bindings[MS_LABEL_BINDING_COLOR].item)
8✔
2237
    writeAttributeBinding(stream, indent, "COLOR",
×
2238
                          &(label->bindings[MS_LABEL_BINDING_COLOR]));
2239
  else {
2240
    MS_INIT_COLOR(c, 0, 0, 0, 255);
8✔
2241
    writeColor(stream, indent, "COLOR", &c, &(label->color));
8✔
2242
  }
2243

2244
  writeString(stream, indent, "ENCODING", NULL, label->encoding);
8✔
2245
  if (label->leader)
8✔
2246
    writeLeader(stream, indent, label->leader);
×
2247
  writeKeyword(stream, indent, "FORCE", label->force, 2, MS_TRUE, "TRUE",
8✔
2248
               MS_LABEL_FORCE_GROUP, "GROUP");
2249
  writeNumber(stream, indent, "MAXLENGTH", 0, label->maxlength);
8✔
2250
  writeNumber(stream, indent, "MAXSCALEDENOM", -1, label->maxscaledenom);
8✔
2251
  writeNumber(stream, indent, "MINDISTANCE", -1, label->mindistance);
8✔
2252
  writeNumberOrKeyword(stream, indent, "MINFEATURESIZE", -1,
8✔
2253
                       label->minfeaturesize, 1, label->autominfeaturesize,
8✔
2254
                       MS_TRUE, "AUTO");
2255
  writeNumber(stream, indent, "MINSCALEDENOM", -1, label->minscaledenom);
8✔
2256
  writeDimension(stream, indent, "OFFSET", label->offsetx, label->offsety, NULL,
8✔
2257
                 NULL);
2258

2259
  if (label->numbindings > 0 &&
8✔
2260
      label->bindings[MS_LABEL_BINDING_OUTLINECOLOR].item)
×
2261
    writeAttributeBinding(stream, indent, "OUTLINECOLOR",
×
2262
                          &(label->bindings[MS_LABEL_BINDING_OUTLINECOLOR]));
2263
  else
2264
    writeColor(stream, indent, "OUTLINECOLOR", NULL, &(label->outlinecolor));
8✔
2265

2266
  writeNumber(stream, indent, "OUTLINEWIDTH", 1, label->outlinewidth);
8✔
2267
  writeKeyword(stream, indent, "PARTIALS", label->partials, 1, MS_TRUE, "TRUE");
8✔
2268

2269
  if (label->numbindings > 0 && label->bindings[MS_LABEL_BINDING_POSITION].item)
8✔
2270
    writeAttributeBinding(stream, indent, "POSITION",
×
2271
                          &(label->bindings[MS_LABEL_BINDING_POSITION]));
2272
  else
2273
    writeKeyword(stream, indent, "POSITION", label->position, 10, MS_UL, "UL",
8✔
2274
                 MS_UC, "UC", MS_UR, "UR", MS_CL, "CL", MS_CC, "CC", MS_CR,
2275
                 "CR", MS_LL, "LL", MS_LC, "LC", MS_LR, "LR", MS_AUTO, "AUTO");
2276

2277
  if (label->numbindings > 0 && label->bindings[MS_LABEL_BINDING_PRIORITY].item)
8✔
2278
    writeAttributeBinding(stream, indent, "PRIORITY",
×
2279
                          &(label->bindings[MS_LABEL_BINDING_PRIORITY]));
2280
  else if (label->nexprbindings > 0 &&
8✔
2281
           label->exprBindings[MS_LABEL_BINDING_PRIORITY].string)
1✔
2282
    writeExpression(stream, indent, "PRIORITY",
1✔
2283
                    &(label->exprBindings[MS_LABEL_BINDING_PRIORITY]));
2284
  else
2285
    writeNumber(stream, indent, "PRIORITY", MS_DEFAULT_LABEL_PRIORITY,
7✔
2286
                label->priority);
7✔
2287

2288
  writeNumber(stream, indent, "REPEATDISTANCE", 0, label->repeatdistance);
8✔
2289
  writeColor(stream, indent, "SHADOWCOLOR", NULL, &(label->shadowcolor));
8✔
2290
  writeDimension(stream, indent, "SHADOWSIZE", label->shadowsizex,
8✔
2291
                 label->shadowsizey,
8✔
2292
                 label->bindings[MS_LABEL_BINDING_SHADOWSIZEX].item,
2293
                 label->bindings[MS_LABEL_BINDING_SHADOWSIZEY].item);
2294

2295
  writeNumber(stream, indent, "MAXOVERLAPANGLE", 22.5, label->maxoverlapangle);
8✔
2296
  for (i = 0; i < label->numstyles; i++)
9✔
2297
    writeStyle(stream, indent, label->styles[i]);
1✔
2298

2299
  writeExpression(stream, indent, "TEXT", &(label->text));
8✔
2300

2301
  writeCharacter(stream, indent, "WRAP", '\0', label->wrap);
8✔
2302
  writeBlockEnd(stream, indent, "LABEL");
8✔
2303
}
2304

2305
char *msWriteLabelToString(labelObj *label) {
1✔
2306
  msIOContext context;
2307
  msIOBuffer buffer;
2308

2309
  context.label = NULL;
1✔
2310
  context.write_channel = MS_TRUE;
1✔
2311
  context.readWriteFunc = msIO_bufferWrite;
1✔
2312
  context.cbData = &buffer;
1✔
2313
  buffer.data = NULL;
1✔
2314
  buffer.data_len = 0;
1✔
2315
  buffer.data_offset = 0;
1✔
2316

2317
  msIO_installHandlers(NULL, &context, NULL);
1✔
2318

2319
  writeLabel(stdout, -1, label);
1✔
2320
  msIO_bufferWrite(&buffer, "", 1);
1✔
2321

2322
  msIO_installHandlers(NULL, NULL, NULL);
1✔
2323

2324
  return (char *)buffer.data;
1✔
2325
}
2326

2327
void msInitExpression(expressionObj *exp) {
1,438,851✔
2328
  memset(exp, 0, sizeof(*exp));
2329
  exp->type = MS_STRING;
1,438,851✔
2330
}
1,438,851✔
2331

2332
void msFreeExpressionTokens(expressionObj *exp) {
846,605✔
2333
  tokenListNodeObjPtr node = NULL;
2334
  tokenListNodeObjPtr nextNode = NULL;
2335

2336
  if (!exp)
846,605✔
2337
    return;
2338

2339
  if (exp->tokens) {
846,605✔
2340
    node = exp->tokens;
2341
    while (node != NULL) {
10,862✔
2342
      nextNode = node->next;
9,459✔
2343

2344
      msFree(node->tokensrc); /* not set very often */
9,459✔
2345

2346
      switch (node->token) {
9,459✔
2347
      case MS_TOKEN_BINDING_DOUBLE:
1,803✔
2348
      case MS_TOKEN_BINDING_INTEGER:
2349
      case MS_TOKEN_BINDING_STRING:
2350
      case MS_TOKEN_BINDING_TIME:
2351
        msFree(node->tokenval.bindval.item);
1,803✔
2352
        break;
1,803✔
2353
      case MS_TOKEN_LITERAL_TIME:
2354
        /* anything to do? */
2355
        break;
2356
      case MS_TOKEN_LITERAL_STRING:
347✔
2357
        msFree(node->tokenval.strval);
347✔
2358
        break;
347✔
2359
      case MS_TOKEN_LITERAL_SHAPE:
119✔
2360
        msFreeShape(node->tokenval.shpval);
119✔
2361
        free(node->tokenval.shpval);
119✔
2362
        break;
119✔
2363
      }
2364

2365
      msFree(node);
9,459✔
2366
      node = nextNode;
2367
    }
2368
    exp->tokens = exp->curtoken = NULL;
1,403✔
2369
  }
2370
}
2371

2372
void msFreeExpression(expressionObj *exp) {
721,436✔
2373
  if (!exp)
721,436✔
2374
    return;
2375
  msFree(exp->string);
721,436✔
2376
  msFree(exp->native_string);
721,436✔
2377
  if ((exp->type == MS_REGEX) && exp->compiled)
721,436✔
2378
    ms_regfree(&(exp->regex));
27✔
2379
  msFreeExpressionTokens(exp);
721,436✔
2380
  msInitExpression(exp); /* re-initialize */
721,436✔
2381
}
2382

2383
int loadExpression(expressionObj *exp) {
4,797✔
2384
  /* TODO: should we call msFreeExpression if exp->string != NULL? We do some
2385
   * checking to avoid a leak but is it enough... */
2386

2387
  msyystring_icase = MS_TRUE;
4,797✔
2388
  if ((exp->type = getSymbol(6, MS_STRING, MS_EXPRESSION, MS_REGEX, MS_ISTRING,
4,797✔
2389
                             MS_IREGEX, MS_LIST)) == -1)
2390
    return (-1);
2391
  if (exp->string != NULL) {
4,797✔
2392
    msFree(exp->string);
×
2393
    msFree(exp->native_string);
×
2394
  }
2395
  exp->string = msStrdup(msyystring_buffer);
4,797✔
2396
  exp->native_string = NULL;
4,797✔
2397

2398
  if (exp->type == MS_ISTRING) {
4,797✔
2399
    exp->flags = exp->flags | MS_EXP_INSENSITIVE;
19✔
2400
    exp->type = MS_STRING;
19✔
2401
  } else if (exp->type == MS_IREGEX) {
4,778✔
2402
    exp->flags = exp->flags | MS_EXP_INSENSITIVE;
19✔
2403
    exp->type = MS_REGEX;
19✔
2404
  }
2405

2406
  return (0);
2407
}
2408

2409
/* ---------------------------------------------------------------------------
2410
   msLoadExpressionString and loadExpressionString
2411

2412
   msLoadExpressionString wraps call to loadExpressionString with mutex
2413
   acquisition and release.  This function should be used everywhere outside
2414
   the mapfile loading phase of an application.  loadExpressionString does
2415
   not check for a mutex!  It should be used only within code that has
2416
   properly acquired a mutex.
2417

2418
   See bug 339 for more details -- SG.
2419
   ------------------------------------------------------------------------ */
2420

2421
int msLoadExpressionString(expressionObj *exp, const char *value) {
2,791✔
2422
  int retval = MS_FAILURE;
2423

2424
  msAcquireLock(TLOCK_PARSER);
2,791✔
2425
  retval = loadExpressionString(exp, value);
2,791✔
2426
  msReleaseLock(TLOCK_PARSER);
2,791✔
2427

2428
  return retval;
2,791✔
2429
}
2430

2431
int loadExpressionString(expressionObj *exp, const char *value) {
2,791✔
2432
  msyystate = MS_TOKENIZE_STRING;
2,791✔
2433
  msyystring = value;
2,791✔
2434
  msyylex(); /* sets things up but processes no tokens */
2,791✔
2435

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

2439
  msyystring_icase = MS_TRUE;
2,791✔
2440
  if ((exp->type = getSymbol2(5, MS_EXPRESSION, MS_REGEX, MS_IREGEX, MS_ISTRING,
2,791✔
2441
                              MS_LIST)) != -1) {
2442
    exp->string = msStrdup(msyystring_buffer);
2,774✔
2443

2444
    if (exp->type == MS_ISTRING) {
2,774✔
2445
      exp->type = MS_STRING;
×
2446
      exp->flags = exp->flags | MS_EXP_INSENSITIVE;
×
2447
    } else if (exp->type == MS_IREGEX) {
2,774✔
2448
      exp->type = MS_REGEX;
×
2449
      exp->flags = exp->flags | MS_EXP_INSENSITIVE;
×
2450
    }
2451
  } else {
2452
    /* failure above is not an error since we'll consider anything not matching
2453
     * (like an unquoted number) as a STRING) */
2454
    exp->type = MS_STRING;
17✔
2455
    if ((strlen(value) - strlen(msyystring_buffer)) == 2)
17✔
2456
      exp->string = msStrdup(msyystring_buffer); /* value was quoted */
3✔
2457
    else
2458
      exp->string = msStrdup(value); /* use the whole value */
14✔
2459
  }
2460

2461
  return (0);
2,791✔
2462
}
2463

2464
/* msGetExpressionString()
2465
 *
2466
 * Returns the string representation of this expression, including delimiters
2467
 * and any flags (e.g. i = case-insensitive).
2468
 *
2469
 * Returns a newly allocated buffer that should be freed by the caller or NULL.
2470
 */
2471
char *msGetExpressionString(expressionObj *exp) {
14✔
2472
  if (exp->string) {
14✔
2473
    char *exprstring;
2474
    size_t buffer_size;
2475
    const char *case_insensitive = "";
2476

2477
    if (exp->flags & MS_EXP_INSENSITIVE)
12✔
2478
      case_insensitive = "i";
2479

2480
    /* Alloc buffer big enough for string + 2 delimiters + 'i' + \0 */
2481
    buffer_size = strlen(exp->string) + 4;
12✔
2482
    exprstring = (char *)msSmallMalloc(buffer_size);
12✔
2483

2484
    switch (exp->type) {
12✔
2485
    case (MS_REGEX):
1✔
2486
      snprintf(exprstring, buffer_size, "/%s/%s", exp->string,
1✔
2487
               case_insensitive);
2488
      return exprstring;
1✔
2489
    case (MS_STRING):
5✔
2490
      snprintf(exprstring, buffer_size, "\"%s\"%s", exp->string,
5✔
2491
               case_insensitive);
2492
      return exprstring;
5✔
2493
    case (MS_EXPRESSION):
6✔
2494
      snprintf(exprstring, buffer_size, "(%s)", exp->string);
6✔
2495
      return exprstring;
6✔
2496
    case (MS_LIST):
×
2497
      snprintf(exprstring, buffer_size, "{%s}", exp->string);
×
2498
      return exprstring;
×
2499
    default:
×
2500
      /* We should never get to here really! */
2501
      free(exprstring);
×
2502
      return NULL;
×
2503
    }
2504
  }
2505
  return NULL;
2506
}
2507

2508
static void writeExpression(FILE *stream, int indent, const char *name,
51✔
2509
                            expressionObj *exp) {
2510
  if (!exp || !exp->string)
51✔
2511
    return;
2512

2513
  writeIndent(stream, ++indent);
2514
  switch (exp->type) {
5✔
2515
  case (MS_LIST):
×
2516
    fprintf(stream, "%s {%s}", name, exp->string);
×
2517
    break;
2518
  case (MS_REGEX):
×
2519
    msIO_fprintf(stream, "%s /%s/", name, exp->string);
×
2520
    break;
×
2521
  case (MS_STRING):
4✔
2522
    msIO_fprintf(stream, "%s ", name);
4✔
2523
    writeStringElement(stream, exp->string);
4✔
2524
    break;
4✔
2525
  case (MS_EXPRESSION):
1✔
2526
    msIO_fprintf(stream, "%s (%s)", name, exp->string);
1✔
2527
    break;
1✔
2528
  }
2529
  if ((exp->type == MS_STRING || exp->type == MS_REGEX) &&
5✔
2530
      (exp->flags & MS_EXP_INSENSITIVE))
4✔
2531
    msIO_fprintf(stream, "i");
×
2532
  writeLineFeed(stream);
2533
}
2534

2535
int loadHashTable(hashTableObj *ptable) {
17,644✔
2536
  assert(ptable);
2537

2538
  for (;;) {
2539
    switch (msyylex()) {
127,298✔
2540
    case (EOF):
×
2541
      msSetError(MS_EOFERR, NULL, "loadHashTable()");
×
2542
      return (MS_FAILURE);
×
2543
    case (END):
2544
      return (MS_SUCCESS);
2545
    case (MS_STRING): {
109,654✔
2546
      char *data = NULL;
109,654✔
2547
      char *key =
2548
          msStrdup(msyystring_buffer); /* the key is *always* a string */
109,654✔
2549
      if (getString(&data) == MS_FAILURE) {
109,654✔
2550
        free(key);
×
2551
        return (MS_FAILURE);
×
2552
      }
2553
      msInsertHashTable(ptable, key, data);
109,654✔
2554

2555
      free(key);
109,654✔
2556
      free(data);
109,654✔
2557
      break;
109,654✔
2558
    }
2559
    default:
×
2560
      msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
2561
                 "loadHashTable()", msyystring_buffer, msyylineno);
2562
      return (MS_FAILURE);
×
2563
    }
2564
  }
2565

2566
  return (MS_SUCCESS);
2567
}
2568

2569
static void writeHashTable(FILE *stream, int indent, const char *title,
48✔
2570
                           hashTableObj *table) {
2571
  struct hashObj *tp;
2572
  int i;
2573

2574
  if (!table)
48✔
2575
    return;
2576
  if (msHashIsEmpty(table))
48✔
2577
    return;
2578

2579
  indent++;
4✔
2580
  writeBlockBegin(stream, indent, title);
4✔
2581
  for (i = 0; i < MS_HASHSIZE; i++) {
168✔
2582
    if (table->items[i] != NULL) {
164✔
2583
      for (tp = table->items[i]; tp != NULL; tp = tp->next)
28✔
2584
        writeNameValuePair(stream, indent, tp->key, tp->data);
14✔
2585
    }
2586
  }
2587
  writeBlockEnd(stream, indent, title);
4✔
2588
}
2589

2590
static void writeHashTableInline(FILE *stream, int indent, char *name,
1✔
2591
                                 hashTableObj *table) {
2592
  struct hashObj *tp = NULL;
2593
  int i;
2594

2595
  if (!table)
1✔
2596
    return;
2597
  if (msHashIsEmpty(table))
1✔
2598
    return;
2599

2600
  ++indent;
2601
  for (i = 0; i < MS_HASHSIZE; ++i) {
×
2602
    if (table->items[i] != NULL) {
×
2603
      for (tp = table->items[i]; tp != NULL; tp = tp->next) {
×
2604
        writeIndent(stream, indent);
2605
        msIO_fprintf(stream, "%s ", name);
×
2606
        writeStringElement(stream, tp->key);
×
2607
        msIO_fprintf(stream, " ");
×
2608
        writeStringElement(stream, tp->data);
×
2609
        writeLineFeed(stream);
2610
      }
2611
    }
2612
  }
2613
}
2614

2615
/*
2616
** Initialize, load and free a cluster object
2617
*/
2618
void initCluster(clusterObj *cluster) {
14,938✔
2619
  cluster->maxdistance = 10;
14,938✔
2620
  cluster->buffer = 0;
14,938✔
2621
  cluster->region = NULL;
14,938✔
2622
  msInitExpression(&(cluster->group));
14,938✔
2623
  msInitExpression(&(cluster->filter));
14,938✔
2624
}
14,938✔
2625

2626
void freeCluster(clusterObj *cluster) {
14,903✔
2627
  msFree(cluster->region);
14,903✔
2628
  msFreeExpression(&(cluster->group));
14,903✔
2629
  msFreeExpression(&(cluster->filter));
14,903✔
2630
}
14,903✔
2631

2632
int loadCluster(clusterObj *cluster) {
5✔
2633
  for (;;) {
2634
    switch (msyylex()) {
16✔
2635
    case (CLUSTER):
2636
      break; /* for string loads */
2637
    case (MAXDISTANCE):
5✔
2638
      if (getDouble(&(cluster->maxdistance), MS_NUM_CHECK_GT, 0, -1) == -1)
5✔
2639
        return (-1);
2640
      break;
2641
    case (BUFFER):
×
2642
      if (getDouble(&(cluster->buffer), MS_NUM_CHECK_GT, 0, -1) == -1)
×
2643
        return (-1);
2644
      break;
2645
    case (REGION):
5✔
2646
      if (getString(&cluster->region) == MS_FAILURE)
5✔
2647
        return (-1);
2648
      break;
2649
    case (END):
2650
      return (0);
2651
      break;
2652
    case (GROUP):
×
2653
      if (loadExpression(&(cluster->group)) == -1)
×
2654
        return (-1);
2655
      break;
2656
    case (FILTER):
×
2657
      if (loadExpression(&(cluster->filter)) == -1)
×
2658
        return (-1);
2659
      break;
2660
    default:
×
2661
      if (strlen(msyystring_buffer) > 0) {
×
2662
        msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
2663
                   "loadCluster()", msyystring_buffer, msyylineno);
2664
        return (-1);
×
2665
      } else {
2666
        return (0); /* end of a string, not an error */
2667
      }
2668
    }
2669
  }
2670
  return (MS_SUCCESS);
2671
}
2672

2673
int msUpdateClusterFromString(clusterObj *cluster, char *string) {
1✔
2674
  if (!cluster || !string)
1✔
2675
    return MS_FAILURE;
2676

2677
  msAcquireLock(TLOCK_PARSER);
1✔
2678

2679
  msyystate = MS_TOKENIZE_STRING;
1✔
2680
  msyystring = string;
1✔
2681
  msyylex(); /* sets things up, but doesn't process any tokens */
1✔
2682

2683
  msyylineno = 1; /* start at line 1 */
1✔
2684

2685
  if (loadCluster(cluster) == -1) {
1✔
2686
    msReleaseLock(TLOCK_PARSER);
×
2687
    return MS_FAILURE; /* parse error */
×
2688
    ;
2689
  }
2690

2691
  msyylex_destroy();
1✔
2692
  msReleaseLock(TLOCK_PARSER);
1✔
2693
  return MS_SUCCESS;
1✔
2694
}
2695

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

2698
  if (cluster->maxdistance == 10 && cluster->buffer == 0.0 &&
8✔
2699
      cluster->region == NULL && cluster->group.string == NULL &&
7✔
2700
      cluster->filter.string == NULL)
7✔
2701
    return; /* Nothing to write */
2702

2703
  indent++;
1✔
2704
  writeBlockBegin(stream, indent, "CLUSTER");
1✔
2705
  writeNumber(stream, indent, "MAXDISTANCE", 10, cluster->maxdistance);
1✔
2706
  writeNumber(stream, indent, "BUFFER", 0, cluster->buffer);
1✔
2707
  writeString(stream, indent, "REGION", NULL, cluster->region);
1✔
2708
  writeExpression(stream, indent, "GROUP", &(cluster->group));
1✔
2709
  writeExpression(stream, indent, "FILTER", &(cluster->filter));
1✔
2710
  writeBlockEnd(stream, indent, "CLUSTER");
1✔
2711
}
2712

2713
char *msWriteClusterToString(clusterObj *cluster) {
1✔
2714
  msIOContext context;
2715
  msIOBuffer buffer;
2716

2717
  context.label = NULL;
1✔
2718
  context.write_channel = MS_TRUE;
1✔
2719
  context.readWriteFunc = msIO_bufferWrite;
1✔
2720
  context.cbData = &buffer;
1✔
2721
  buffer.data = NULL;
1✔
2722
  buffer.data_len = 0;
1✔
2723
  buffer.data_offset = 0;
1✔
2724

2725
  msIO_installHandlers(NULL, &context, NULL);
1✔
2726

2727
  writeCluster(stdout, -1, cluster);
1✔
2728
  msIO_bufferWrite(&buffer, "", 1);
1✔
2729

2730
  msIO_installHandlers(NULL, NULL, NULL);
1✔
2731

2732
  return (char *)buffer.data;
1✔
2733
}
2734

2735
/*
2736
** Initialize, load and free a single style
2737
*/
2738
int initStyle(styleObj *style) {
25,067✔
2739
  int i;
2740
  MS_REFCNT_INIT(style);
25,067✔
2741
  MS_INIT_COLOR(style->color, -1, -1, -1, 255); /* must explicitly set colors */
25,067✔
2742
  MS_INIT_COLOR(style->outlinecolor, -1, -1, -1, 255);
25,067✔
2743
  /* New Color Range fields*/
2744
  MS_INIT_COLOR(style->mincolor, -1, -1, -1, 255);
25,067✔
2745
  MS_INIT_COLOR(style->maxcolor, -1, -1, -1, 255);
25,067✔
2746
  style->minvalue = 0.0;
25,067✔
2747
  style->maxvalue = 1.0;
25,067✔
2748
  style->rangeitem = NULL;
25,067✔
2749
  /* End Color Range fields*/
2750
  style->symbol = 0; /* there is always a default symbol*/
25,067✔
2751
  style->symbolname = NULL;
25,067✔
2752
  style->size = -1; /* in SIZEUNITS (layerObj) */
25,067✔
2753
  style->minsize = MS_MINSYMBOLSIZE;
25,067✔
2754
  style->maxsize = MS_MAXSYMBOLSIZE;
25,067✔
2755
  style->width = 1;        /* in pixels */
25,067✔
2756
  style->outlinewidth = 0; /* in pixels */
25,067✔
2757
  style->minwidth = MS_MINSYMBOLWIDTH;
25,067✔
2758
  style->maxwidth = MS_MAXSYMBOLWIDTH;
25,067✔
2759
  style->minscaledenom = style->maxscaledenom = -1.0;
25,067✔
2760
  style->offsetx = style->offsety = 0;                   /* no offset */
25,067✔
2761
  style->polaroffsetpixel = style->polaroffsetangle = 0; /* no polar offset */
25,067✔
2762
  style->angle = 0;
25,067✔
2763
  style->autoangle = MS_FALSE;
25,067✔
2764
  style->antialiased = MS_TRUE;
25,067✔
2765
  style->opacity = 100; /* fully opaque */
25,067✔
2766

2767
  msInitExpression(&(style->_geomtransform));
25,067✔
2768
  style->_geomtransform.type = MS_GEOMTRANSFORM_NONE;
25,067✔
2769

2770
  style->patternlength = 0; /* solid line */
25,067✔
2771
  style->gap = 0;
25,067✔
2772
  style->initialgap = -1;
25,067✔
2773
  style->linecap = MS_CJC_DEFAULT_CAPS;
25,067✔
2774
  style->linejoin = MS_CJC_DEFAULT_JOINS;
25,067✔
2775
  style->linejoinmaxsize = MS_CJC_DEFAULT_JOIN_MAXSIZE;
25,067✔
2776

2777
  style->numbindings = 0;
25,067✔
2778
  style->nexprbindings = 0;
25,067✔
2779
  for (i = 0; i < MS_STYLE_BINDING_LENGTH; i++) {
325,871✔
2780
    style->bindings[i].item = NULL;
300,804✔
2781
    style->bindings[i].index = -1;
300,804✔
2782
    msInitExpression(&(style->exprBindings[i]));
300,804✔
2783
  }
2784

2785
  style->sizeunits = MS_INHERIT;
25,067✔
2786
  style->scalefactor = 1.0;
25,067✔
2787

2788
  return MS_SUCCESS;
25,067✔
2789
}
2790

2791
int loadStyle(styleObj *style) {
14,121✔
2792
  int symbol;
2793

2794
  for (;;) {
2795
    switch (msyylex()) {
49,563✔
2796
      /* New Color Range fields*/
2797
    case (COLORRANGE):
50✔
2798
      /*These are both in one line now*/
2799
      if (loadColor(&(style->mincolor), NULL) != MS_SUCCESS)
50✔
2800
        return (MS_FAILURE);
2801
      if (loadColor(&(style->maxcolor), NULL) != MS_SUCCESS)
50✔
2802
        return (MS_FAILURE);
2803
      break;
2804
    case (DATARANGE):
50✔
2805
      /*These are both in one line now*/
2806
      if (getDouble(&(style->minvalue), MS_NUM_CHECK_NONE, -1, -1) == -1)
50✔
2807
        return (MS_FAILURE);
2808
      if (getDouble(&(style->maxvalue), MS_NUM_CHECK_NONE, -1, -1) == -1)
50✔
2809
        return (MS_FAILURE);
2810
      break;
2811
    case (RANGEITEM):
16✔
2812
      if (getString(&style->rangeitem) == MS_FAILURE)
16✔
2813
        return (MS_FAILURE);
2814
      break;
2815
      /* End Range fields*/
2816
    case (ANGLE):
411✔
2817
      if ((symbol = getSymbol(3, MS_NUMBER, MS_BINDING, MS_AUTO)) == -1)
411✔
2818
        return (MS_FAILURE);
2819

2820
      if (symbol == MS_NUMBER) {
411✔
2821
        if (msCheckNumber(msyynumber, MS_NUM_CHECK_RANGE, -360.0, 360.0) ==
255✔
2822
            MS_FAILURE) {
2823
          msSetError(MS_MISCERR,
×
2824
                     "Invalid ANGLE, must be between -360 and 360 (line %d)",
2825
                     "loadStyle()", msyylineno);
2826
          return (MS_FAILURE);
×
2827
        }
2828
        style->angle = (double)msyynumber;
255✔
2829
      } else if (symbol == MS_BINDING) {
156✔
2830
        if (style->bindings[MS_STYLE_BINDING_ANGLE].item != NULL)
104✔
2831
          msFree(style->bindings[MS_STYLE_BINDING_ANGLE].item);
×
2832
        style->bindings[MS_STYLE_BINDING_ANGLE].item =
104✔
2833
            msStrdup(msyystring_buffer);
104✔
2834
        style->numbindings++;
104✔
2835
      } else {
2836
        style->autoangle = MS_TRUE;
52✔
2837
      }
2838
      break;
2839
    case (ANTIALIAS):
2✔
2840
      if ((symbol = getSymbol(2, MS_TRUE, MS_FALSE)) == -1)
2✔
2841
        return (MS_FAILURE);
2842
      if (symbol == MS_FALSE) {
2✔
2843
        style->antialiased = MS_FALSE;
1✔
2844
      }
2845
      break;
2846
    case (COLOR):
12,556✔
2847
      if (loadColor(&(style->color),
12,556✔
2848
                    &(style->bindings[MS_STYLE_BINDING_COLOR])) != MS_SUCCESS)
2849
        return (MS_FAILURE);
2850
      if (style->bindings[MS_STYLE_BINDING_COLOR].item)
12,556✔
2851
        style->numbindings++;
20✔
2852
      break;
2853
    case (EOF):
×
2854
      msSetError(MS_EOFERR, NULL, "loadStyle()");
×
2855
      return (MS_FAILURE); /* missing END (probably) */
×
2856
    case (END): {
14,121✔
2857
      int alpha;
2858

2859
      /* apply opacity as the alpha channel color(s) */
2860
      if (style->opacity < 100) {
14,121✔
2861
        alpha = MS_NINT(style->opacity * 2.55);
244✔
2862

2863
        style->color.alpha = alpha;
244✔
2864
        style->outlinecolor.alpha = alpha;
244✔
2865

2866
        style->mincolor.alpha = alpha;
244✔
2867
        style->maxcolor.alpha = alpha;
244✔
2868
      }
2869

2870
      return (MS_SUCCESS);
2871
    } break;
2872
    case (GAP):
87✔
2873
      if (getDouble(&(style->gap), MS_NUM_CHECK_NONE, -1, -1) == -1)
87✔
2874
        return (MS_FAILURE);
2875
      break;
2876
    case (INITIALGAP):
45✔
2877
      if (getDouble(&(style->initialgap), MS_NUM_CHECK_GTE, 0, -1) ==
45✔
2878
          -1) { // zero is ok
2879
        msSetError(MS_MISCERR,
×
2880
                   "INITIALGAP requires a positive values (line %d)",
2881
                   "loadStyle()", msyylineno);
2882
        return (MS_FAILURE);
×
2883
      }
2884
      break;
2885
    case (MAXSCALEDENOM):
×
2886
      if (getDouble(&(style->maxscaledenom), MS_NUM_CHECK_GTE, 0, -1) == -1)
×
2887
        return (MS_FAILURE);
2888
      break;
2889
    case (MINSCALEDENOM):
21✔
2890
      if (getDouble(&(style->minscaledenom), MS_NUM_CHECK_GTE, 0, -1) == -1)
21✔
2891
        return (MS_FAILURE);
2892
      break;
2893
    case (GEOMTRANSFORM): {
1,201✔
2894
      int s;
2895
      if ((s = getSymbol(2, MS_STRING, MS_EXPRESSION)) == -1)
1,201✔
2896
        return (MS_FAILURE);
2897
      if (s == MS_STRING)
1,201✔
2898
        msStyleSetGeomTransform(style, msyystring_buffer);
1,165✔
2899
      else {
2900
        /* handle expression case here for the moment */
2901
        msFree(style->_geomtransform.string);
36✔
2902
        style->_geomtransform.string = msStrdup(msyystring_buffer);
36✔
2903
        style->_geomtransform.type = MS_GEOMTRANSFORM_EXPRESSION;
36✔
2904
      }
2905
    } break;
2906
    case (LINECAP):
36✔
2907
      if ((style->linecap = getSymbol(4, MS_CJC_BUTT, MS_CJC_ROUND,
36✔
2908
                                      MS_CJC_SQUARE, MS_CJC_TRIANGLE)) == -1)
2909
        return (MS_FAILURE);
2910
      break;
2911
    case (LINEJOIN):
×
2912
      if ((style->linejoin = getSymbol(4, MS_CJC_NONE, MS_CJC_ROUND,
×
2913
                                       MS_CJC_MITER, MS_CJC_BEVEL)) == -1)
2914
        return (MS_FAILURE);
2915
      break;
2916
    case (LINEJOINMAXSIZE):
×
2917
      if (getDouble(&(style->linejoinmaxsize), MS_NUM_CHECK_GT, 0, -1) == -1)
×
2918
        return (MS_FAILURE);
2919
      break;
2920
    case (MAXSIZE):
75✔
2921
      if (getDouble(&(style->maxsize), MS_NUM_CHECK_GT, 0, -1) == -1)
75✔
2922
        return (MS_FAILURE);
2923
      break;
2924
    case (MINSIZE):
×
2925
      if (getDouble(&(style->minsize), MS_NUM_CHECK_GTE, 0, -1) == -1)
×
2926
        return (MS_FAILURE);
2927
      break;
2928
    case (MAXWIDTH):
×
2929
      if (getDouble(&(style->maxwidth), MS_NUM_CHECK_GT, 0, -1) == -1)
×
2930
        return (MS_FAILURE);
2931
      break;
2932
    case (MINWIDTH):
×
2933
      if (getDouble(&(style->minwidth), MS_NUM_CHECK_GTE, 0, -1) == -1)
×
2934
        return (MS_FAILURE);
2935
      break;
2936
    case (OFFSET):
176✔
2937
      if ((symbol = getSymbol(2, MS_NUMBER, MS_BINDING)) == -1)
176✔
2938
        return (MS_FAILURE);
2939
      if (symbol == MS_NUMBER)
176✔
2940
        style->offsetx = (double)msyynumber; // any double ok
176✔
2941
      else {
2942
        if (style->bindings[MS_STYLE_BINDING_OFFSET_X].item != NULL)
×
2943
          msFree(style->bindings[MS_STYLE_BINDING_OFFSET_X].item);
×
2944
        style->bindings[MS_STYLE_BINDING_OFFSET_X].item =
×
2945
            msStrdup(msyystring_buffer);
×
2946
        style->numbindings++;
×
2947
      }
2948

2949
      if ((symbol = getSymbol(2, MS_NUMBER, MS_BINDING)) == -1)
176✔
2950
        return (MS_FAILURE);
2951
      if (symbol == MS_NUMBER)
176✔
2952
        style->offsety = (double)msyynumber; // any double ok
176✔
2953
      else {
2954
        if (style->bindings[MS_STYLE_BINDING_OFFSET_Y].item != NULL)
×
2955
          msFree(style->bindings[MS_STYLE_BINDING_OFFSET_Y].item);
×
2956
        style->bindings[MS_STYLE_BINDING_OFFSET_Y].item =
×
2957
            msStrdup(msyystring_buffer);
×
2958
        style->numbindings++;
×
2959
      }
2960
      break;
2961
    case (OPACITY):
248✔
2962
      if ((symbol = getSymbol(2, MS_NUMBER, MS_BINDING)) == -1)
248✔
2963
        return (MS_FAILURE);
2964
      if (symbol == MS_NUMBER)
248✔
2965
        style->opacity = MS_MAX(MS_MIN((int)msyynumber, 100),
248✔
2966
                                0); /* force opacity to between 0 and 100 */
2967
      else {
2968
        if (style->bindings[MS_STYLE_BINDING_OPACITY].item != NULL)
×
2969
          msFree(style->bindings[MS_STYLE_BINDING_OPACITY].item);
×
2970
        style->bindings[MS_STYLE_BINDING_OPACITY].item =
×
2971
            msStrdup(msyystring_buffer);
×
2972
        style->numbindings++;
×
2973
      }
2974
      break;
2975
    case (OUTLINECOLOR):
3,932✔
2976
      if (loadColor(&(style->outlinecolor),
3,932✔
2977
                    &(style->bindings[MS_STYLE_BINDING_OUTLINECOLOR])) !=
2978
          MS_SUCCESS)
2979
        return (MS_FAILURE);
2980
      if (style->bindings[MS_STYLE_BINDING_OUTLINECOLOR].item)
3,932✔
2981
        style->numbindings++;
×
2982
      break;
2983
    case (PATTERN): {
2984
      int done = MS_FALSE;
2985
      for (;;) { /* read till the next END */
2986
        switch (msyylex()) {
236✔
2987
        case (END):
72✔
2988
          if (style->patternlength < 2) {
72✔
2989
            msSetError(MS_SYMERR,
×
2990
                       "Not enough pattern elements. A minimum of 2 are "
2991
                       "required (line %d)",
2992
                       "loadStyle()", msyylineno);
2993
            return (MS_FAILURE);
×
2994
          }
2995
          done = MS_TRUE;
2996
          break;
2997
        case (MS_NUMBER): /* read the pattern values */
164✔
2998
          if (style->patternlength == MS_MAXPATTERNLENGTH) {
164✔
2999
            msSetError(MS_SYMERR, "Pattern too long.", "loadStyle()");
×
3000
            return (MS_FAILURE);
×
3001
          }
3002
          style->pattern[style->patternlength] =
164✔
3003
              atof(msyystring_buffer); // good enough?
164✔
3004
          style->patternlength++;
164✔
3005
          break;
164✔
3006
        default:
×
3007
          msSetError(MS_TYPEERR, "Parsing error near (%s):(line %d)",
×
3008
                     "loadStyle()", msyystring_buffer, msyylineno);
3009
          return (MS_FAILURE);
×
3010
        }
3011
        if (done == MS_TRUE)
236✔
3012
          break;
3013
      }
3014
      break;
3015
    }
3016
    case (OUTLINEWIDTH):
43✔
3017
      if ((symbol = getSymbol(2, MS_NUMBER, MS_BINDING)) == -1)
43✔
3018
        return (MS_FAILURE);
3019
      if (symbol == MS_NUMBER) {
43✔
3020
        if (msCheckNumber(msyynumber, MS_NUM_CHECK_GTE, 0, -1) == MS_FAILURE) {
25✔
3021
          msSetError(MS_MISCERR,
×
3022
                     "Invalid OUTLINEWIDTH, must be greater then or equal to 0 "
3023
                     "(line %d)",
3024
                     "loadStyle()", msyylineno);
3025
          return (MS_FAILURE);
×
3026
        }
3027
        style->outlinewidth = (double)msyynumber;
25✔
3028
      } else {
3029
        if (style->bindings[MS_STYLE_BINDING_OUTLINEWIDTH].item != NULL)
18✔
3030
          msFree(style->bindings[MS_STYLE_BINDING_OUTLINEWIDTH].item);
×
3031
        style->bindings[MS_STYLE_BINDING_OUTLINEWIDTH].item =
18✔
3032
            msStrdup(msyystring_buffer);
18✔
3033
        style->numbindings++;
18✔
3034
      }
3035
      break;
3036
    case (SIZE):
5,826✔
3037
      if (style->exprBindings[MS_STYLE_BINDING_SIZE].string) {
5,826✔
3038
        msFreeExpression(&style->exprBindings[MS_STYLE_BINDING_SIZE]);
3✔
3039
        style->nexprbindings--;
3✔
3040
      }
3041

3042
      if ((symbol = getSymbol(3, MS_EXPRESSION, MS_NUMBER, MS_BINDING)) == -1)
5,826✔
3043
        return (MS_FAILURE);
3044
      if (symbol == MS_NUMBER) {
5,826✔
3045
        if (msCheckNumber(msyynumber, MS_NUM_CHECK_GT, 0, -1) == MS_FAILURE) {
5,727✔
3046
          msSetError(MS_MISCERR,
×
3047
                     "Invalid SIZE, must be greater than 0 (line %d)",
3048
                     "loadStyle()", msyylineno);
3049
          return (MS_FAILURE);
×
3050
        }
3051
        style->size = (double)msyynumber;
5,727✔
3052
      } else if (symbol == MS_EXPRESSION) {
99✔
3053
        msFree(style->exprBindings[MS_STYLE_BINDING_SIZE].string);
6✔
3054
        style->exprBindings[MS_STYLE_BINDING_SIZE].string =
6✔
3055
            msStrdup(msyystring_buffer);
6✔
3056
        style->exprBindings[MS_STYLE_BINDING_SIZE].type = MS_EXPRESSION;
6✔
3057
        style->nexprbindings++;
6✔
3058
      } else {
3059
        if (style->bindings[MS_STYLE_BINDING_SIZE].item != NULL)
93✔
3060
          msFree(style->bindings[MS_STYLE_BINDING_SIZE].item);
3✔
3061
        style->bindings[MS_STYLE_BINDING_SIZE].item =
93✔
3062
            msStrdup(msyystring_buffer);
93✔
3063
        style->numbindings++;
93✔
3064
      }
3065
      break;
3066
    case (STYLE):
3067
      break; /* for string loads */
3068
    case (SYMBOL):
7,125✔
3069
      if ((symbol = getSymbol(3, MS_NUMBER, MS_STRING, MS_BINDING)) == -1)
7,125✔
3070
        return (MS_FAILURE);
3071
      if (symbol == MS_NUMBER) {
7,125✔
3072
        if (msCheckNumber(msyynumber, MS_NUM_CHECK_GTE, 0, -1) == MS_FAILURE) {
4,544✔
3073
          msSetError(
×
3074
              MS_MISCERR,
3075
              "Invalid SYMBOL id, must be greater than or equal to 0 (line %d)",
3076
              "loadStyle()", msyylineno);
3077
          return (MS_FAILURE);
×
3078
        }
3079
        if (style->symbolname != NULL) {
4,544✔
3080
          msFree(style->symbolname);
×
3081
          style->symbolname = NULL;
×
3082
        }
3083
        style->symbol = (int)msyynumber;
4,544✔
3084
      } else if (symbol == MS_STRING) {
2,581✔
3085
        if (style->symbolname != NULL)
2,565✔
3086
          msFree(style->symbolname);
×
3087
        style->symbolname = msStrdup(msyystring_buffer);
2,565✔
3088
      } else {
3089
        if (style->bindings[MS_STYLE_BINDING_SYMBOL].item != NULL)
16✔
3090
          msFree(style->bindings[MS_STYLE_BINDING_SYMBOL].item);
×
3091
        style->bindings[MS_STYLE_BINDING_SYMBOL].item =
16✔
3092
            msStrdup(msyystring_buffer);
16✔
3093
        style->numbindings++;
16✔
3094
      }
3095
      break;
3096
    case (WIDTH):
3,453✔
3097
      if ((symbol = getSymbol(2, MS_NUMBER, MS_BINDING)) == -1)
3,453✔
3098
        return (MS_FAILURE);
3099
      if (symbol == MS_NUMBER) {
3,453✔
3100
        if (msCheckNumber(msyynumber, MS_NUM_CHECK_GTE, 0, -1) == MS_FAILURE) {
3,435✔
3101
          msSetError(
×
3102
              MS_MISCERR,
3103
              "Invalid WIDTH, must be greater than or equal to 0 (line %d)",
3104
              "loadStyle()", msyylineno);
3105
          return (MS_FAILURE);
×
3106
        }
3107
        style->width = (double)msyynumber;
3,435✔
3108
      } else {
3109
        if (style->bindings[MS_STYLE_BINDING_WIDTH].item != NULL)
18✔
3110
          msFree(style->bindings[MS_STYLE_BINDING_WIDTH].item);
×
3111
        style->bindings[MS_STYLE_BINDING_WIDTH].item =
18✔
3112
            msStrdup(msyystring_buffer);
18✔
3113
        style->numbindings++;
18✔
3114
      }
3115
      break;
3116
    case (POLAROFFSET):
13✔
3117
      if ((symbol = getSymbol(2, MS_NUMBER, MS_BINDING)) == -1)
13✔
3118
        return (MS_FAILURE);
3119
      if (symbol == MS_NUMBER) {
13✔
3120
        style->polaroffsetpixel = (double)msyynumber; // ok?
×
3121
      } else {
3122
        if (style->bindings[MS_STYLE_BINDING_POLAROFFSET_PIXEL].item != NULL)
13✔
3123
          msFree(style->bindings[MS_STYLE_BINDING_POLAROFFSET_PIXEL].item);
×
3124
        style->bindings[MS_STYLE_BINDING_POLAROFFSET_PIXEL].item =
13✔
3125
            msStrdup(msyystring_buffer);
13✔
3126
        style->numbindings++;
13✔
3127
      }
3128

3129
      if ((symbol = getSymbol(2, MS_NUMBER, MS_BINDING)) == -1)
13✔
3130
        return (MS_FAILURE);
3131
      if (symbol == MS_NUMBER) {
13✔
3132
        style->polaroffsetangle = (double)msyynumber; // ok?
×
3133
      } else {
3134
        if (style->bindings[MS_STYLE_BINDING_POLAROFFSET_ANGLE].item != NULL)
13✔
3135
          msFree(style->bindings[MS_STYLE_BINDING_POLAROFFSET_ANGLE].item);
×
3136
        style->bindings[MS_STYLE_BINDING_POLAROFFSET_ANGLE].item =
13✔
3137
            msStrdup(msyystring_buffer);
13✔
3138
        style->numbindings++;
13✔
3139
      }
3140
      break;
3141
    default:
×
3142
      if (strlen(msyystring_buffer) > 0) {
×
3143
        msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
3144
                   "loadStyle()", msyystring_buffer, msyylineno);
3145
        return (MS_FAILURE);
×
3146
      } else {
3147
        return (MS_SUCCESS); /* end of a string, not an error */
3148
      }
3149
    }
3150
  }
3151
}
3152

3153
int msUpdateStyleFromString(styleObj *style, char *string) {
4✔
3154
  if (!style || !string)
4✔
3155
    return MS_FAILURE;
3156

3157
  msAcquireLock(TLOCK_PARSER);
4✔
3158

3159
  msyystate = MS_TOKENIZE_STRING;
4✔
3160
  msyystring = string;
4✔
3161
  msyylex(); /* sets things up, but doesn't process any tokens */
4✔
3162

3163
  msyylineno = 1; /* start at line 1 */
4✔
3164

3165
  if (loadStyle(style) == -1) {
4✔
3166
    msReleaseLock(TLOCK_PARSER);
×
3167
    return MS_FAILURE; /* parse error */
×
3168
    ;
3169
  }
3170

3171
  msyylex_destroy();
4✔
3172
  msReleaseLock(TLOCK_PARSER);
4✔
3173
  return MS_SUCCESS;
4✔
3174
}
3175

3176
int freeStyle(styleObj *style) {
25,080✔
3177
  int i;
3178

3179
  if (MS_REFCNT_DECR_IS_NOT_ZERO(style)) {
25,080✔
3180
    return MS_FAILURE;
3181
  }
3182

3183
  msFree(style->symbolname);
25,026✔
3184
  msFreeExpression(&style->_geomtransform);
25,026✔
3185
  msFree(style->rangeitem);
25,026✔
3186

3187
  for (i = 0; i < MS_STYLE_BINDING_LENGTH; i++) {
325,338✔
3188
    msFree(style->bindings[i].item);
300,312✔
3189
    msFreeExpression(&(style->exprBindings[i]));
300,312✔
3190
  }
3191

3192
  return MS_SUCCESS;
3193
}
3194

3195
void writeStyle(FILE *stream, int indent, styleObj *style) {
11✔
3196

3197
  indent++;
11✔
3198
  writeBlockBegin(stream, indent, "STYLE");
11✔
3199

3200
  if (style->numbindings > 0 && style->bindings[MS_STYLE_BINDING_ANGLE].item)
11✔
3201
    writeAttributeBinding(stream, indent, "ANGLE",
×
3202
                          &(style->bindings[MS_STYLE_BINDING_ANGLE]));
3203
  else
3204
    writeNumberOrKeyword(stream, indent, "ANGLE", 0, style->angle,
11✔
3205
                         style->autoangle, 1, MS_TRUE, "AUTO");
3206

3207
  if (style->numbindings > 0 && style->bindings[MS_STYLE_BINDING_COLOR].item)
11✔
3208
    writeAttributeBinding(stream, indent, "COLOR",
×
3209
                          &(style->bindings[MS_STYLE_BINDING_COLOR]));
3210
  else
3211
    writeColor(stream, indent, "COLOR", NULL, &(style->color));
11✔
3212

3213
  writeNumber(stream, indent, "GAP", 0, style->gap);
11✔
3214
  writeNumber(stream, indent, "INITIALGAP", -1, style->initialgap);
11✔
3215

3216
  if (style->_geomtransform.type == MS_GEOMTRANSFORM_EXPRESSION) {
11✔
3217
    writeIndent(stream, indent + 1);
×
3218
    msIO_fprintf(stream, "GEOMTRANSFORM (%s)\n", style->_geomtransform.string);
×
3219
  } else if (style->_geomtransform.type != MS_GEOMTRANSFORM_NONE) {
11✔
3220
    writeKeyword(stream, indent, "GEOMTRANSFORM", style->_geomtransform.type, 8,
1✔
3221
                 MS_GEOMTRANSFORM_BBOX, "\"bbox\"", MS_GEOMTRANSFORM_END,
3222
                 "\"end\"", MS_GEOMTRANSFORM_LABELPOINT, "\"labelpnt\"",
3223
                 MS_GEOMTRANSFORM_LABELPOLY, "\"labelpoly\"",
3224
                 MS_GEOMTRANSFORM_LABELCENTER, "\"labelcenter\"",
3225
                 MS_GEOMTRANSFORM_START, "\"start\"", MS_GEOMTRANSFORM_VERTICES,
3226
                 "\"vertices\"", MS_GEOMTRANSFORM_CENTROID, "\"centroid\"");
3227
  }
3228

3229
  if (style->linecap != MS_CJC_DEFAULT_CAPS) {
11✔
3230
    writeKeyword(stream, indent, "LINECAP", (int)style->linecap, 5, MS_CJC_NONE,
×
3231
                 "NONE", MS_CJC_ROUND, "ROUND", MS_CJC_SQUARE, "SQUARE",
3232
                 MS_CJC_BUTT, "BUTT", MS_CJC_TRIANGLE, "TRIANGLE");
3233
  }
3234
  if (style->linejoin != MS_CJC_DEFAULT_JOINS) {
11✔
3235
    writeKeyword(stream, indent, "LINEJOIN", (int)style->linejoin, 5,
×
3236
                 MS_CJC_NONE, "NONE", MS_CJC_ROUND, "ROUND", MS_CJC_BEVEL,
3237
                 "BEVEL", MS_CJC_MITER, "MITER");
3238
  }
3239
  writeNumber(stream, indent, "LINEJOINMAXSIZE", MS_CJC_DEFAULT_JOIN_MAXSIZE,
11✔
3240
              style->linejoinmaxsize);
3241

3242
  writeNumber(stream, indent, "MAXSCALEDENOM", -1, style->maxscaledenom);
11✔
3243
  writeNumber(stream, indent, "MAXSIZE", MS_MAXSYMBOLSIZE, style->maxsize);
11✔
3244
  writeNumber(stream, indent, "MAXWIDTH", MS_MAXSYMBOLWIDTH, style->maxwidth);
11✔
3245
  writeNumber(stream, indent, "MINSCALEDENOM", -1, style->minscaledenom);
11✔
3246
  writeNumber(stream, indent, "MINSIZE", MS_MINSYMBOLSIZE, style->minsize);
11✔
3247
  writeNumber(stream, indent, "MINWIDTH", MS_MINSYMBOLWIDTH, style->minwidth);
11✔
3248
  if ((style->numbindings > 0 &&
11✔
3249
       (style->bindings[MS_STYLE_BINDING_OFFSET_X].item ||
×
3250
        style->bindings[MS_STYLE_BINDING_OFFSET_Y].item)) ||
×
3251
      style->offsetx != 0 || style->offsety != 0)
11✔
3252
    writeDimension(stream, indent, "OFFSET", style->offsetx, style->offsety,
1✔
3253
                   style->bindings[MS_STYLE_BINDING_OFFSET_X].item,
3254
                   style->bindings[MS_STYLE_BINDING_OFFSET_Y].item);
3255
  if ((style->numbindings > 0 &&
11✔
3256
       (style->bindings[MS_STYLE_BINDING_POLAROFFSET_PIXEL].item ||
×
3257
        style->bindings[MS_STYLE_BINDING_POLAROFFSET_ANGLE].item)) ||
×
3258
      style->polaroffsetangle != 0 || style->polaroffsetpixel != 0)
11✔
3259
    writeDimension(stream, indent, "POLAROFFSET", style->polaroffsetpixel,
×
3260
                   style->polaroffsetangle,
3261
                   style->bindings[MS_STYLE_BINDING_POLAROFFSET_PIXEL].item,
3262
                   style->bindings[MS_STYLE_BINDING_POLAROFFSET_ANGLE].item);
3263

3264
  if (style->numbindings > 0 && style->bindings[MS_STYLE_BINDING_OPACITY].item)
11✔
3265
    writeAttributeBinding(stream, indent, "OPACITY",
×
3266
                          &(style->bindings[MS_STYLE_BINDING_OPACITY]));
3267
  else
3268
    writeNumber(stream, indent, "OPACITY", 100, style->opacity);
11✔
3269

3270
  if (style->numbindings > 0 &&
11✔
3271
      style->bindings[MS_STYLE_BINDING_OUTLINECOLOR].item)
×
3272
    writeAttributeBinding(stream, indent, "OUTLINECOLOR",
×
3273
                          &(style->bindings[MS_STYLE_BINDING_OUTLINECOLOR]));
3274
  else
3275
    writeColor(stream, indent, "OUTLINECOLOR", NULL, &(style->outlinecolor));
11✔
3276

3277
  if (style->numbindings > 0 &&
11✔
3278
      style->bindings[MS_STYLE_BINDING_OUTLINEWIDTH].item)
×
3279
    writeAttributeBinding(stream, indent, "OUTLINEWIDTH",
×
3280
                          &(style->bindings[MS_STYLE_BINDING_OUTLINEWIDTH]));
3281
  else
3282
    writeNumber(stream, indent, "OUTLINEWIDTH", 0, style->outlinewidth);
11✔
3283

3284
  /* PATTERN */
3285
  if (style->patternlength != 0) {
11✔
3286
    int i;
3287
    indent++;
×
3288
    writeBlockBegin(stream, indent, "PATTERN");
×
3289
    writeIndent(stream, indent);
3290
    for (i = 0; i < style->patternlength; i++)
×
3291
      msIO_fprintf(stream, " %.2f", style->pattern[i]);
×
3292
    msIO_fprintf(stream, "\n");
×
3293
    writeBlockEnd(stream, indent, "PATTERN");
×
3294
    indent--;
3295
  }
3296

3297
  if (style->numbindings > 0 && style->bindings[MS_STYLE_BINDING_SIZE].item)
11✔
3298
    writeAttributeBinding(stream, indent, "SIZE",
×
3299
                          &(style->bindings[MS_STYLE_BINDING_SIZE]));
3300
  else
3301
    writeNumber(stream, indent, "SIZE", -1, style->size);
11✔
3302

3303
  if (style->numbindings > 0 && style->bindings[MS_STYLE_BINDING_SYMBOL].item)
11✔
3304
    writeAttributeBinding(stream, indent, "SYMBOL",
×
3305
                          &(style->bindings[MS_STYLE_BINDING_SYMBOL]));
3306
  else
3307
    writeNumberOrString(stream, indent, "SYMBOL", 0, style->symbol,
11✔
3308
                        style->symbolname);
3309

3310
  if (style->numbindings > 0 && style->bindings[MS_STYLE_BINDING_WIDTH].item)
11✔
3311
    writeAttributeBinding(stream, indent, "WIDTH",
×
3312
                          &(style->bindings[MS_STYLE_BINDING_WIDTH]));
3313
  else
3314
    writeNumber(stream, indent, "WIDTH", 1, style->width);
11✔
3315

3316
  writeString(stream, indent, "RANGEITEM", NULL, style->rangeitem);
11✔
3317
  /* If COLORRANGE is valid, assume DATARANGE also needs to be written */
3318
  if (MS_VALID_COLOR(style->mincolor) && MS_VALID_COLOR(style->maxcolor)) {
11✔
3319
    writeColorRange(stream, indent, "COLORRANGE", &(style->mincolor),
×
3320
                    &(style->maxcolor));
3321
    writeDoubleRange(stream, indent, "DATARANGE", style->minvalue,
×
3322
                     style->maxvalue);
3323
  }
3324

3325
  writeBlockEnd(stream, indent, "STYLE");
11✔
3326
}
11✔
3327

3328
char *msWriteStyleToString(styleObj *style) {
1✔
3329
  msIOContext context;
3330
  msIOBuffer buffer;
3331

3332
  context.label = NULL;
1✔
3333
  context.write_channel = MS_TRUE;
1✔
3334
  context.readWriteFunc = msIO_bufferWrite;
1✔
3335
  context.cbData = &buffer;
1✔
3336
  buffer.data = NULL;
1✔
3337
  buffer.data_len = 0;
1✔
3338
  buffer.data_offset = 0;
1✔
3339

3340
  msIO_installHandlers(NULL, &context, NULL);
1✔
3341

3342
  writeStyle(stdout, -1, style);
1✔
3343
  msIO_bufferWrite(&buffer, "", 1);
1✔
3344

3345
  msIO_installHandlers(NULL, NULL, NULL);
1✔
3346

3347
  return (char *)buffer.data;
1✔
3348
}
3349

3350
/*
3351
** Initialize, load and free a single class
3352
*/
3353
int initClass(classObj *class) {
19,257✔
3354
  class->status = MS_ON;
19,257✔
3355
  class->debug = MS_OFF;
19,257✔
3356
  MS_REFCNT_INIT(class);
19,257✔
3357
  class->isfallback = FALSE;
19,257✔
3358

3359
  msInitExpression(&(class->expression));
19,257✔
3360
  class->name = NULL;
19,257✔
3361
  class->title = NULL;
19,257✔
3362
  msInitExpression(&(class->text));
19,257✔
3363

3364
  class->template = NULL;
19,257✔
3365

3366
  initHashTable(&(class->metadata));
19,257✔
3367
  initHashTable(&(class->validation));
19,257✔
3368

3369
  class->maxscaledenom = class->minscaledenom = -1.0;
19,257✔
3370
  class->minfeaturesize = -1; /* no limit */
19,257✔
3371

3372
  /* Set maxstyles = 0, styles[] will be allocated as needed on first call
3373
   * to msGrowClassStyles()
3374
   */
3375
  class->numstyles = class->maxstyles = 0;
19,257✔
3376
  class->styles = NULL;
19,257✔
3377

3378
  class->numlabels = class->maxlabels = 0;
19,257✔
3379
  class->labels = NULL;
19,257✔
3380

3381
  class->keyimage = NULL;
19,257✔
3382

3383
  class->group = NULL;
19,257✔
3384

3385
  class->leader = NULL;
19,257✔
3386

3387
  class->sizeunits = MS_INHERIT;
19,257✔
3388
  class->scalefactor = 1.0;
19,257✔
3389

3390
  return (0);
19,257✔
3391
}
3392

3393
int freeClass(classObj *class) {
19,331✔
3394
  int i;
3395

3396
  if (MS_REFCNT_DECR_IS_NOT_ZERO(class)) {
19,331✔
3397
    return MS_FAILURE;
3398
  }
3399

3400
  msFreeExpression(&(class->expression));
19,216✔
3401
  msFreeExpression(&(class->text));
19,216✔
3402
  msFree(class->name);
19,216✔
3403
  msFree(class->title);
19,216✔
3404
  msFree(class->template);
19,216✔
3405
  msFree(class->group);
19,216✔
3406

3407
  msFreeHashItems(&(class->metadata));
19,216✔
3408
  msFreeHashItems(&(class->validation));
19,216✔
3409

3410
  for (i = 0; i < class->numstyles; i++) { /* each style */
38,307✔
3411
    if (class->styles[i] != NULL) {
19,091✔
3412
      if (freeStyle(class->styles[i]) == MS_SUCCESS) {
19,091✔
3413
        msFree(class->styles[i]);
19,088✔
3414
      }
3415
    }
3416
  }
3417
  msFree(class->styles);
19,216✔
3418

3419
  for (i = 0; i < class->numlabels; i++) { /* each label */
22,504✔
3420
    if (class->labels[i] != NULL) {
3,288✔
3421
      if (freeLabel(class->labels[i]) == MS_SUCCESS) {
3,288✔
3422
        msFree(class->labels[i]);
3,285✔
3423
      }
3424
    }
3425
  }
3426
  msFree(class->labels);
19,216✔
3427

3428
  msFree(class->keyimage);
19,216✔
3429

3430
  if (class->leader) {
19,216✔
3431
    freeLabelLeader(class->leader);
16✔
3432
    msFree(class->leader);
16✔
3433
    class->leader = NULL;
16✔
3434
  }
3435

3436
  return MS_SUCCESS;
3437
}
3438

3439
/*
3440
** Ensure there is at least one free entry in the sttyles array of this
3441
** classObj. Grow the allocated styles array if necessary and allocate
3442
** a new style for styles[numstyles] if there is not already one,
3443
** setting its contents to all zero bytes (i.e. does not call initStyle()
3444
** on it).
3445
**
3446
** This function is safe to use for the initial allocation of the styles[]
3447
** array as well (i.e. when maxstyles==0 and styles==NULL)
3448
**
3449
** Returns a reference to the new styleObj on success, NULL on error.
3450
*/
3451
styleObj *msGrowClassStyles(classObj *class) {
19,214✔
3452
  /* Do we need to increase the size of styles[] by  MS_STYLE_ALLOCSIZE?
3453
   */
3454
  if (class->numstyles == class->maxstyles) {
19,214✔
3455
    styleObj **newStylePtr;
3456
    int i, newsize;
3457

3458
    newsize = class->maxstyles + MS_STYLE_ALLOCSIZE;
18,194✔
3459

3460
    /* Alloc/realloc styles */
3461
    newStylePtr =
3462
        (styleObj **)realloc(class->styles, newsize * sizeof(styleObj *));
18,194✔
3463
    MS_CHECK_ALLOC(newStylePtr, newsize * sizeof(styleObj *), NULL);
18,194✔
3464

3465
    class->styles = newStylePtr;
18,194✔
3466
    class->maxstyles = newsize;
18,194✔
3467
    for (i = class->numstyles; i < class->maxstyles; i++) {
90,970✔
3468
      class->styles[i] = NULL;
72,776✔
3469
    }
3470
  }
3471

3472
  if (class->styles[class->numstyles] == NULL) {
19,214✔
3473
    class->styles[class->numstyles] = (styleObj *)calloc(1, sizeof(styleObj));
19,214✔
3474
    MS_CHECK_ALLOC(class->styles[class->numstyles], sizeof(styleObj), NULL);
19,214✔
3475
  }
3476

3477
  return class->styles[class->numstyles];
19,214✔
3478
}
3479

3480
/* exactly the same as for a classObj */
3481
styleObj *msGrowLabelStyles(labelObj *label) {
5,822✔
3482
  /* Do we need to increase the size of styles[] by  MS_STYLE_ALLOCSIZE?
3483
   */
3484
  if (label->numstyles == label->maxstyles) {
5,822✔
3485
    styleObj **newStylePtr;
3486
    int i, newsize;
3487

3488
    newsize = label->maxstyles + MS_STYLE_ALLOCSIZE;
5,792✔
3489

3490
    /* Alloc/realloc styles */
3491
    newStylePtr =
3492
        (styleObj **)realloc(label->styles, newsize * sizeof(styleObj *));
5,792✔
3493
    MS_CHECK_ALLOC(newStylePtr, newsize * sizeof(styleObj *), NULL);
5,792✔
3494

3495
    label->styles = newStylePtr;
5,792✔
3496
    label->maxstyles = newsize;
5,792✔
3497
    for (i = label->numstyles; i < label->maxstyles; i++) {
28,960✔
3498
      label->styles[i] = NULL;
23,168✔
3499
    }
3500
  }
3501

3502
  if (label->styles[label->numstyles] == NULL) {
5,822✔
3503
    label->styles[label->numstyles] = (styleObj *)calloc(1, sizeof(styleObj));
5,822✔
3504
    MS_CHECK_ALLOC(label->styles[label->numstyles], sizeof(styleObj), NULL);
5,822✔
3505
  }
3506

3507
  return label->styles[label->numstyles];
5,822✔
3508
}
3509

3510
/* exactly the same as for a labelLeaderObj, needs refactoring */
3511
styleObj *msGrowLeaderStyles(labelLeaderObj *leader) {
16✔
3512
  /* Do we need to increase the size of styles[] by  MS_STYLE_ALLOCSIZE?
3513
   */
3514
  if (leader->numstyles == leader->maxstyles) {
16✔
3515
    styleObj **newStylePtr;
3516
    int i, newsize;
3517

3518
    newsize = leader->maxstyles + MS_STYLE_ALLOCSIZE;
16✔
3519

3520
    /* Alloc/realloc styles */
3521
    newStylePtr =
3522
        (styleObj **)realloc(leader->styles, newsize * sizeof(styleObj *));
16✔
3523
    MS_CHECK_ALLOC(newStylePtr, newsize * sizeof(styleObj *), NULL);
16✔
3524

3525
    leader->styles = newStylePtr;
16✔
3526
    leader->maxstyles = newsize;
16✔
3527
    for (i = leader->numstyles; i < leader->maxstyles; i++) {
80✔
3528
      leader->styles[i] = NULL;
64✔
3529
    }
3530
  }
3531

3532
  if (leader->styles[leader->numstyles] == NULL) {
16✔
3533
    leader->styles[leader->numstyles] = (styleObj *)calloc(1, sizeof(styleObj));
16✔
3534
    MS_CHECK_ALLOC(leader->styles[leader->numstyles], sizeof(styleObj), NULL);
16✔
3535
  }
3536

3537
  return leader->styles[leader->numstyles];
16✔
3538
}
3539

3540
/* msMaybeAllocateClassStyle()
3541
**
3542
** Ensure that requested style index exists and has been initialized.
3543
**
3544
** Returns MS_SUCCESS/MS_FAILURE.
3545
*/
3546
int msMaybeAllocateClassStyle(classObj *c, int idx) {
3,208✔
3547
  if (c == NULL)
3,208✔
3548
    return MS_FAILURE;
3549

3550
  if (idx < 0) {
3,208✔
3551
    msSetError(MS_MISCERR, "Invalid style index: %d",
×
3552
               "msMaybeAllocateClassStyle()", idx);
3553
    return MS_FAILURE;
×
3554
  }
3555

3556
  /* Alloc empty styles as needed up to idx.
3557
   * Nothing to do if requested style already exists
3558
   */
3559
  while (c->numstyles <= idx) {
6,414✔
3560
    if (msGrowClassStyles(c) == NULL)
3,206✔
3561
      return MS_FAILURE;
3562

3563
    if (initStyle(c->styles[c->numstyles]) == MS_FAILURE) {
3,206✔
3564
      msSetError(MS_MISCERR, "Failed to init new styleObj",
×
3565
                 "msMaybeAllocateClassStyle()");
3566
      freeStyle(c->styles[c->numstyles]);
×
3567
      free(c->styles[c->numstyles]);
×
3568
      c->styles[c->numstyles] = NULL;
×
3569
      return (MS_FAILURE);
×
3570
    }
3571
    c->numstyles++;
3,206✔
3572
  }
3573
  return MS_SUCCESS;
3574
}
3575

3576
/*
3577
 * Reset style info in the class to defaults
3578
 * the only members we don't touch are name, expression, and join/query stuff
3579
 * This is used with STYLEITEM before overwriting the contents of a class.
3580
 */
3581
void resetClassStyle(classObj *class) {
51✔
3582
  int i;
3583

3584
  /* reset labels */
3585
  for (i = 0; i < class->numlabels; i++) {
54✔
3586
    if (class->labels[i] != NULL) {
3✔
3587
      if (freeLabel(class->labels[i]) == MS_SUCCESS) {
3✔
3588
        msFree(class->labels[i]);
3✔
3589
      }
3590
      class->labels[i] = NULL;
3✔
3591
    }
3592
  }
3593
  class->numlabels = 0;
51✔
3594

3595
  msFreeExpression(&(class->text));
51✔
3596
  msInitExpression(&(class->text));
51✔
3597

3598
  /* reset styles */
3599
  for (i = 0; i < class->numstyles; i++) {
132✔
3600
    if (class->styles[i] != NULL) {
81✔
3601
      if (freeStyle(class->styles[i]) == MS_SUCCESS) {
81✔
3602
        msFree(class->styles[i]);
81✔
3603
      }
3604
      class->styles[i] = NULL;
81✔
3605
    }
3606
  }
3607
  class->numstyles = 0;
51✔
3608

3609
  class->layer = NULL;
51✔
3610
}
51✔
3611

3612
labelObj *msGrowClassLabels(classObj *class) {
3,308✔
3613

3614
  /* Do we need to increase the size of labels[] by MS_LABEL_ALLOCSIZE?
3615
   */
3616
  if (class->numlabels == class->maxlabels) {
3,308✔
3617
    labelObj **newLabelPtr;
3618
    int i, newsize;
3619

3620
    newsize = class->maxlabels + MS_LABEL_ALLOCSIZE;
3,237✔
3621

3622
    /* Alloc/realloc labels */
3623
    newLabelPtr =
3624
        (labelObj **)realloc(class->labels, newsize * sizeof(labelObj *));
3,237✔
3625
    MS_CHECK_ALLOC(newLabelPtr, newsize * sizeof(labelObj *), NULL);
3,237✔
3626

3627
    class->labels = newLabelPtr;
3,237✔
3628
    class->maxlabels = newsize;
3,237✔
3629
    for (i = class->numlabels; i < class->maxlabels; i++) {
9,711✔
3630
      class->labels[i] = NULL;
6,474✔
3631
    }
3632
  }
3633

3634
  if (class->labels[class->numlabels] == NULL) {
3,308✔
3635
    class->labels[class->numlabels] = (labelObj *)calloc(1, sizeof(labelObj));
3,308✔
3636
    MS_CHECK_ALLOC(class->labels[class->numlabels], sizeof(labelObj), NULL);
3,308✔
3637
  }
3638

3639
  return class->labels[class->numlabels];
3,308✔
3640
}
3641

3642
int loadClass(classObj *class, layerObj *layer) {
13,160✔
3643
  if (!class || !layer)
13,160✔
3644
    return (-1);
3645

3646
  class->layer = (layerObj *)layer;
13,160✔
3647

3648
  for (;;) {
3649
    switch (msyylex()) {
43,891✔
3650
    case (CLASS):
3651
      break; /* for string loads */
3652
    case (DEBUG):
×
3653
      if ((class->debug = getSymbol(3, MS_ON, MS_OFF, MS_NUMBER)) == -1)
×
3654
        return (-1);
3655
      if (class->debug == MS_NUMBER) {
×
3656
        if (msCheckNumber(msyynumber, MS_NUM_CHECK_RANGE, 0, 5) == MS_FAILURE) {
×
3657
          msSetError(MS_MISCERR,
×
3658
                     "Invalid DEBUG level, must be between 0 and 5 (line %d)",
3659
                     "loadClass()", msyylineno);
3660
          return (-1);
×
3661
        }
3662
        class->debug = (int)msyynumber;
×
3663
      }
3664
      break;
3665
    case (EOF):
×
3666
      msSetError(MS_EOFERR, NULL, "loadClass()");
×
3667
      return (-1);
×
3668
    case (END):
3669
      return (0);
3670
      break;
3671
    case (EXPRESSION):
3,325✔
3672
      if (loadExpression(&(class->expression)) == -1)
3,325✔
3673
        return (-1); /* loadExpression() cleans up previously allocated
3674
                        expression */
3675
      break;
3676
    case (GROUP):
1,821✔
3677
      if (getString(&class->group) == MS_FAILURE)
1,821✔
3678
        return (-1); /* getString() cleans up previously allocated string */
3679
      break;
3680
    case (KEYIMAGE):
29✔
3681
      if (getString(&class->keyimage) == MS_FAILURE)
29✔
3682
        return (-1); /* getString() cleans up previously allocated string */
3683
      break;
3684
    case (LABEL):
3,130✔
3685
      if (msGrowClassLabels(class) == NULL)
3,130✔
3686
        return (-1);
3687
      initLabel(class->labels[class->numlabels]);
3,130✔
3688
      class->labels[class->numlabels]->size =
3,130✔
3689
          MS_MEDIUM; /* only set a default if the LABEL section is present */
3690
      if (loadLabel(class->labels[class->numlabels]) == -1) {
3,130✔
3691
        freeLabel(class->labels[class->numlabels]);
×
3692
        free(class->labels[class->numlabels]);
×
3693
        class->labels[class->numlabels] = NULL;
×
3694
        return (-1);
×
3695
      }
3696
      class->numlabels++;
3,130✔
3697
      break;
3,130✔
3698
    case (LEADER):
15✔
3699
      if (!class->leader) {
15✔
3700
        class->leader = msSmallMalloc(sizeof(labelLeaderObj));
15✔
3701
        initLeader(class->leader);
15✔
3702
      }
3703
      if (loadLeader(class->leader) == -1)
15✔
3704
        return (-1);
3705
      break;
3706
    case (MAXSCALE):
48✔
3707
    case (MAXSCALEDENOM):
3708
      if (getDouble(&(class->maxscaledenom), MS_NUM_CHECK_GTE, 0, -1) == -1)
48✔
3709
        return (-1);
3710
      break;
3711
    case (METADATA):
402✔
3712
      if (loadHashTable(&(class->metadata)) != MS_SUCCESS)
402✔
3713
        return (-1);
3714
      break;
3715
    case (MINSCALE):
37✔
3716
    case (MINSCALEDENOM):
3717
      if (getDouble(&(class->minscaledenom), MS_NUM_CHECK_GTE, 0, -1) == -1)
37✔
3718
        return (-1);
3719
      break;
3720
    case (MINFEATURESIZE):
×
3721
      if (getInteger(&(class->minfeaturesize), MS_NUM_CHECK_GT, 0, -1) == -1)
×
3722
        return (-1);
3723
      break;
3724
    case (NAME):
8,045✔
3725
      if (getString(&class->name) == MS_FAILURE)
8,045✔
3726
        return (-1);
3727
      break;
3728
    case (STATUS):
×
3729
      if ((class->status = getSymbol(2, MS_ON, MS_OFF)) == -1)
×
3730
        return (-1);
3731
      break;
3732
    case (STYLE):
12,936✔
3733
      if (msGrowClassStyles(class) == NULL)
12,936✔
3734
        return (-1);
3735
      initStyle(class->styles[class->numstyles]);
12,936✔
3736
      if (loadStyle(class->styles[class->numstyles]) != MS_SUCCESS) {
12,936✔
3737
        freeStyle(class->styles[class->numstyles]);
×
3738
        free(class->styles[class->numstyles]);
×
3739
        class->styles[class->numstyles] = NULL;
×
3740
        return (-1);
×
3741
      }
3742
      class->numstyles++;
12,936✔
3743
      break;
12,936✔
3744
    case (TEMPLATE):
402✔
3745
      if (getString(&class->template) == MS_FAILURE)
402✔
3746
        return (-1); /* getString() cleans up previously allocated string */
3747
      break;
3748
    case (TEXT):
493✔
3749
      if (loadExpression(&(class->text)) == -1)
493✔
3750
        return (-1); /* loadExpression() cleans up previously allocated
3751
                        expression */
3752
      if ((class->text.type != MS_STRING) &&
493✔
3753
          (class->text.type != MS_EXPRESSION)) {
3754
        msSetError(
×
3755
            MS_MISCERR,
3756
            "Text expressions support constant or tagged replacement strings.",
3757
            "loadClass()");
3758
        return (-1);
×
3759
      }
3760
      break;
3761
    case (TITLE):
32✔
3762
      if (getString(&class->title) == MS_FAILURE)
32✔
3763
        return (-1); /* getString() cleans up previously allocated string */
3764
      break;
3765
    case (VALIDATION):
13✔
3766
      if (loadHashTable(&(class->validation)) != MS_SUCCESS)
13✔
3767
        return (-1);
3768
      break;
3769
    case (FALLBACK):
3✔
3770
      if ((class->isfallback = getSymbol(2, MS_TRUE, MS_FALSE)) == -1)
3✔
3771
        return (-1);
3772
      break;
3773
    default:
×
3774
      if (strlen(msyystring_buffer) > 0) {
×
3775
        msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
3776
                   "loadClass()", msyystring_buffer, msyylineno);
3777
        return (-1);
×
3778
      } else {
3779
        return (0); /* end of a string, not an error */
3780
      }
3781
    }
3782
  }
3783
}
3784

3785
static int classResolveSymbolNames(classObj *class) {
13,160✔
3786
  int i, j;
3787
  int try_addimage_if_notfound = MS_TRUE;
3788

3789
  /* step through styles and labels to resolve symbol names */
3790
  /* class styles */
3791
  for (i = 0; i < class->numstyles; i++) {
26,096✔
3792
    if (class->styles[i]->symbolname) {
12,936✔
3793
      if ((class->styles[i]->symbol = msGetSymbolIndex(
1,871✔
3794
               &(class->layer->map->symbolset), class->styles[i]->symbolname,
1,871✔
3795
               try_addimage_if_notfound)) == -1) {
3796
        msSetError(MS_MISCERR,
×
3797
                   "Undefined symbol \"%s\" in class, style %d of layer %s.",
3798
                   "classResolveSymbolNames()", class->styles[i]->symbolname, i,
×
3799
                   class->layer->name);
×
3800
        return MS_FAILURE;
×
3801
      }
3802
    }
3803
  }
3804

3805
  /* label styles */
3806
  for (i = 0; i < class->numlabels; i++) {
16,290✔
3807
    for (j = 0; j < class->labels[i]->numstyles; j++) {
4,296✔
3808
      if (class->labels[i]->styles[j]->symbolname) {
1,166✔
3809
        if ((class->labels[i]->styles[j]->symbol =
694✔
3810
                 msGetSymbolIndex(&(class->layer->map->symbolset),
694✔
3811
                                  class->labels[i]->styles[j]->symbolname,
3812
                                  try_addimage_if_notfound)) == -1) {
3813
          msSetError(
×
3814
              MS_MISCERR,
3815
              "Undefined symbol \"%s\" in class, label style %d of layer %s.",
3816
              "classResolveSymbolNames()",
3817
              class->labels[i]->styles[j]->symbolname, j, class->layer->name);
×
3818
          return MS_FAILURE;
×
3819
        }
3820
      }
3821
    }
3822
  }
3823

3824
  return MS_SUCCESS;
3825
}
3826

3827
int msUpdateClassFromString(classObj *class, char *string) {
×
3828
  if (!class || !string)
×
3829
    return MS_FAILURE;
3830

3831
  msAcquireLock(TLOCK_PARSER);
×
3832

3833
  msyystate = MS_TOKENIZE_STRING;
×
3834
  msyystring = string;
×
3835
  msyylex(); /* sets things up, but doesn't process any tokens */
×
3836

3837
  msyylineno = 1; /* start at line 1 */
×
3838

3839
  if (loadClass(class, class->layer) == -1) {
×
3840
    msReleaseLock(TLOCK_PARSER);
×
3841
    return MS_FAILURE; /* parse error */
×
3842
    ;
3843
  }
3844

3845
  msyylex_destroy();
×
3846
  msReleaseLock(TLOCK_PARSER);
×
3847

3848
  if (classResolveSymbolNames(class) != MS_SUCCESS)
×
3849
    return MS_FAILURE;
3850

3851
  return MS_SUCCESS;
3852
}
3853

3854
static void writeClass(FILE *stream, int indent, classObj *class) {
9✔
3855
  int i;
3856

3857
  if (class->status == MS_DELETE)
9✔
3858
    return;
3859

3860
  indent++;
9✔
3861
  writeBlockBegin(stream, indent, "CLASS");
9✔
3862
  writeString(stream, indent, "NAME", NULL, class->name);
9✔
3863
  writeString(stream, indent, "GROUP", NULL, class->group);
9✔
3864
  writeNumber(stream, indent, "DEBUG", 0, class->debug);
9✔
3865
  writeExpression(stream, indent, "EXPRESSION", &(class->expression));
9✔
3866
  writeString(stream, indent, "KEYIMAGE", NULL, class->keyimage);
9✔
3867
  for (i = 0; i < class->numlabels; i++)
12✔
3868
    writeLabel(stream, indent, class->labels[i]);
3✔
3869
  if (class->leader)
9✔
3870
    writeLeader(stream, indent, class->leader);
×
3871
  writeNumber(stream, indent, "MAXSCALEDENOM", -1, class->maxscaledenom);
9✔
3872
  writeHashTable(stream, indent, "METADATA", &(class->metadata));
9✔
3873
  writeNumber(stream, indent, "MINSCALEDENOM", -1, class->minscaledenom);
9✔
3874
  writeNumber(stream, indent, "MINFEATURESIZE", -1, class->minfeaturesize);
9✔
3875
  writeKeyword(stream, indent, "STATUS", class->status, 1, MS_OFF, "OFF");
9✔
3876
  for (i = 0; i < class->numstyles; i++)
18✔
3877
    writeStyle(stream, indent, class->styles[i]);
9✔
3878
  writeString(stream, indent, "TEMPLATE", NULL, class->template);
9✔
3879
  writeExpression(stream, indent, "TEXT", &(class->text));
9✔
3880
  writeString(stream, indent, "TITLE", NULL, class->title);
9✔
3881
  writeHashTable(stream, indent, "VALIDATION", &(class->validation));
9✔
3882
  writeKeyword(stream, indent, "FALLBACK", class->isfallback, 1, MS_TRUE,
9✔
3883
               "TRUE");
3884
  writeBlockEnd(stream, indent, "CLASS");
9✔
3885
}
3886

3887
static void writeIdentify(FILE *stream, int indent, layerObj *layer) {
7✔
3888
  if (layer->identificationclassauto || layer->identificationclassgroup) {
7✔
3889
    indent++;
×
3890
    writeBlockBegin(stream, indent, "IDENTIFY");
×
3891
    if (layer->identificationclassauto) {
×
3892
      writeIndent(stream, indent + 1);
×
3893
      msIO_fprintf(stream, "CLASSAUTO\n");
×
3894
    }
3895
    if (layer->identificationclassgroup)
×
3896
      writeString(stream, indent, "CLASSGROUP", NULL,
×
3897
                  layer->identificationclassgroup);
3898
    writeBlockEnd(stream, indent, "IDENTIFY");
×
3899
  }
3900
}
7✔
3901

3902
char *msWriteClassToString(classObj *class) {
×
3903
  msIOContext context;
3904
  msIOBuffer buffer;
3905

3906
  context.label = NULL;
×
3907
  context.write_channel = MS_TRUE;
×
3908
  context.readWriteFunc = msIO_bufferWrite;
×
3909
  context.cbData = &buffer;
×
3910
  buffer.data = NULL;
×
3911
  buffer.data_len = 0;
×
3912
  buffer.data_offset = 0;
×
3913

3914
  msIO_installHandlers(NULL, &context, NULL);
×
3915

3916
  writeClass(stdout, -1, class);
×
3917
  msIO_bufferWrite(&buffer, "", 1);
×
3918

3919
  msIO_installHandlers(NULL, NULL, NULL);
×
3920

3921
  return (char *)buffer.data;
×
3922
}
3923

3924
int initCompositingFilter(CompositingFilter *filter) {
×
3925
  filter->filter = NULL;
×
3926
  filter->next = NULL;
×
3927
  return MS_SUCCESS;
×
3928
}
3929

3930
void freeCompositingFilter(CompositingFilter *filter) {
82✔
3931
  if (!filter)
82✔
3932
    return;
3933
  if (filter->next)
×
3934
    freeCompositingFilter(filter->next);
×
3935
  free(filter->filter);
×
3936
  free(filter);
×
3937
}
3938

3939
int initLayerCompositer(LayerCompositer *compositer) {
82✔
3940
  compositer->comp_op = MS_COMPOP_SRC_OVER;
82✔
3941
  compositer->opacity = 100;
82✔
3942
  compositer->next = NULL;
82✔
3943
  compositer->filter = NULL;
82✔
3944
  return MS_SUCCESS;
82✔
3945
}
3946

3947
void freeLayerCompositer(LayerCompositer *compositer) {
82✔
3948
  if (!compositer)
82✔
3949
    return;
3950
  if (compositer->next)
82✔
3951
    freeLayerCompositer(compositer->next);
×
3952
  freeCompositingFilter(compositer->filter);
82✔
3953
  free(compositer);
82✔
3954
}
3955

3956
/*
3957
** Initialize, load and free a single layer structure
3958
*/
3959
int initLayer(layerObj *layer, mapObj *map) {
14,938✔
3960
  if (layer == NULL) {
14,938✔
3961
    msSetError(MS_MEMERR, "Layer is null", "initLayer()");
×
3962
    return (-1);
×
3963
  }
3964
  layer->debug = (int)msGetGlobalDebugLevel();
14,938✔
3965
  MS_REFCNT_INIT(layer);
14,938✔
3966

3967
  /* Set maxclasses = 0, class[] will be allocated as needed on first call
3968
   * to msGrowLayerClasses()
3969
   */
3970
  layer->numclasses = 0;
14,938✔
3971
  layer->maxclasses = 0;
14,938✔
3972
  layer->class = NULL;
14,938✔
3973

3974
  layer->name = NULL;
14,938✔
3975
  layer->group = NULL;
14,938✔
3976
  layer->status = MS_OFF;
14,938✔
3977
  layer->data = NULL;
14,938✔
3978
  layer->rendermode = MS_FIRST_MATCHING_CLASS;
14,938✔
3979

3980
  layer->map = map; /* point back to the encompassing structure */
14,938✔
3981

3982
  layer->type = -1;
14,938✔
3983

3984
  layer->toleranceunits = MS_PIXELS;
14,938✔
3985
  layer->tolerance =
14,938✔
3986
      -1; /* perhaps this should have a different value based on type */
3987
  layer->identificationclassauto = MS_FALSE;
14,938✔
3988
  layer->identificationclassgroup = NULL;
14,938✔
3989

3990
  layer->symbolscaledenom = -1.0; /* -1 means nothing is set */
14,938✔
3991
  layer->scalefactor = 1.0;
14,938✔
3992
  layer->maxscaledenom = -1.0;
14,938✔
3993
  layer->minscaledenom = -1.0;
14,938✔
3994
  layer->minfeaturesize = -1; /* no limit */
14,938✔
3995
  layer->maxgeowidth = -1.0;
14,938✔
3996
  layer->mingeowidth = -1.0;
14,938✔
3997

3998
  layer->sizeunits = MS_PIXELS;
14,938✔
3999

4000
  layer->maxfeatures = -1; /* no quota */
14,938✔
4001
  layer->startindex = -1;  /*used for pagination*/
14,938✔
4002

4003
  layer->scaletokens = NULL;
14,938✔
4004
  layer->numscaletokens = 0;
14,938✔
4005

4006
  layer->template = layer->header = layer->footer = NULL;
14,938✔
4007

4008
  layer->transform = MS_TRUE;
14,938✔
4009

4010
  layer->classitem = NULL;
14,938✔
4011
  layer->classitemindex = -1;
14,938✔
4012

4013
  layer->units = MS_METERS;
14,938✔
4014
  if (msInitProjection(&(layer->projection)) == -1)
14,938✔
4015
    return (-1);
4016

4017
  if (map) {
14,938✔
4018
    msProjectionInheritContextFrom(&(layer->projection), &(map->projection));
14,907✔
4019
  }
4020

4021
  layer->project = MS_TRUE;
14,938✔
4022
  layer->reprojectorLayerToMap = NULL;
14,938✔
4023
  layer->reprojectorMapToLayer = NULL;
14,938✔
4024

4025
  initCluster(&layer->cluster);
14,938✔
4026

4027
  MS_INIT_COLOR(layer->offsite, -1, -1, -1, 255);
14,938✔
4028

4029
  layer->labelcache = MS_ON;
14,938✔
4030
  layer->postlabelcache = MS_FALSE;
14,938✔
4031

4032
  layer->labelitem = NULL;
14,938✔
4033
  layer->labelitemindex = -1;
14,938✔
4034

4035
  layer->labelmaxscaledenom = -1;
14,938✔
4036
  layer->labelminscaledenom = -1;
14,938✔
4037

4038
  layer->tileitem = msStrdup("location");
14,938✔
4039
  layer->tileitemindex = -1;
14,938✔
4040
  layer->tileindex = NULL;
14,938✔
4041
  layer->tilesrs = NULL;
14,938✔
4042

4043
  layer->bandsitem = NULL;
14,938✔
4044
  layer->bandsitemindex = -1;
14,938✔
4045

4046
  layer->currentfeature = layer->features = NULL;
14,938✔
4047

4048
  layer->connection = NULL;
14,938✔
4049
  layer->plugin_library = NULL;
14,938✔
4050
  layer->plugin_library_original = NULL;
14,938✔
4051
  layer->connectiontype = MS_SHAPEFILE;
14,938✔
4052
  layer->vtable = NULL;
14,938✔
4053
  layer->classgroup = NULL;
14,938✔
4054

4055
  layer->layerinfo = NULL;
14,938✔
4056
  layer->wfslayerinfo = NULL;
14,938✔
4057

4058
  layer->items = NULL;
14,938✔
4059
  layer->iteminfo = NULL;
14,938✔
4060
  layer->numitems = 0;
14,938✔
4061

4062
  layer->resultcache = NULL;
14,938✔
4063

4064
  msInitExpression(&(layer->filter));
14,938✔
4065
  layer->filteritem = NULL;
14,938✔
4066
  layer->filteritemindex = -1;
14,938✔
4067

4068
  layer->
4069
    requires
4070
  = layer->labelrequires = NULL;
14,938✔
4071

4072
  initHashTable(&(layer->metadata));
14,938✔
4073
  initHashTable(&(layer->bindvals));
14,938✔
4074
  initHashTable(&(layer->validation));
14,938✔
4075

4076
  layer->styleitem = NULL;
14,938✔
4077
  layer->styleitemindex = -1;
14,938✔
4078

4079
  layer->processing = NULL;
14,938✔
4080
  layer->numjoins = 0;
14,938✔
4081
  layer->joins = (joinObj *)malloc(MS_MAXJOINS * sizeof(joinObj));
14,938✔
4082
  MS_CHECK_ALLOC(layer->joins, MS_MAXJOINS * sizeof(joinObj), -1);
14,938✔
4083

4084
  layer->extent.minx = -1.0;
14,938✔
4085
  layer->extent.miny = -1.0;
14,938✔
4086
  layer->extent.maxx = -1.0;
14,938✔
4087
  layer->extent.maxy = -1.0;
14,938✔
4088

4089
  layer->mask = NULL;
14,938✔
4090
  layer->maskimage = NULL;
14,938✔
4091
  layer->grid = NULL;
14,938✔
4092

4093
  msInitExpression(&(layer->_geomtransform));
14,938✔
4094
  layer->_geomtransform.type = MS_GEOMTRANSFORM_NONE;
14,938✔
4095

4096
  msInitExpression(&(layer->utfdata));
14,938✔
4097
  layer->utfitem = NULL;
14,938✔
4098
  layer->utfitemindex = -1;
14,938✔
4099

4100
  layer->encoding = NULL;
14,938✔
4101

4102
  layer->sortBy.nProperties = 0;
14,938✔
4103
  layer->sortBy.properties = NULL;
14,938✔
4104
  layer->orig_st = NULL;
14,938✔
4105

4106
  layer->compositer = NULL;
14,938✔
4107

4108
  initHashTable(&(layer->connectionoptions));
14,938✔
4109

4110
  return (0);
14,938✔
4111
}
4112

4113
int initScaleToken(scaleTokenObj *token) {
78✔
4114
  token->n_entries = 0;
78✔
4115
  token->name = NULL;
78✔
4116
  token->tokens = NULL;
78✔
4117
  return MS_SUCCESS;
78✔
4118
}
4119

4120
int freeScaleTokenEntry(scaleTokenEntryObj *token) {
176✔
4121
  msFree(token->value);
176✔
4122
  return MS_SUCCESS;
176✔
4123
}
4124

4125
int freeScaleToken(scaleTokenObj *scaletoken) {
78✔
4126
  int i;
4127
  msFree(scaletoken->name);
78✔
4128
  for (i = 0; i < scaletoken->n_entries; i++) {
254✔
4129
    freeScaleTokenEntry(&scaletoken->tokens[i]);
176✔
4130
  }
4131
  msFree(scaletoken->tokens);
78✔
4132
  return MS_SUCCESS;
78✔
4133
}
4134

4135
int freeLayer(layerObj *layer) {
15,294✔
4136
  int i;
4137
  if (!layer)
15,294✔
4138
    return MS_FAILURE;
4139
  if (MS_REFCNT_DECR_IS_NOT_ZERO(layer)) {
15,294✔
4140
    return MS_FAILURE;
4141
  }
4142

4143
  if (layer->debug >= MS_DEBUGLEVEL_VVV)
14,903✔
4144
    msDebug("freeLayer(): freeing layer at %p.\n", layer);
21✔
4145

4146
  if (msLayerIsOpen(layer))
14,903✔
4147
    msLayerClose(layer);
1,021✔
4148

4149
  msFree(layer->name);
14,903✔
4150
  msFree(layer->encoding);
14,903✔
4151
  msFree(layer->group);
14,903✔
4152
  msFree(layer->data);
14,903✔
4153
  msFree(layer->classitem);
14,903✔
4154
  msFree(layer->labelitem);
14,903✔
4155
  msFree(layer->header);
14,903✔
4156
  msFree(layer->footer);
14,903✔
4157
  msFree(layer->template);
14,903✔
4158
  msFree(layer->tileindex);
14,903✔
4159
  msFree(layer->tileitem);
14,903✔
4160
  msFree(layer->tilesrs);
14,903✔
4161
  msFree(layer->bandsitem);
14,903✔
4162
  msFree(layer->plugin_library);
14,903✔
4163
  msFree(layer->plugin_library_original);
14,903✔
4164
  msFree(layer->connection);
14,903✔
4165
  msFree(layer->vtable);
14,903✔
4166
  msFree(layer->classgroup);
14,903✔
4167

4168
  msProjectDestroyReprojector(layer->reprojectorLayerToMap);
14,903✔
4169
  msProjectDestroyReprojector(layer->reprojectorMapToLayer);
14,903✔
4170
  msFreeProjection(&(layer->projection));
14,903✔
4171
  msFreeExpression(&layer->_geomtransform);
14,903✔
4172

4173
  freeCluster(&layer->cluster);
14,903✔
4174

4175
  for (i = 0; i < layer->maxclasses; i++) {
108,327✔
4176
    if (layer->class[i] != NULL) {
93,424✔
4177
      layer->class[i]->layer = NULL;
19,010✔
4178
      if (freeClass(layer->class[i]) == MS_SUCCESS) {
19,010✔
4179
        msFree(layer->class[i]);
19,003✔
4180
      }
4181
    }
4182
  }
4183
  msFree(layer->class);
14,903✔
4184

4185
  if (layer->numscaletokens > 0) {
14,903✔
4186
    for (i = 0; i < layer->numscaletokens; i++) {
125✔
4187
      freeScaleToken(&layer->scaletokens[i]);
78✔
4188
    }
4189
    msFree(layer->scaletokens);
47✔
4190
  }
4191

4192
  if (layer->features)
14,903✔
4193
    freeFeatureList(layer->features);
2,500✔
4194

4195
  if (layer->resultcache) {
14,903✔
4196
    cleanupResultCache(layer->resultcache);
1,008✔
4197
    msFree(layer->resultcache);
1,008✔
4198
  }
4199

4200
  msFree(layer->identificationclassgroup);
14,903✔
4201

4202
  msFree(layer->styleitem);
14,903✔
4203

4204
  msFree(layer->filteritem);
14,903✔
4205
  msFreeExpression(&(layer->filter));
14,903✔
4206

4207
  msFree(layer->requires);
14,903✔
4208
  msFree(layer->labelrequires);
14,903✔
4209

4210
  msFreeHashItems(&(layer->metadata));
14,903✔
4211
  msFreeHashItems(&(layer->validation));
14,903✔
4212
  msFreeHashItems(&layer->bindvals);
14,903✔
4213

4214
  CSLDestroy(layer->processing);
14,903✔
4215

4216
  for (i = 0; i < layer->numjoins; i++) /* each join */
14,903✔
4217
    freeJoin(&(layer->joins[i]));
×
4218
  msFree(layer->joins);
14,903✔
4219
  layer->numjoins = 0;
14,903✔
4220

4221
  layer->classgroup = NULL;
14,903✔
4222

4223
  msFree(layer->mask);
14,903✔
4224
  if (layer->maskimage) {
14,903✔
4225
    msFreeImage(layer->maskimage);
47✔
4226
  }
4227

4228
  if (layer->compositer) {
14,903✔
4229
    freeLayerCompositer(layer->compositer);
82✔
4230
  }
4231

4232
  if (layer->grid) {
14,903✔
4233
    freeGrid(layer->grid);
8✔
4234
    msFree(layer->grid);
8✔
4235
  }
4236

4237
  msFreeExpression(&(layer->utfdata));
14,903✔
4238
  msFree(layer->utfitem);
14,903✔
4239

4240
  for (i = 0; i < layer->sortBy.nProperties; i++)
14,919✔
4241
    msFree(layer->sortBy.properties[i].item);
16✔
4242
  msFree(layer->sortBy.properties);
14,903✔
4243

4244
  msFreeHashItems(&layer->connectionoptions);
14,903✔
4245

4246
  return MS_SUCCESS;
14,903✔
4247
}
4248

4249
/*
4250
** Ensure there is at least one free entry in the class array of this
4251
** layerObj. Grow the allocated class array if necessary and allocate
4252
** a new class for class[numclasses] if there is not already one,
4253
** setting its contents to all zero bytes (i.e. does not call initClass()
4254
** on it).
4255
**
4256
** This function is safe to use for the initial allocation of the class[]
4257
** array as well (i.e. when maxclasses==0 and class==NULL)
4258
**
4259
** Returns a reference to the new classObj on success, NULL on error.
4260
*/
4261
classObj *msGrowLayerClasses(layerObj *layer) {
19,251✔
4262
  /* Do we need to increase the size of class[] by  MS_CLASS_ALLOCSIZE?
4263
   */
4264
  if (layer->numclasses == layer->maxclasses) {
19,251✔
4265
    classObj **newClassPtr;
4266
    int i, newsize;
4267

4268
    newsize = layer->maxclasses + MS_CLASS_ALLOCSIZE;
11,707✔
4269

4270
    /* Alloc/realloc classes */
4271
    newClassPtr =
4272
        (classObj **)realloc(layer->class, newsize * sizeof(classObj *));
11,707✔
4273
    MS_CHECK_ALLOC(newClassPtr, newsize * sizeof(classObj *), NULL);
11,707✔
4274

4275
    layer->class = newClassPtr;
11,707✔
4276
    layer->maxclasses = newsize;
11,707✔
4277
    for (i = layer->numclasses; i < layer->maxclasses; i++) {
105,363✔
4278
      layer->class[i] = NULL;
93,656✔
4279
    }
4280
  }
4281

4282
  if (layer->class[layer->numclasses] == NULL) {
19,251✔
4283
    layer->class[layer->numclasses] = (classObj *)calloc(1, sizeof(classObj));
19,251✔
4284
    MS_CHECK_ALLOC(layer->class[layer->numclasses], sizeof(classObj), NULL);
19,251✔
4285
  }
4286

4287
  return layer->class[layer->numclasses];
19,251✔
4288
}
4289

4290
scaleTokenObj *msGrowLayerScaletokens(layerObj *layer) {
78✔
4291
  layer->scaletokens = msSmallRealloc(
156✔
4292
      layer->scaletokens, (layer->numscaletokens + 1) * sizeof(scaleTokenObj));
78✔
4293
  memset(&layer->scaletokens[layer->numscaletokens], 0, sizeof(scaleTokenObj));
78✔
4294
  return &layer->scaletokens[layer->numscaletokens];
78✔
4295
}
4296

4297
int loadScaletoken(scaleTokenObj *token, layerObj *layer) {
78✔
4298
  (void)layer;
4299
  for (;;) {
4300
    int stop = 0;
4301
    switch (msyylex()) {
234✔
4302
    case (EOF):
×
4303
      msSetError(MS_EOFERR, NULL, "loadScaletoken()");
×
4304
      return (MS_FAILURE);
×
4305
    case (NAME):
78✔
4306
      if (getString(&token->name) == MS_FAILURE)
78✔
4307
        return (MS_FAILURE);
4308
      break;
4309
    case (VALUES):
254✔
4310
      for (;;) {
4311
        if (stop)
4312
          break;
4313
        switch (msyylex()) {
254✔
4314
        case (EOF):
×
4315
          msSetError(MS_EOFERR, NULL, "loadScaletoken()");
×
4316
          return (MS_FAILURE);
×
4317
        case (END):
78✔
4318
          stop = 1;
4319
          if (token->n_entries == 0) {
78✔
4320
            msSetError(MS_PARSEERR,
×
4321
                       "Scaletoken (line:%d) has no VALUES defined",
4322
                       "loadScaleToken()", msyylineno);
4323
            return (MS_FAILURE);
×
4324
          }
4325
          token->tokens[token->n_entries - 1].maxscale = DBL_MAX;
78✔
4326
          break;
4327
        case (MS_STRING):
176✔
4328
          /* we have a key */
4329
          token->tokens =
176✔
4330
              msSmallRealloc(token->tokens, (token->n_entries + 1) *
176✔
4331
                                                sizeof(scaleTokenEntryObj));
4332

4333
          if (1 != sscanf(msyystring_buffer, "%lf",
176✔
4334
                          &token->tokens[token->n_entries].minscale)) {
176✔
4335
            msSetError(MS_PARSEERR,
×
4336
                       "failed to parse SCALETOKEN VALUE (%s):(line %d), "
4337
                       "expecting \"minscale\"",
4338
                       "loadScaletoken()", msyystring_buffer, msyylineno);
4339
            return (MS_FAILURE);
×
4340
          }
4341
          if (token->n_entries == 0) {
176✔
4342
            /* check supplied value was 0*/
4343
            if (token->tokens[0].minscale != 0) {
78✔
4344
              msSetError(MS_PARSEERR,
×
4345
                         "First SCALETOKEN VALUE (%s):(line %d) must be zero, "
4346
                         "expecting \"0\"",
4347
                         "loadScaletoken()", msyystring_buffer, msyylineno);
4348
              return (MS_FAILURE);
×
4349
            }
4350
          } else {
4351
            /* set max scale of previous token */
4352
            token->tokens[token->n_entries - 1].maxscale =
98✔
4353
                token->tokens[token->n_entries].minscale;
98✔
4354
          }
4355
          token->tokens[token->n_entries].value = NULL;
176✔
4356
          if (getString(&(token->tokens[token->n_entries].value)) == MS_FAILURE)
176✔
4357
            return (MS_FAILURE);
4358
          token->n_entries++;
176✔
4359
          break;
176✔
4360
        default:
×
4361
          msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
4362
                     "loadScaletoken()", msyystring_buffer, msyylineno);
4363
          return (MS_FAILURE);
×
4364
        }
4365
      }
4366
      break;
4367
    case (END):
78✔
4368
      if (!token->name || !*(token->name)) {
78✔
4369
        msSetError(MS_PARSEERR,
×
4370
                   "ScaleToken missing mandatory NAME entry (line %d)",
4371
                   "loadScaleToken()", msyylineno);
4372
        return MS_FAILURE;
×
4373
      }
4374
      if (token->n_entries == 0) {
78✔
4375
        msSetError(MS_PARSEERR,
×
4376
                   "ScaleToken missing at least one VALUES entry (line %d)",
4377
                   "loadScaleToken()", msyylineno);
4378
        return MS_FAILURE;
×
4379
      }
4380
      return MS_SUCCESS;
4381
    default:
×
4382
      msSetError(MS_IDENTERR, "Parsing error 2 near (%s):(line %d)",
×
4383
                 "loadScaletoken()", msyystring_buffer, msyylineno);
4384
      return (MS_FAILURE);
×
4385
    }
4386
  } /* next token*/
4387
}
4388

4389
static const struct {
4390
  CompositingOperation eOp;
4391
  const char *pszName;
4392
} CompOps[] = {
4393
    {MS_COMPOP_CLEAR, "clear"},
4394
    {MS_COMPOP_COLOR_BURN, "color-burn"},
4395
    {MS_COMPOP_COLOR_DODGE, "color-dodge"},
4396
    {MS_COMPOP_CONTRAST, "contrast"},
4397
    {MS_COMPOP_DARKEN, "darken"},
4398
    {MS_COMPOP_DIFFERENCE, "difference"},
4399
    {MS_COMPOP_DST, "dst"},
4400
    {MS_COMPOP_DST_ATOP, "dst-atop"},
4401
    {MS_COMPOP_DST_IN, "dst-in"},
4402
    {MS_COMPOP_DST_OUT, "dst-out"},
4403
    {MS_COMPOP_DST_OVER, "dst-over"},
4404
    {MS_COMPOP_EXCLUSION, "exclusion"},
4405
    {MS_COMPOP_HARD_LIGHT, "hard-light"},
4406
    {MS_COMPOP_HSL_COLOR, "hsl-color"},
4407
    {MS_COMPOP_HSL_HUE, "hsl-hue"},
4408
    {MS_COMPOP_HSL_LUMINOSITY, "hsl-luminosity"},
4409
    {MS_COMPOP_HSL_SATURATION, "hsl-saturation"},
4410
    {MS_COMPOP_INVERT, "invert"},
4411
    {MS_COMPOP_INVERT_RGB, "invert-rgb"},
4412
    {MS_COMPOP_LIGHTEN, "lighten"},
4413
    {MS_COMPOP_MINUS, "minus"},
4414
    {MS_COMPOP_MULTIPLY, "multiply"},
4415
    {MS_COMPOP_OVERLAY, "overlay"},
4416
    {MS_COMPOP_PLUS, "plus"},
4417
    {MS_COMPOP_SCREEN, "screen"},
4418
    {MS_COMPOP_SOFT_LIGHT, "soft-light"},
4419
    {MS_COMPOP_SRC, "src"},
4420
    {MS_COMPOP_SRC_ATOP, "src-atop"},
4421
    {MS_COMPOP_SRC_IN, "src-in"},
4422
    {MS_COMPOP_SRC_OUT, "src-out"},
4423
    {MS_COMPOP_SRC_OVER, "src-over"},
4424
    {MS_COMPOP_XOR, "xor"},
4425
};
4426

4427
#define SIZEOF_COMP_OPS ((int)(sizeof(CompOps) / sizeof(CompOps[0])))
4428

4429
int loadLayerCompositer(LayerCompositer *compositer) {
81✔
4430
  for (;;) {
4431
    switch (msyylex()) {
176✔
4432
    case COMPFILTER: {
×
4433
      CompositingFilter **filter = &compositer->filter;
×
4434
      while (*filter) {
×
4435
        filter = &((*filter)->next);
×
4436
      }
4437
      *filter = msSmallMalloc(sizeof(CompositingFilter));
×
4438
      initCompositingFilter(*filter);
×
4439
      if (getString(&((*filter)->filter)) == MS_FAILURE)
×
4440
        return (MS_FAILURE);
4441
    } break;
4442
    case COMPOP: {
14✔
4443
      char *compop = NULL;
14✔
4444
      if (getString(&compop) == MS_FAILURE)
14✔
4445
        return (MS_FAILURE);
×
4446

4447
      bool bFound = false;
4448
      for (int i = 0; i < SIZEOF_COMP_OPS; ++i) {
240✔
4449
        if (strcmp(compop, CompOps[i].pszName) == 0) {
240✔
4450
          bFound = true;
4451
          compositer->comp_op = CompOps[i].eOp;
14✔
4452
          break;
4453
        }
4454
      }
4455
      if (!bFound) {
4456
        msSetError(MS_PARSEERR, "Unknown COMPOP \"%s\"",
×
4457
                   "loadLayerCompositer()", compop);
4458
        free(compop);
×
4459
        return MS_FAILURE;
×
4460
      }
4461
      free(compop);
14✔
4462
    } break;
14✔
4463
    case END:
4464
      return MS_SUCCESS;
4465
    case OPACITY:
81✔
4466
      if (getInteger(&(compositer->opacity), MS_NUM_CHECK_RANGE, 0, 100) ==
81✔
4467
          -1) {
4468
        msSetError(MS_PARSEERR, "OPACITY must be between 0 and 100 (line %d)",
×
4469
                   "loadLayerCompositer()", msyylineno);
4470
        return MS_FAILURE;
×
4471
      }
4472
      break;
4473
    default:
×
4474
      msSetError(MS_IDENTERR, "Parsing error 2 near (%s):(line %d)",
×
4475
                 "loadLayerCompositer()", msyystring_buffer, msyylineno);
4476
      return (MS_FAILURE);
×
4477
    }
4478
  }
4479
}
4480

4481
static int loadIdentification(layerObj *layer) {
126✔
4482
  for (;;) {
4483
    switch (msyylex()) {
252✔
4484
    case TOLERANCE:
×
4485
      if (getDouble(&(layer->tolerance), MS_NUM_CHECK_GTE, 0, -1) == -1)
×
4486
        return (-1);
4487
      break;
4488

4489
    case TOLERANCEUNITS:
×
4490
      if ((layer->toleranceunits = getSymbol(
×
4491
               8, MS_INCHES, MS_FEET, MS_MILES, MS_METERS, MS_KILOMETERS,
4492
               MS_NAUTICALMILES, MS_DD, MS_PIXELS)) == -1)
4493
        return (-1);
4494
      break;
4495

4496
    case CLASSGROUP:
21✔
4497
      if (getString(&layer->identificationclassgroup) == MS_FAILURE)
21✔
4498
        return (-1);
4499
      break;
4500

4501
    case CLASSAUTO:
105✔
4502
      layer->identificationclassauto = MS_TRUE;
105✔
4503
      break;
105✔
4504

4505
    case END:
4506
      return MS_SUCCESS;
4507

4508
    default:
×
4509
      msSetError(MS_IDENTERR, "Parsing error 2 near (%s):(line %d)",
×
4510
                 "loadIdentification()", msyystring_buffer, msyylineno);
4511
      return (MS_FAILURE);
×
4512
    }
4513
  }
4514
}
4515

4516
int loadLayer(layerObj *layer, mapObj *map) {
13,986✔
4517
  int type;
4518

4519
  layer->map = (mapObj *)map;
13,986✔
4520

4521
  for (;;) {
4522
    switch (msyylex()) {
118,459✔
4523
    case (BINDVALS):
3✔
4524
      if (loadHashTable(&(layer->bindvals)) != MS_SUCCESS)
3✔
4525
        return (-1);
4526
      break;
4527
    case (CLASS):
13,160✔
4528
      if (msGrowLayerClasses(layer) == NULL)
13,160✔
4529
        return (-1);
4530
      initClass(layer->class[layer->numclasses]);
13,160✔
4531
      if (loadClass(layer->class[layer->numclasses], layer) == -1) {
13,160✔
4532
        freeClass(layer->class[layer->numclasses]);
×
4533
        free(layer->class[layer->numclasses]);
×
4534
        layer->class[layer->numclasses] = NULL;
×
4535
        return (-1);
×
4536
      }
4537
      layer->numclasses++;
13,160✔
4538
      break;
13,160✔
4539
    case (CLUSTER):
4✔
4540
      if (loadCluster(&layer->cluster) == -1)
4✔
4541
        return (-1);
4542
      break;
4543
    case (CLASSGROUP):
782✔
4544
      if (getString(&layer->classgroup) == MS_FAILURE)
782✔
4545
        return (-1); /* getString() cleans up previously allocated string */
4546
      break;
4547
    case (CLASSITEM):
3,150✔
4548
      if (getString(&layer->classitem) == MS_FAILURE)
3,150✔
4549
        return (-1); /* getString() cleans up previously allocated string */
4550
      break;
4551
    case (COMPOSITE): {
81✔
4552
      LayerCompositer *compositer = msSmallMalloc(sizeof(LayerCompositer));
81✔
4553
      initLayerCompositer(compositer);
81✔
4554
      if (MS_FAILURE == loadLayerCompositer(compositer)) {
81✔
4555
        freeLayerCompositer(compositer);
×
4556
        return -1;
×
4557
      }
4558
      if (!layer->compositer) {
81✔
4559
        layer->compositer = compositer;
81✔
4560
      } else {
4561
        LayerCompositer *lctmp = layer->compositer;
4562
        while (lctmp->next)
×
4563
          lctmp = lctmp->next;
4564
        lctmp->next = compositer;
×
4565
      }
4566
      break;
4567
    }
4568
    case (CONNECTION):
2,570✔
4569
      if (getString(&layer->connection) == MS_FAILURE)
2,570✔
4570
        return (-1); /* getString() cleans up previously allocated string */
4571
      break;
4572
    case (CONNECTIONTYPE):
2,635✔
4573
      if ((type = getSymbol(14, MS_OGR, MS_POSTGIS, MS_WMS, MS_ORACLESPATIAL,
2,635✔
4574
                            MS_WFS, MS_GRATICULE, MS_PLUGIN, MS_UNION,
4575
                            MS_UVRASTER, MS_CONTOUR, MS_KERNELDENSITY, MS_IDW,
4576
                            MS_FLATGEOBUF, MS_RASTER_LABEL)) == -1)
4577
        return (-1);
4578
      layer->connectiontype = type;
2,635✔
4579
      break;
2,635✔
4580
    case (DATA):
9,615✔
4581
      if (getString(&layer->data) == MS_FAILURE)
9,615✔
4582
        return (-1); /* getString() cleans up previously allocated string */
4583
      break;
4584
    case (DEBUG):
901✔
4585
      if ((layer->debug = getSymbol(3, MS_ON, MS_OFF, MS_NUMBER)) == -1)
901✔
4586
        return (-1);
4587
      if (layer->debug == MS_NUMBER) {
901✔
4588
        if (msCheckNumber(msyynumber, MS_NUM_CHECK_RANGE, 0, 5) == MS_FAILURE) {
61✔
4589
          msSetError(MS_MISCERR,
×
4590
                     "Invalid DEBUG level, must be between 0 and 5 (line %d)",
4591
                     "loadLayer()", msyylineno);
4592
          return (-1);
×
4593
        }
4594
        layer->debug = (int)msyynumber;
61✔
4595
      }
4596
      break;
4597
    case (EOF):
×
4598
      msSetError(MS_EOFERR, NULL, "loadLayer()");
×
4599
      return (-1);
×
4600
      break;
4601
    case (ENCODING):
6✔
4602
      if (getString(&layer->encoding) == MS_FAILURE)
6✔
4603
        return (-1);
4604
      break;
4605
    case (END):
13,986✔
4606
      if ((int)layer->type == -1) {
13,986✔
4607
        msSetError(MS_MISCERR, "Layer type not set.", "loadLayer()");
×
4608
        return (-1);
×
4609
      }
4610

4611
      return (0);
4612
      break;
4613
    case (EXTENT): {
629✔
4614
      if (getDouble(&(layer->extent.minx), MS_NUM_CHECK_NONE, -1, -1) == -1)
629✔
4615
        return (-1);
4616
      if (getDouble(&(layer->extent.miny), MS_NUM_CHECK_NONE, -1, -1) == -1)
629✔
4617
        return (-1);
4618
      if (getDouble(&(layer->extent.maxx), MS_NUM_CHECK_NONE, -1, -1) == -1)
629✔
4619
        return (-1);
4620
      if (getDouble(&(layer->extent.maxy), MS_NUM_CHECK_NONE, -1, -1) == -1)
629✔
4621
        return (-1);
4622
      if (!MS_VALID_EXTENT(layer->extent)) {
629✔
4623
        msSetError(MS_MISCERR,
×
4624
                   "Given layer extent is invalid. Check that it is in the "
4625
                   "form: minx, miny, maxx, maxy",
4626
                   "loadLayer()");
4627
        return (-1);
×
4628
      }
4629
      break;
4630
    }
4631
    case (FEATURE):
2,978✔
4632
      if ((int)layer->type == -1) {
2,978✔
4633
        msSetError(MS_MISCERR,
×
4634
                   "Layer type must be set before defining inline features.",
4635
                   "loadLayer()");
4636
        return (-1);
×
4637
      }
4638

4639
      if (layer->type == MS_LAYER_POLYGON)
2,978✔
4640
        type = MS_SHAPE_POLYGON;
4641
      else if (layer->type == MS_LAYER_LINE)
2,866✔
4642
        type = MS_SHAPE_LINE;
4643
      else
4644
        type = MS_SHAPE_POINT;
4645

4646
      layer->connectiontype = MS_INLINE;
2,978✔
4647

4648
      if (loadFeature(layer, type) == MS_FAILURE)
2,978✔
4649
        return (-1);
4650
      break;
4651
    case (FILTER):
546✔
4652
      if (loadExpression(&(layer->filter)) == -1)
546✔
4653
        return (-1); /* loadExpression() cleans up previously allocated
4654
                        expression */
4655
      break;
4656
    case (FILTERITEM):
221✔
4657
      if (getString(&layer->filteritem) == MS_FAILURE)
221✔
4658
        return (-1); /* getString() cleans up previously allocated string */
4659
      break;
4660
    case (FOOTER):
572✔
4661
      if (getString(&layer->footer) == MS_FAILURE)
572✔
4662
        return (-1); /* getString() cleans up previously allocated string */
4663
      break;
4664
    case (GRID):
8✔
4665
      layer->connectiontype = MS_GRATICULE;
8✔
4666
      if (layer->grid) {
8✔
4667
        freeGrid(layer->grid);
×
4668
        msFree(layer->grid);
×
4669
      }
4670
      layer->grid = (void *)malloc(sizeof(graticuleObj));
8✔
4671
      MS_CHECK_ALLOC(layer->grid, sizeof(graticuleObj), -1);
8✔
4672

4673
      initGrid(layer->grid);
8✔
4674
      loadGrid(layer);
8✔
4675
      break;
8✔
4676
    case (GROUP):
179✔
4677
      if (getString(&layer->group) == MS_FAILURE)
179✔
4678
        return (-1); /* getString() cleans up previously allocated string */
4679
      break;
4680
    case (GEOMTRANSFORM): {
43✔
4681
      if (getSymbol(1, MS_EXPRESSION) == -1)
43✔
4682
        return (MS_FAILURE);
4683
      /* handle expression case here for the moment */
4684
      msFree(layer->_geomtransform.string);
43✔
4685
      layer->_geomtransform.string = msStrdup(msyystring_buffer);
43✔
4686
      layer->_geomtransform.type = MS_GEOMTRANSFORM_EXPRESSION;
43✔
4687
    } break;
43✔
4688
    case (HEADER):
604✔
4689
      if (getString(&layer->header) == MS_FAILURE)
604✔
4690
        return (-1); /* getString() cleans up previously allocated string */
4691
      break;
4692
    case (JOIN):
×
4693
      if (layer->numjoins == MS_MAXJOINS) { /* no room */
×
4694
        msSetError(MS_IDENTERR, "Maximum number of joins reached.",
×
4695
                   "loadLayer()");
4696
        return (-1);
×
4697
      }
4698

4699
      if (loadJoin(&(layer->joins[layer->numjoins])) == -1) {
×
4700
        freeJoin(&(layer->joins[layer->numjoins]));
×
4701
        return (-1);
×
4702
      }
4703
      layer->numjoins++;
×
4704
      break;
×
4705
    case (LABELCACHE):
30✔
4706
      if ((layer->labelcache = getSymbol(2, MS_ON, MS_OFF)) == -1)
30✔
4707
        return (-1);
4708
      break;
4709
    case (LABELITEM):
929✔
4710
      if (getString(&layer->labelitem) == MS_FAILURE)
929✔
4711
        return (-1); /* getString() cleans up previously allocated string */
4712
      break;
4713
    case (LABELMAXSCALE):
×
4714
    case (LABELMAXSCALEDENOM):
4715
      if (getDouble(&(layer->labelmaxscaledenom), MS_NUM_CHECK_GTE, 0, -1) ==
×
4716
          -1)
4717
        return (-1);
4718
      break;
4719
    case (LABELMINSCALE):
×
4720
    case (LABELMINSCALEDENOM):
4721
      if (getDouble(&(layer->labelminscaledenom), MS_NUM_CHECK_GTE, 0, -1) ==
×
4722
          -1)
4723
        return (-1);
4724
      break;
4725
    case (LABELREQUIRES):
5✔
4726
      if (getString(&layer->labelrequires) == MS_FAILURE)
5✔
4727
        return (-1); /* getString() cleans up previously allocated string */
4728
      break;
4729
    case (LAYER):
4730
      break; /* for string loads */
4731
    case (MASK):
51✔
4732
      if (getString(&layer->mask) == MS_FAILURE)
51✔
4733
        return (-1); /* getString() cleans up previously allocated string */
4734
      break;
4735
    case (MAXFEATURES):
2✔
4736
      if (getInteger(&(layer->maxfeatures), MS_NUM_CHECK_GT, 0, -1) == -1)
2✔
4737
        return (-1);
4738
      break;
4739
    case (MAXSCALE):
3✔
4740
    case (MAXSCALEDENOM):
4741
      if (getDouble(&(layer->maxscaledenom), MS_NUM_CHECK_GTE, 0, -1) == -1)
3✔
4742
        return (-1);
4743
      break;
4744
    case (MAXGEOWIDTH):
×
4745
      if (getDouble(&(layer->maxgeowidth), MS_NUM_CHECK_GT, 0, -1) == -1)
×
4746
        return (-1);
4747
      break;
4748
    case (METADATA):
8,107✔
4749
      if (loadHashTable(&(layer->metadata)) != MS_SUCCESS)
8,107✔
4750
        return (-1);
4751
      break;
4752
    case (MINSCALE):
68✔
4753
    case (MINSCALEDENOM):
4754
      if (getDouble(&(layer->minscaledenom), MS_NUM_CHECK_GTE, 0, -1) == -1)
68✔
4755
        return (-1);
4756
      break;
4757
    case (MINGEOWIDTH):
×
4758
      if (getDouble(&(layer->mingeowidth), MS_NUM_CHECK_GT, 0, -1) == -1)
×
4759
        return (-1);
4760
      break;
4761
    case (MINFEATURESIZE):
×
4762
      if (getInteger(&(layer->minfeaturesize), MS_NUM_CHECK_GT, 0, -1) == -1)
×
4763
        return (-1);
4764
      break;
4765
    case (NAME):
13,893✔
4766
      if (getString(&layer->name) == MS_FAILURE)
13,893✔
4767
        return (-1);
4768
      break;
4769
    case (OFFSITE):
35✔
4770
      if (loadColor(&(layer->offsite), NULL) != MS_SUCCESS)
35✔
4771
        return (-1);
4772
      break;
4773

4774
    case (CONNECTIONOPTIONS):
2✔
4775
      if (loadHashTable(&(layer->connectionoptions)) != MS_SUCCESS)
2✔
4776
        return (-1);
4777
      break;
4778
    case (MS_PLUGIN): {
×
4779
      int rv;
4780
      if (map->config) { // value *must* represent a config key
×
4781
        char *value = NULL;
×
4782
        const char *plugin_library = NULL;
4783

4784
        if (getString(&value) == MS_FAILURE)
×
4785
          return (-1);
×
4786
        plugin_library = msConfigGetPlugin(map->config, value);
×
4787
        msFree(value);
×
4788
        if (!plugin_library) {
×
4789
          msSetError(MS_MISCERR,
×
4790
                     "Plugin value not found in config file. See "
4791
                     "mapserver.org/mapfile/config.html for more information.",
4792
                     "loadLayer()");
4793
          return (-1);
×
4794
        }
4795
        msFree(layer->plugin_library_original);
×
4796
        layer->plugin_library_original = strdup(plugin_library);
×
4797
      } else {
4798
        if (getString(&layer->plugin_library_original) == MS_FAILURE)
×
4799
          return (-1);
4800
      }
4801
      rv = msBuildPluginLibraryPath(&layer->plugin_library,
×
4802
                                    layer->plugin_library_original, map);
×
4803
      if (rv == MS_FAILURE)
×
4804
        return (-1);
4805
    } break;
4806
    case (PROCESSING): {
1,439✔
4807
      /* NOTE: processing array maintained as size+1 with NULL terminator.
4808
               This ensure that CSL (GDAL string list) functions can be
4809
               used on the list for easy processing. */
4810
      char *value = NULL;
1,439✔
4811
      if (getString(&value) == MS_FAILURE)
1,439✔
4812
        return (-1);
×
4813
      msLayerAddProcessing(layer, value);
1,439✔
4814
      free(value);
1,439✔
4815
      value = NULL;
4816
    } break;
1,439✔
4817
    case (POSTLABELCACHE):
×
4818
      if ((layer->postlabelcache = getSymbol(2, MS_TRUE, MS_FALSE)) == -1)
×
4819
        return (-1);
4820
      if (layer->postlabelcache)
×
4821
        layer->labelcache = MS_OFF;
×
4822
      break;
4823
    case (PROJECTION):
9,937✔
4824
      if (loadProjection(&(layer->projection), map) == -1)
9,937✔
4825
        return (-1);
4826
      layer->project = MS_TRUE;
9,937✔
4827
      break;
9,937✔
4828
    case (REQUIRES):
10✔
4829
      if (getString(&layer->requires) == MS_FAILURE)
10✔
4830
        return (-1); /* getString() cleans up previously allocated string */
4831
      break;
4832
    case (SCALETOKEN):
78✔
4833
      if (msGrowLayerScaletokens(layer) == NULL)
78✔
4834
        return (-1);
4835
      initScaleToken(&layer->scaletokens[layer->numscaletokens]);
78✔
4836
      if (loadScaletoken(&layer->scaletokens[layer->numscaletokens], layer) ==
78✔
4837
          -1) {
4838
        freeScaleToken(&layer->scaletokens[layer->numscaletokens]);
×
4839
        return (-1);
×
4840
      }
4841
      layer->numscaletokens++;
78✔
4842
      break;
78✔
4843
    case (SIZEUNITS):
256✔
4844
      if ((layer->sizeunits = getSymbol(
256✔
4845
               8, MS_INCHES, MS_FEET, MS_MILES, MS_METERS, MS_KILOMETERS,
4846
               MS_NAUTICALMILES, MS_DD, MS_PIXELS)) == -1)
4847
        return (-1);
4848
      break;
4849
    case (STATUS):
11,888✔
4850
      if ((layer->status = getSymbol(3, MS_ON, MS_OFF, MS_DEFAULT)) == -1)
11,888✔
4851
        return (-1);
4852
      break;
4853
    case (STYLEITEM):
132✔
4854
      if (getString(&layer->styleitem) == MS_FAILURE)
132✔
4855
        return (-1); /* getString() cleans up previously allocated string */
4856
      break;
4857
    case (SYMBOLSCALE):
1✔
4858
    case (SYMBOLSCALEDENOM):
4859
      if (getDouble(&(layer->symbolscaledenom), MS_NUM_CHECK_GTE, 1, -1) == -1)
1✔
4860
        return (-1);
4861
      break;
4862
    case (TEMPLATE):
3,072✔
4863
      if (getString(&layer->template) == MS_FAILURE)
3,072✔
4864
        return (-1); /* getString() cleans up previously allocated string */
4865
      break;
4866
    case (TILEINDEX):
303✔
4867
      if (getString(&layer->tileindex) == MS_FAILURE)
303✔
4868
        return (-1); /* getString() cleans up previously allocated string */
4869
      break;
4870
    case (TILEITEM):
301✔
4871
      if (getString(&layer->tileitem) == MS_FAILURE)
301✔
4872
        return (-1); /* getString() cleans up previously allocated string */
4873
      break;
4874
    case (TILESRS):
13✔
4875
      if (getString(&layer->tilesrs) == MS_FAILURE)
13✔
4876
        return (-1); /* getString() cleans up previously allocated string */
4877
      break;
4878
    case (IDENTIFY):
126✔
4879
      if (loadIdentification(layer) == MS_FAILURE)
126✔
4880
        return (-1);
4881
      break;
4882
    case (TOLERANCE):
68✔
4883
      if (getDouble(&(layer->tolerance), MS_NUM_CHECK_GTE, 0, -1) == -1)
68✔
4884
        return (-1);
4885
      break;
4886
    case (TOLERANCEUNITS):
37✔
4887
      if ((layer->toleranceunits = getSymbol(
37✔
4888
               8, MS_INCHES, MS_FEET, MS_MILES, MS_METERS, MS_KILOMETERS,
4889
               MS_NAUTICALMILES, MS_DD, MS_PIXELS)) == -1)
4890
        return (-1);
4891
      break;
4892
    case (TRANSFORM):
74✔
4893
      if ((layer->transform =
74✔
4894
               getSymbol(11, MS_TRUE, MS_FALSE, MS_UL, MS_UC, MS_UR, MS_CL,
74✔
4895
                         MS_CC, MS_CR, MS_LL, MS_LC, MS_LR)) == -1)
4896
        return (-1);
4897
      break;
4898
    case (TYPE):
13,990✔
4899
      if ((type =
13,990✔
4900
               getSymbol(9, MS_LAYER_POINT, MS_LAYER_LINE, MS_LAYER_RASTER,
13,990✔
4901
                         MS_LAYER_POLYGON, MS_LAYER_ANNOTATION, MS_LAYER_QUERY,
4902
                         MS_LAYER_CIRCLE, MS_LAYER_CHART, TILEINDEX)) == -1)
4903
        return (-1);
4904
      if (type == TILEINDEX)
13,990✔
4905
        type = MS_LAYER_TILEINDEX; /* TILEINDEX is also a parameter */
4906
      if (type == MS_LAYER_ANNOTATION) {
13,986✔
4907
        msSetError(MS_IDENTERR,
×
4908
                   "Annotation Layers have been removed. To obtain same "
4909
                   "functionality, use a layer with label->styles and no "
4910
                   "class->styles",
4911
                   "loadLayer()");
4912
        return -1;
×
4913
      }
4914
      layer->type = type;
13,990✔
4915
      break;
13,990✔
4916
    case (UNITS):
3✔
4917
      if ((layer->units = getSymbol(9, MS_INCHES, MS_FEET, MS_MILES, MS_METERS,
3✔
4918
                                    MS_KILOMETERS, MS_NAUTICALMILES, MS_DD,
4919
                                    MS_PIXELS, MS_PERCENTAGES)) == -1)
4920
        return (-1);
4921
      break;
4922
    case (UTFDATA):
1✔
4923
      if (loadExpression(&(layer->utfdata)) == -1)
1✔
4924
        return (-1); /* loadExpression() cleans up previously allocated
4925
                        expression */
4926
      break;
4927
    case (UTFITEM):
1✔
4928
      if (getString(&layer->utfitem) == MS_FAILURE)
1✔
4929
        return (-1);
4930
      break;
4931
    case (VALIDATION):
904✔
4932
      if (loadHashTable(&(layer->validation)) != MS_SUCCESS)
904✔
4933
        return (-1);
4934
      break;
4935
    default:
×
4936
      if (strlen(msyystring_buffer) > 0) {
×
4937
        msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
4938
                   "loadLayer()", msyystring_buffer, msyylineno);
4939
        return (-1);
×
4940
      } else {
4941
        return (0); /* end of a string, not an error */
4942
      }
4943
    }
4944
  } /* next token */
4945
}
4946

4947
int msUpdateLayerFromString(layerObj *layer, char *string) {
27✔
4948
  int i;
4949

4950
  if (!layer || !string)
27✔
4951
    return MS_FAILURE;
4952

4953
  msAcquireLock(TLOCK_PARSER);
27✔
4954

4955
  msyystate = MS_TOKENIZE_STRING;
27✔
4956
  msyystring = string;
27✔
4957
  msyylex(); /* sets things up, but doesn't process any tokens */
27✔
4958

4959
  msyylineno = 1; /* start at line 1 */
27✔
4960

4961
  if (loadLayer(layer, layer->map) == -1) {
27✔
4962
    msReleaseLock(TLOCK_PARSER);
×
4963
    return MS_FAILURE; /* parse error */
×
4964
    ;
4965
  }
4966

4967
  msyylex_destroy();
27✔
4968
  msReleaseLock(TLOCK_PARSER);
27✔
4969

4970
  /* step through classes to resolve symbol names */
4971
  for (i = 0; i < layer->numclasses; i++) {
27✔
4972
    if (classResolveSymbolNames(layer->class[i]) != MS_SUCCESS)
×
4973
      return MS_FAILURE;
4974
  }
4975

4976
  return MS_SUCCESS;
4977
}
4978

4979
static void writeCompositingFilter(FILE *stream, int indent,
4980
                                   CompositingFilter *filter) {
4981
  while (filter) {
×
4982
    writeString(stream, indent, "COMPFILTER", "", filter->filter);
×
4983
    filter = filter->next;
×
4984
  }
4985
}
4986

4987
static void writeLayerCompositer(FILE *stream, int indent,
7✔
4988
                                 LayerCompositer *compositor) {
4989
  indent++;
7✔
4990
  while (compositor) {
7✔
4991
    writeBlockBegin(stream, indent, "COMPOSITE");
×
4992
    writeCompositingFilter(stream, indent, compositor->filter);
×
4993
    if (compositor->comp_op != MS_COMPOP_SRC_OVER) {
×
4994

4995
      bool bFound = false;
4996
      for (int i = 0; i < SIZEOF_COMP_OPS; ++i) {
×
4997
        if (compositor->comp_op == CompOps[i].eOp) {
×
4998
          bFound = true;
4999
          writeString(stream, indent, "COMPOP", NULL, CompOps[i].pszName);
×
5000
          break;
×
5001
        }
5002
      }
5003
      if (!bFound) {
5004
        assert(0);
5005
      }
5006
    }
5007
    writeNumber(stream, indent, "OPACITY", 100, compositor->opacity);
×
5008
    writeBlockEnd(stream, indent, "COMPOSITE");
×
5009
    compositor = compositor->next;
×
5010
  }
5011
}
7✔
5012
static void writeLayer(FILE *stream, int indent, layerObj *layer) {
7✔
5013
  int i;
5014
  featureListNodeObjPtr current = NULL;
5015

5016
  if (layer->status == MS_DELETE)
7✔
5017
    return;
5018

5019
  indent++;
7✔
5020
  writeBlockBegin(stream, indent, "LAYER");
7✔
5021
  writeHashTable(stream, indent, "BINDVALS", &(layer->bindvals));
7✔
5022
  /* class - see below */
5023
  writeString(stream, indent, "CLASSGROUP", NULL, layer->classgroup);
7✔
5024
  writeString(stream, indent, "CLASSITEM", NULL, layer->classitem);
7✔
5025
  writeCluster(stream, indent, &(layer->cluster));
7✔
5026
  writeLayerCompositer(stream, indent, layer->compositer);
7✔
5027
  writeString(stream, indent, "CONNECTION", NULL, layer->connection);
7✔
5028
  writeKeyword(stream, indent, "CONNECTIONTYPE", layer->connectiontype, 12,
7✔
5029
               MS_OGR, "OGR", MS_POSTGIS, "POSTGIS", MS_WMS, "WMS",
5030
               MS_ORACLESPATIAL, "ORACLESPATIAL", MS_WFS, "WFS", MS_PLUGIN,
5031
               "PLUGIN", MS_UNION, "UNION", MS_UVRASTER, "UVRASTER", MS_CONTOUR,
5032
               "CONTOUR", MS_KERNELDENSITY, "KERNELDENSITY", MS_IDW, "IDW",
5033
               MS_FLATGEOBUF, "FLATGEOBUF");
5034
  writeHashTable(stream, indent, "CONNECTIONOPTIONS",
7✔
5035
                 &(layer->connectionoptions));
5036
  writeString(stream, indent, "DATA", NULL, layer->data);
7✔
5037
  writeNumber(stream, indent, "DEBUG", 0,
7✔
5038
              layer->debug); /* is this right? see loadLayer() */
7✔
5039
  writeString(stream, indent, "ENCODING", NULL, layer->encoding);
7✔
5040
  writeExtent(stream, indent, "EXTENT", layer->extent);
7✔
5041
  writeExpression(stream, indent, "FILTER", &(layer->filter));
7✔
5042
  writeString(stream, indent, "FILTERITEM", NULL, layer->filteritem);
7✔
5043
  writeString(stream, indent, "FOOTER", NULL, layer->footer);
7✔
5044
  writeString(stream, indent, "GROUP", NULL, layer->group);
7✔
5045

5046
  if (layer->_geomtransform.type == MS_GEOMTRANSFORM_EXPRESSION) {
7✔
5047
    writeIndent(stream, indent + 1);
×
5048
    fprintf(stream, "GEOMTRANSFORM (%s)\n", layer->_geomtransform.string);
×
5049
  }
5050

5051
  writeString(stream, indent, "HEADER", NULL, layer->header);
7✔
5052
  /* join - see below */
5053
  writeKeyword(stream, indent, "LABELCACHE", layer->labelcache, 1, MS_OFF,
7✔
5054
               "OFF");
5055
  writeString(stream, indent, "LABELITEM", NULL, layer->labelitem);
7✔
5056
  writeNumber(stream, indent, "LABELMAXSCALEDENOM", -1,
7✔
5057
              layer->labelmaxscaledenom);
5058
  writeNumber(stream, indent, "LABELMINSCALEDENOM", -1,
7✔
5059
              layer->labelminscaledenom);
5060
  writeString(stream, indent, "LABELREQUIRES", NULL, layer->labelrequires);
7✔
5061
  writeNumber(stream, indent, "MAXFEATURES", -1, layer->maxfeatures);
7✔
5062
  writeNumber(stream, indent, "MAXGEOWIDTH", -1, layer->maxgeowidth);
7✔
5063
  writeNumber(stream, indent, "MAXSCALEDENOM", -1, layer->maxscaledenom);
7✔
5064
  writeString(stream, indent, "MASK", NULL, layer->mask);
7✔
5065
  writeHashTable(stream, indent, "METADATA", &(layer->metadata));
7✔
5066
  writeNumber(stream, indent, "MINGEOWIDTH", -1, layer->mingeowidth);
7✔
5067
  writeNumber(stream, indent, "MINSCALEDENOM", -1, layer->minscaledenom);
7✔
5068
  writeNumber(stream, indent, "MINFEATURESIZE", -1, layer->minfeaturesize);
7✔
5069
  writeString(stream, indent, "NAME", NULL, layer->name);
7✔
5070
  writeColor(stream, indent, "OFFSITE", NULL, &(layer->offsite));
7✔
5071
  writeString(stream, indent, "PLUGIN", NULL, layer->plugin_library_original);
7✔
5072
  writeKeyword(stream, indent, "POSTLABELCACHE", layer->postlabelcache, 1,
7✔
5073
               MS_TRUE, "TRUE");
5074
  for (i = 0; layer->processing && layer->processing[i]; i++)
8✔
5075
    writeString(stream, indent, "PROCESSING", NULL, layer->processing[i]);
1✔
5076
  writeKeyword(stream, indent, "PROCESSING", layer->rendermode, 1,
7✔
5077
               MS_ALL_MATCHING_CLASSES, "\"RENDERMODE=ALL_MATCHING_CLASSES\"");
5078
  writeProjection(stream, indent, &(layer->projection));
7✔
5079
  writeString(stream, indent, "REQUIRES", NULL, layer->requires);
7✔
5080
  writeKeyword(stream, indent, "SIZEUNITS", layer->sizeunits, 7, MS_INCHES,
7✔
5081
               "INCHES", MS_FEET, "FEET", MS_MILES, "MILES", MS_METERS,
5082
               "METERS", MS_KILOMETERS, "KILOMETERS", MS_NAUTICALMILES,
5083
               "NAUTICALMILES", MS_DD, "DD");
5084
  writeKeyword(stream, indent, "STATUS", layer->status, 3, MS_ON, "ON", MS_OFF,
7✔
5085
               "OFF", MS_DEFAULT, "DEFAULT");
5086
  writeString(stream, indent, "STYLEITEM", NULL, layer->styleitem);
7✔
5087
  writeNumber(stream, indent, "SYMBOLSCALEDENOM", -1, layer->symbolscaledenom);
7✔
5088
  writeString(stream, indent, "TEMPLATE", NULL, layer->template);
7✔
5089
  writeString(stream, indent, "TILEINDEX", NULL, layer->tileindex);
7✔
5090
  writeString(stream, indent, "TILEITEM", NULL, layer->tileitem);
7✔
5091
  writeString(stream, indent, "TILESRS", NULL, layer->tilesrs);
7✔
5092
  writeNumber(stream, indent, "TOLERANCE", -1, layer->tolerance);
7✔
5093
  writeKeyword(stream, indent, "TOLERANCEUNITS", layer->toleranceunits, 7,
7✔
5094
               MS_INCHES, "INCHES", MS_FEET, "FEET", MS_MILES, "MILES",
5095
               MS_METERS, "METERS", MS_KILOMETERS, "KILOMETERS",
5096
               MS_NAUTICALMILES, "NAUTICALMILES", MS_DD, "DD");
5097
  writeIdentify(stream, indent, layer);
7✔
5098
  writeKeyword(stream, indent, "TRANSFORM", layer->transform, 10, MS_FALSE,
7✔
5099
               "FALSE", MS_UL, "UL", MS_UC, "UC", MS_UR, "UR", MS_CL, "CL",
5100
               MS_CC, "CC", MS_CR, "CR", MS_LL, "LL", MS_LC, "LC", MS_LR, "LR");
5101
  writeKeyword(stream, indent, "TYPE", layer->type, 9, MS_LAYER_POINT, "POINT",
7✔
5102
               MS_LAYER_LINE, "LINE", MS_LAYER_POLYGON, "POLYGON",
5103
               MS_LAYER_RASTER, "RASTER", MS_LAYER_ANNOTATION, "ANNOTATION",
5104
               MS_LAYER_QUERY, "QUERY", MS_LAYER_CIRCLE, "CIRCLE",
5105
               MS_LAYER_TILEINDEX, "TILEINDEX", MS_LAYER_CHART, "CHART");
5106
  writeKeyword(stream, indent, "UNITS", layer->units, 9, MS_INCHES, "INCHES",
7✔
5107
               MS_FEET, "FEET", MS_MILES, "MILES", MS_METERS, "METERS",
5108
               MS_KILOMETERS, "KILOMETERS", MS_NAUTICALMILES, "NAUTICALMILES",
5109
               MS_DD, "DD", MS_PIXELS, "PIXELS", MS_PERCENTAGES,
5110
               "PERCENTATGES");
5111
  writeExpression(stream, indent, "UTFDATA", &(layer->utfdata));
7✔
5112
  writeString(stream, indent, "UTFITEM", NULL, layer->utfitem);
7✔
5113
  writeHashTable(stream, indent, "VALIDATION", &(layer->validation));
7✔
5114

5115
  /* write potentially multiply occurring objects last */
5116
  for (i = 0; i < layer->numscaletokens; i++)
7✔
5117
    writeScaleToken(stream, indent, &(layer->scaletokens[i]));
×
5118
  for (i = 0; i < layer->numjoins; i++)
7✔
5119
    writeJoin(stream, indent, &(layer->joins[i]));
×
5120
  for (i = 0; i < layer->numclasses; i++)
16✔
5121
    writeClass(stream, indent, layer->class[i]);
9✔
5122

5123
  if (layer->grid && layer->connectiontype == MS_GRATICULE)
7✔
5124
    writeGrid(stream, indent, layer->grid);
×
5125
  else {
5126
    current = layer->features;
7✔
5127
    while (current != NULL) {
10✔
5128
      writeFeature(stream, indent, &(current->shape));
3✔
5129
      current = current->next;
3✔
5130
    }
5131
  }
5132

5133
  writeBlockEnd(stream, indent, "LAYER");
7✔
5134
  writeLineFeed(stream);
5135
}
5136

5137
char *msWriteLayerToString(layerObj *layer) {
×
5138
  msIOContext context;
5139
  msIOBuffer buffer;
5140

5141
  context.label = NULL;
×
5142
  context.write_channel = MS_TRUE;
×
5143
  context.readWriteFunc = msIO_bufferWrite;
×
5144
  context.cbData = &buffer;
×
5145
  buffer.data = NULL;
×
5146
  buffer.data_len = 0;
×
5147
  buffer.data_offset = 0;
×
5148

5149
  msIO_installHandlers(NULL, &context, NULL);
×
5150

5151
  writeLayer(stdout, -1, layer);
×
5152
  msIO_bufferWrite(&buffer, "", 1);
×
5153

5154
  msIO_installHandlers(NULL, NULL, NULL);
×
5155

5156
  return (char *)buffer.data;
×
5157
}
5158

5159
/*
5160
** Initialize, load and free a referenceMapObj structure
5161
*/
5162
void initReferenceMap(referenceMapObj *ref) {
3,518✔
5163
  ref->height = ref->width = 0;
3,518✔
5164
  ref->extent.minx = ref->extent.miny = ref->extent.maxx = ref->extent.maxy =
3,518✔
5165
      -1.0;
5166
  ref->image = NULL;
3,518✔
5167
  MS_INIT_COLOR(ref->color, 255, 0, 0, 255);
3,518✔
5168
  MS_INIT_COLOR(ref->outlinecolor, 0, 0, 0, 255);
3,518✔
5169
  ref->status = MS_OFF;
3,518✔
5170
  ref->marker = 0;
3,518✔
5171
  ref->markername = NULL;
3,518✔
5172
  ref->markersize = 0;
3,518✔
5173
  ref->minboxsize = 3;
3,518✔
5174
  ref->maxboxsize = 0;
3,518✔
5175
  ref->map = NULL;
3,518✔
5176
}
3,518✔
5177

5178
void freeReferenceMap(referenceMapObj *ref) {
3,470✔
5179
  msFree(ref->image);
3,470✔
5180
  msFree(ref->markername);
3,470✔
5181
}
3,470✔
5182

5183
int loadReferenceMap(referenceMapObj *ref, mapObj *map) {
×
5184
  int state;
5185

5186
  ref->map = (mapObj *)map;
×
5187

5188
  for (;;) {
5189
    switch (msyylex()) {
×
5190
    case (EOF):
×
5191
      msSetError(MS_EOFERR, NULL, "loadReferenceMap()");
×
5192
      return (-1);
×
5193
    case (END):
×
5194
      if (!ref->image) {
×
5195
        msSetError(MS_MISCERR, "No image defined for the reference map.",
×
5196
                   "loadReferenceMap()");
5197
        return (-1);
×
5198
      }
5199
      if (ref->width == 0 || ref->height == 0) {
×
5200
        msSetError(MS_MISCERR, "No image size defined for the reference map.",
×
5201
                   "loadReferenceMap()");
5202
        return (-1);
×
5203
      }
5204
      return (0);
5205
      break;
5206
    case (COLOR):
×
5207
      if (loadColor(&(ref->color), NULL) != MS_SUCCESS)
×
5208
        return (-1);
5209
      break;
5210
    case (EXTENT):
×
5211
      if (getDouble(&(ref->extent.minx), MS_NUM_CHECK_NONE, -1, -1) == -1)
×
5212
        return (-1);
5213
      if (getDouble(&(ref->extent.miny), MS_NUM_CHECK_NONE, -1, -1) == -1)
×
5214
        return (-1);
5215
      if (getDouble(&(ref->extent.maxx), MS_NUM_CHECK_NONE, -1, -1) == -1)
×
5216
        return (-1);
5217
      if (getDouble(&(ref->extent.maxy), MS_NUM_CHECK_NONE, -1, -1) == -1)
×
5218
        return (-1);
5219
      if (!MS_VALID_EXTENT(ref->extent)) {
×
5220
        msSetError(MS_MISCERR,
×
5221
                   "Given reference extent is invalid. Check that it "
5222
                   "is in the form: minx, miny, maxx, maxy",
5223
                   "loadReferenceMap()");
5224
        return (-1);
×
5225
      }
5226
      break;
5227
    case (IMAGE):
×
5228
      if (getString(&ref->image) == MS_FAILURE)
×
5229
        return (-1);
5230
      break;
5231
    case (OUTLINECOLOR):
×
5232
      if (loadColor(&(ref->outlinecolor), NULL) != MS_SUCCESS)
×
5233
        return (-1);
5234
      break;
5235
    case (SIZE):
×
5236
      if (getInteger(&(ref->width), MS_NUM_CHECK_RANGE, 5, ref->map->maxsize) ==
×
5237
          -1)
5238
        return (-1); // is 5 reasonable?
5239
      if (getInteger(&(ref->height), MS_NUM_CHECK_RANGE, 5,
×
5240
                     ref->map->maxsize) == -1)
×
5241
        return (-1);
5242
      break;
5243
    case (STATUS):
×
5244
      if ((ref->status = getSymbol(2, MS_ON, MS_OFF)) == -1)
×
5245
        return (-1);
5246
      break;
5247
    case (MARKER):
×
5248
      if ((state = getSymbol(2, MS_NUMBER, MS_STRING)) == -1)
×
5249
        return (-1);
5250

5251
      if (state == MS_NUMBER) {
×
5252
        if (msCheckNumber(msyynumber, MS_NUM_CHECK_GTE, 0, -1) == MS_FAILURE) {
×
5253
          msSetError(MS_MISCERR,
×
5254
                     "Invalid MARKER, must be greater than 0 (line %d)",
5255
                     "loadReferenceMap()", msyylineno);
5256
          return (-1);
×
5257
        }
5258
        ref->marker = (int)msyynumber;
×
5259
      } else {
5260
        if (ref->markername != NULL)
×
5261
          msFree(ref->markername);
×
5262
        ref->markername = msStrdup(msyystring_buffer);
×
5263
      }
5264
      break;
5265
    case (MARKERSIZE):
×
5266
      if (getInteger(&(ref->markersize), MS_NUM_CHECK_GT, 0, -1) == -1)
×
5267
        return (-1);
5268
      break;
5269
    case (MINBOXSIZE):
×
5270
      if (getInteger(&(ref->minboxsize), MS_NUM_CHECK_GT, 0, -1) == -1)
×
5271
        return (-1);
5272
      break;
5273
    case (MAXBOXSIZE):
×
5274
      if (getInteger(&(ref->maxboxsize), MS_NUM_CHECK_GT, 0, -1) == -1)
×
5275
        return (-1);
5276
      break;
5277
    case (REFERENCE):
5278
      break; /* for string loads */
5279
    default:
×
5280
      if (strlen(msyystring_buffer) > 0) {
×
5281
        msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
5282
                   "loadReferenceMap()", msyystring_buffer, msyylineno);
5283
        return (-1);
×
5284
      } else {
5285
        return (0); /* end of a string, not an error */
5286
      }
5287
    }
5288
  } /* next token */
5289
}
5290

5291
int msUpdateReferenceMapFromString(referenceMapObj *ref, char *string) {
×
5292
  if (!ref || !string)
×
5293
    return MS_FAILURE;
5294

5295
  msAcquireLock(TLOCK_PARSER);
×
5296

5297
  msyystate = MS_TOKENIZE_STRING;
×
5298
  msyystring = string;
×
5299
  msyylex(); /* sets things up, but doesn't process any tokens */
×
5300

5301
  msyylineno = 1; /* start at line 1 */
×
5302

5303
  if (loadReferenceMap(ref, ref->map) == -1) {
×
5304
    msReleaseLock(TLOCK_PARSER);
×
5305
    return MS_FAILURE; /* parse error */
×
5306
    ;
5307
  }
5308

5309
  msyylex_destroy();
×
5310
  msReleaseLock(TLOCK_PARSER);
×
5311
  return MS_SUCCESS;
×
5312
}
5313

5314
static void writeReferenceMap(FILE *stream, int indent, referenceMapObj *ref) {
1✔
5315
  colorObj c;
5316

5317
  if (!ref->image)
1✔
5318
    return;
1✔
5319

5320
  indent++;
×
5321
  writeBlockBegin(stream, indent, "REFERENCE");
×
5322
  MS_INIT_COLOR(c, 255, 0, 0, 255);
×
5323
  writeColor(stream, indent, "COLOR", &c, &(ref->color));
×
5324
  writeExtent(stream, indent, "EXTENT", ref->extent);
×
5325
  writeString(stream, indent, "IMAGE", NULL, ref->image);
×
5326
  MS_INIT_COLOR(c, 0, 0, 0, 255);
×
5327
  writeColor(stream, indent, "OUTLINECOLOR", &c, &(ref->outlinecolor));
×
5328
  writeDimension(stream, indent, "SIZE", ref->width, ref->height, NULL, NULL);
×
5329
  writeKeyword(stream, indent, "STATUS", ref->status, 2, MS_ON, "ON", MS_OFF,
×
5330
               "OFF");
5331
  writeNumberOrString(stream, indent, "MARKER", 0, ref->marker,
×
5332
                      ref->markername);
5333
  writeNumber(stream, indent, "MARKERSIZE", -1, ref->markersize);
×
5334
  writeNumber(stream, indent, "MAXBOXSIZE", -1, ref->maxboxsize);
×
5335
  writeNumber(stream, indent, "MINBOXSIZE", -1, ref->minboxsize);
×
5336
  writeBlockEnd(stream, indent, "REFERENCE");
×
5337
  writeLineFeed(stream);
5338
}
5339

5340
char *msWriteReferenceMapToString(referenceMapObj *ref) {
×
5341
  msIOContext context;
5342
  msIOBuffer buffer;
5343

5344
  context.label = NULL;
×
5345
  context.write_channel = MS_TRUE;
×
5346
  context.readWriteFunc = msIO_bufferWrite;
×
5347
  context.cbData = &buffer;
×
5348
  buffer.data = NULL;
×
5349
  buffer.data_len = 0;
×
5350
  buffer.data_offset = 0;
×
5351

5352
  msIO_installHandlers(NULL, &context, NULL);
×
5353

5354
  writeReferenceMap(stdout, -1, ref);
×
5355
  msIO_bufferWrite(&buffer, "", 1);
×
5356

5357
  msIO_installHandlers(NULL, NULL, NULL);
×
5358

5359
  return (char *)buffer.data;
×
5360
}
5361

5362
#define MAX_FORMATOPTIONS 100
5363

5364
static int loadOutputFormat(mapObj *map) {
1,399✔
5365
  char *name = NULL;
5366
  char *mimetype = NULL;
1,399✔
5367
  char *driver = NULL;
5368
  char *extension = NULL;
1,399✔
5369
  int imagemode = MS_NOOVERRIDE;
5370
  int transparent = MS_NOOVERRIDE;
5371
  char *formatoptions[MAX_FORMATOPTIONS];
5372
  int numformatoptions = 0;
5373
  char *value = NULL;
1,399✔
5374

5375
  for (;;) {
5376
    switch (msyylex()) {
8,911✔
5377
    case (EOF):
×
5378
      msSetError(MS_EOFERR, NULL, "loadOutputFormat()");
×
5379
      goto load_output_error;
×
5380

5381
    case (END): {
1,399✔
5382
      outputFormatObj *format;
5383

5384
      if (driver == NULL) {
1,399✔
5385
        msSetError(MS_MISCERR,
×
5386
                   "OUTPUTFORMAT clause lacks DRIVER keyword near (%s):(%d)",
5387
                   "loadOutputFormat()", msyystring_buffer, msyylineno);
5388
        goto load_output_error;
×
5389
      }
5390

5391
      format = msCreateDefaultOutputFormat(map, driver, name, NULL);
1,399✔
5392
      if (format == NULL) {
1,399✔
5393
        msSetError(MS_MISCERR,
2✔
5394
                   "OUTPUTFORMAT (%s) clause references driver (%s), but this "
5395
                   "driver isn't configured.",
5396
                   "loadOutputFormat()", name, driver);
5397
        goto load_output_error;
2✔
5398
      }
5399
      msFree(name);
1,397✔
5400
      name = NULL;
5401
      msFree(driver);
1,397✔
5402
      driver = NULL;
5403

5404
      if (transparent != MS_NOOVERRIDE)
1,397✔
5405
        format->transparent = transparent;
122✔
5406
      if (extension != NULL) {
1,397✔
5407
        msFree(format->extension);
717✔
5408
        format->extension = extension;
717✔
5409
        extension = NULL;
717✔
5410
      }
5411
      if (mimetype != NULL) {
1,397✔
5412
        msFree(format->mimetype);
1,161✔
5413
        format->mimetype = mimetype;
1,161✔
5414
        mimetype = NULL;
1,161✔
5415
      }
5416
      if (imagemode != MS_NOOVERRIDE) {
1,397✔
5417
        if (format->renderer != MS_RENDER_WITH_AGG ||
758✔
5418
            imagemode != MS_IMAGEMODE_PC256) {
5419
          /* don't force to PC256 with agg, this can happen when using mapfile
5420
           * defined GD outputformats that are now falling back to agg/png8
5421
           */
5422
          format->imagemode = imagemode;
749✔
5423
        }
5424

5425
        if (transparent == MS_NOOVERRIDE) {
758✔
5426
          if (imagemode == MS_IMAGEMODE_RGB)
680✔
5427
            format->transparent = MS_FALSE;
59✔
5428
          else if (imagemode == MS_IMAGEMODE_RGBA)
621✔
5429
            format->transparent = MS_TRUE;
24✔
5430
        }
5431
        if (format->imagemode == MS_IMAGEMODE_INT16 ||
758✔
5432
            format->imagemode == MS_IMAGEMODE_FLOAT32 ||
758✔
5433
            format->imagemode == MS_IMAGEMODE_BYTE)
5434
          format->renderer = MS_RENDER_WITH_RAWDATA;
588✔
5435
      }
5436
      while (numformatoptions--) {
3,379✔
5437
        char *key = strchr(formatoptions[numformatoptions], '=');
1,982✔
5438
        if (!key || !*(key + 1)) {
1,982✔
5439
          msSetError(
×
5440
              MS_MISCERR,
5441
              "Failed to parse FORMATOPTION, expecting \"KEY=VALUE\" syntax.",
5442
              "loadOutputFormat()");
5443
          goto load_output_error;
×
5444
        }
5445
        *key = 0;
1,982✔
5446
        key++;
1,982✔
5447
        msSetOutputFormatOption(format, formatoptions[numformatoptions], key);
1,982✔
5448
        free(formatoptions[numformatoptions]);
1,982✔
5449
      }
5450

5451
      format->inmapfile = MS_TRUE;
1,397✔
5452

5453
      msOutputFormatValidate(format, MS_FALSE);
1,397✔
5454
      return (0);
1,397✔
5455
    }
5456
    case (NAME):
1,371✔
5457
      msFree(name);
1,371✔
5458
      if ((name = getToken()) == NULL)
1,371✔
5459
        goto load_output_error;
×
5460
      break;
5461
    case (MIMETYPE):
1,163✔
5462
      if (getString(&mimetype) == MS_FAILURE)
1,163✔
5463
        goto load_output_error;
×
5464
      break;
5465
    case (DRIVER): {
1,399✔
5466
      int s;
5467
      if ((s = getSymbol(2, MS_STRING, TEMPLATE)) ==
1,399✔
5468
          -1) /* allow the template to be quoted or not in the mapfile */
5469
        goto load_output_error;
×
5470
      free(driver);
1,399✔
5471
      if (s == MS_STRING)
1,399✔
5472
        driver = msStrdup(msyystring_buffer);
1,399✔
5473
      else
5474
        driver = msStrdup("TEMPLATE");
×
5475
    } break;
5476
    case (EXTENSION):
717✔
5477
      if (getString(&extension) == MS_FAILURE)
717✔
5478
        goto load_output_error;
×
5479
      if (extension[0] == '.') {
717✔
5480
        char *temp = msStrdup(extension + 1);
×
5481
        free(extension);
×
5482
        extension = temp;
×
5483
      }
5484
      break;
5485
    case (FORMATOPTION):
1,982✔
5486
      if (getString(&value) == MS_FAILURE)
1,982✔
5487
        goto load_output_error;
×
5488
      if (numformatoptions < MAX_FORMATOPTIONS)
1,982✔
5489
        formatoptions[numformatoptions++] = value;
1,982✔
5490
      value = NULL;
1,982✔
5491
      break;
1,982✔
5492
    case (IMAGEMODE):
5493
      value = getToken();
758✔
5494
      if (strcasecmp(value, "PC256") == 0)
758✔
5495
        imagemode = MS_IMAGEMODE_PC256;
5496
      else if (strcasecmp(value, "RGB") == 0)
749✔
5497
        imagemode = MS_IMAGEMODE_RGB;
5498
      else if (strcasecmp(value, "RGBA") == 0)
643✔
5499
        imagemode = MS_IMAGEMODE_RGBA;
5500
      else if (strcasecmp(value, "INT16") == 0)
588✔
5501
        imagemode = MS_IMAGEMODE_INT16;
5502
      else if (strcasecmp(value, "FLOAT32") == 0)
329✔
5503
        imagemode = MS_IMAGEMODE_FLOAT32;
5504
      else if (strcasecmp(value, "BYTE") == 0)
307✔
5505
        imagemode = MS_IMAGEMODE_BYTE;
5506
      else if (strcasecmp(value, "FEATURE") == 0)
×
5507
        imagemode = MS_IMAGEMODE_FEATURE;
5508
      else {
5509
        msSetError(MS_IDENTERR,
×
5510
                   "Parsing error near (%s):(line %d), expected PC256, RGB, "
5511
                   "RGBA, FEATURE, BYTE, INT16, or FLOAT32 for IMAGEMODE.",
5512
                   "loadOutputFormat()", msyystring_buffer, msyylineno);
5513
        goto load_output_error;
×
5514
      }
5515
      free(value);
758✔
5516
      value = NULL;
758✔
5517
      break;
758✔
5518
    case (TRANSPARENT):
122✔
5519
      if ((transparent = getSymbol(2, MS_ON, MS_OFF)) == -1)
122✔
5520
        goto load_output_error;
×
5521
      break;
5522
    default:
×
5523
      msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
5524
                 "loadOutputFormat()", msyystring_buffer, msyylineno);
5525
      goto load_output_error;
×
5526
    }
5527
  } /* next token */
5528
load_output_error:
2✔
5529
  msFree(driver);
2✔
5530
  msFree(extension);
2✔
5531
  msFree(mimetype);
2✔
5532
  msFree(name);
2✔
5533
  msFree(value);
2✔
5534
  return -1;
2✔
5535
}
5536

5537
/*
5538
** utility function to write an output format structure to file
5539
*/
5540
static void writeOutputformatobject(FILE *stream, int indent,
1✔
5541
                                    outputFormatObj *outputformat) {
5542
  int i = 0;
5543
  if (!outputformat)
1✔
5544
    return;
5545

5546
  indent++;
1✔
5547
  writeBlockBegin(stream, indent, "OUTPUTFORMAT");
1✔
5548
  writeString(stream, indent, "NAME", NULL, outputformat->name);
1✔
5549
  writeString(stream, indent, "MIMETYPE", NULL, outputformat->mimetype);
1✔
5550
  writeString(stream, indent, "DRIVER", NULL, outputformat->driver);
1✔
5551
  writeString(stream, indent, "EXTENSION", NULL, outputformat->extension);
1✔
5552
  writeKeyword(stream, indent, "IMAGEMODE", outputformat->imagemode, 7,
1✔
5553
               MS_IMAGEMODE_PC256, "PC256", MS_IMAGEMODE_RGB, "RGB",
5554
               MS_IMAGEMODE_RGBA, "RGBA", MS_IMAGEMODE_INT16, "INT16",
5555
               MS_IMAGEMODE_FLOAT32, "FLOAT32", MS_IMAGEMODE_BYTE, "BYTE",
5556
               MS_IMAGEMODE_FEATURE, "FEATURE");
5557
  writeKeyword(stream, indent, "TRANSPARENT", outputformat->transparent, 2,
1✔
5558
               MS_TRUE, "TRUE", MS_FALSE, "FALSE");
5559
  for (i = 0; i < outputformat->numformatoptions; i++)
1✔
5560
    writeString(stream, indent, "FORMATOPTION", NULL,
×
5561
                outputformat->formatoptions[i]);
×
5562
  writeBlockEnd(stream, indent, "OUTPUTFORMAT");
1✔
5563
  writeLineFeed(stream);
5564
}
5565

5566
/*
5567
** Write the output formats to file
5568
*/
5569
static void writeOutputformat(FILE *stream, int indent, mapObj *map) {
1✔
5570
  int i = 0;
5571
  if (!map->outputformat)
1✔
5572
    return;
5573

5574
  writeOutputformatobject(stream, indent, map->outputformat);
1✔
5575
  for (i = 0; i < map->numoutputformats; i++) {
3✔
5576
    if (map->outputformatlist[i]->inmapfile == MS_TRUE &&
2✔
5577
        strcmp(map->outputformatlist[i]->name, map->outputformat->name) != 0)
1✔
5578
      writeOutputformatobject(stream, indent, map->outputformatlist[i]);
×
5579
  }
5580
}
5581

5582
/*
5583
** Initialize, load and free a legendObj structure
5584
*/
5585
void initLegend(legendObj *legend) {
3,479✔
5586
  legend->height = legend->width = 0;
3,479✔
5587
  MS_INIT_COLOR(legend->imagecolor, 255, 255, 255, 255); /* white */
3,479✔
5588
  MS_INIT_COLOR(legend->outlinecolor, -1, -1, -1, 255);
3,479✔
5589
  initLabel(&legend->label);
3,479✔
5590
  legend->label.position = MS_XY; /* override */
3,479✔
5591
  legend->keysizex = 20;
3,479✔
5592
  legend->keysizey = 10;
3,479✔
5593
  legend->keyspacingx = 5;
3,479✔
5594
  legend->keyspacingy = 5;
3,479✔
5595
  legend->status = MS_OFF;
3,479✔
5596
  legend->transparent = MS_NOOVERRIDE;
3,479✔
5597
  legend->position = MS_LL;
3,479✔
5598
  legend->postlabelcache = MS_FALSE; /* draw with labels */
3,479✔
5599
  legend->template = NULL;
3,479✔
5600
  legend->map = NULL;
3,479✔
5601
}
3,479✔
5602

5603
void freeLegend(legendObj *legend) {
3,470✔
5604
  if (legend->template)
3,470✔
5605
    free(legend->template);
1✔
5606
  freeLabel(&(legend->label));
3,470✔
5607
}
3,470✔
5608

5609
int loadLegend(legendObj *legend, mapObj *map) {
480✔
5610
  legend->map = (mapObj *)map;
480✔
5611

5612
  for (;;) {
5613
    switch (msyylex()) {
1,037✔
5614
    case (EOF):
×
5615
      msSetError(MS_EOFERR, NULL, "loadLegend()");
×
5616
      return (-1);
×
5617
    case (END):
480✔
5618
      legend->label.position = MS_XY; /* overrides go here */
480✔
5619
      return (0);
480✔
5620
      break;
5621
    case (IMAGECOLOR):
423✔
5622
      if (loadColor(&(legend->imagecolor), NULL) != MS_SUCCESS)
423✔
5623
        return (-1);
5624
      break;
5625
    case (KEYSIZE):
4✔
5626
      if (getInteger(&(legend->keysizex), MS_NUM_CHECK_RANGE,
4✔
5627
                     MS_LEGEND_KEYSIZE_MIN, MS_LEGEND_KEYSIZE_MAX) == -1)
5628
        return (-1);
5629
      if (getInteger(&(legend->keysizey), MS_NUM_CHECK_RANGE,
4✔
5630
                     MS_LEGEND_KEYSIZE_MIN, MS_LEGEND_KEYSIZE_MAX) == -1)
5631
        return (-1);
5632
      break;
5633
    case (KEYSPACING):
4✔
5634
      if (getInteger(&(legend->keyspacingx), MS_NUM_CHECK_RANGE,
4✔
5635
                     MS_LEGEND_KEYSPACING_MIN, MS_LEGEND_KEYSPACING_MAX) == -1)
5636
        return (-1);
5637
      if (getInteger(&(legend->keyspacingy), MS_NUM_CHECK_RANGE,
4✔
5638
                     MS_LEGEND_KEYSPACING_MIN, MS_LEGEND_KEYSPACING_MAX) == -1)
5639
        return (-1);
5640
      break;
5641
    case (LABEL):
78✔
5642
      if (loadLabel(&(legend->label)) == -1)
78✔
5643
        return (-1);
5644
      legend->label.angle = 0; /* force */
78✔
5645
      break;
78✔
5646
    case (LEGEND):
5647
      break; /* for string loads */
5648
    case (OUTLINECOLOR):
×
5649
      if (loadColor(&(legend->outlinecolor), NULL) != MS_SUCCESS)
×
5650
        return (-1);
5651
      break;
5652
    case (POSITION):
16✔
5653
      if ((legend->position =
16✔
5654
               getSymbol(6, MS_UL, MS_UR, MS_LL, MS_LR, MS_UC, MS_LC)) == -1)
16✔
5655
        return (-1);
5656
      break;
5657
    case (POSTLABELCACHE):
8✔
5658
      if ((legend->postlabelcache = getSymbol(2, MS_TRUE, MS_FALSE)) == -1)
8✔
5659
        return (-1);
5660
      break;
5661
    case (STATUS):
16✔
5662
      if ((legend->status = getSymbol(3, MS_ON, MS_OFF, MS_EMBED)) == -1)
16✔
5663
        return (-1);
5664
      break;
5665
    case (TRANSPARENT):
8✔
5666
      if ((legend->transparent = getSymbol(2, MS_ON, MS_OFF)) == -1)
8✔
5667
        return (-1);
5668
      break;
5669
    case (TEMPLATE):
×
5670
      if (getString(&legend->template) == MS_FAILURE)
×
5671
        return (-1);
5672
      break;
5673
    default:
×
5674
      if (strlen(msyystring_buffer) > 0) {
×
5675
        msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
5676
                   "loadLegend()", msyystring_buffer, msyylineno);
5677
        return (-1);
×
5678
      } else {
5679
        return (0); /* end of a string, not an error */
5680
      }
5681
    }
5682
  } /* next token */
5683
}
5684

5685
int msUpdateLegendFromString(legendObj *legend, char *string) {
×
5686
  if (!legend || !string)
×
5687
    return MS_FAILURE;
5688

5689
  msAcquireLock(TLOCK_PARSER);
×
5690

5691
  msyystate = MS_TOKENIZE_STRING;
×
5692
  msyystring = string;
×
5693
  msyylex(); /* sets things up, but doesn't process any tokens */
×
5694

5695
  msyylineno = 1; /* start at line 1 */
×
5696

5697
  if (loadLegend(legend, legend->map) == -1) {
×
5698
    msReleaseLock(TLOCK_PARSER);
×
5699
    return MS_FAILURE; /* parse error */
×
5700
    ;
5701
  }
5702

5703
  msyylex_destroy();
×
5704
  msReleaseLock(TLOCK_PARSER);
×
5705
  return MS_SUCCESS;
×
5706
}
5707

5708
static void writeLegend(FILE *stream, int indent, legendObj *legend) {
1✔
5709
  colorObj c;
5710

5711
  indent++;
1✔
5712
  writeBlockBegin(stream, indent, "LEGEND");
1✔
5713
  MS_INIT_COLOR(c, 255, 255, 255, 255);
1✔
5714
  writeColor(stream, indent, "IMAGECOLOR", &c, &(legend->imagecolor));
1✔
5715
  writeDimension(stream, indent, "KEYSIZE", legend->keysizex, legend->keysizey,
1✔
5716
                 NULL, NULL);
5717
  writeDimension(stream, indent, "KEYSPACING", legend->keyspacingx,
1✔
5718
                 legend->keyspacingy, NULL, NULL);
1✔
5719
  writeLabel(stream, indent, &(legend->label));
1✔
5720
  writeColor(stream, indent, "OUTLINECOLOR", NULL, &(legend->outlinecolor));
1✔
5721
  if (legend->status == MS_EMBED)
1✔
5722
    writeKeyword(stream, indent, "POSITION", legend->position, 6, MS_LL, "LL",
×
5723
                 MS_UL, "UL", MS_UR, "UR", MS_LR, "LR", MS_UC, "UC", MS_LC,
5724
                 "LC");
5725
  writeKeyword(stream, indent, "POSTLABELCACHE", legend->postlabelcache, 1,
1✔
5726
               MS_TRUE, "TRUE");
5727
  writeKeyword(stream, indent, "STATUS", legend->status, 3, MS_ON, "ON", MS_OFF,
1✔
5728
               "OFF", MS_EMBED, "EMBED");
5729
  writeKeyword(stream, indent, "TRANSPARENT", legend->transparent, 2, MS_TRUE,
1✔
5730
               "TRUE", MS_FALSE, "FALSE");
5731
  writeString(stream, indent, "TEMPLATE", NULL, legend->template);
1✔
5732
  writeBlockEnd(stream, indent, "LEGEND");
1✔
5733
  writeLineFeed(stream);
5734
}
1✔
5735

5736
char *msWriteLegendToString(legendObj *legend) {
×
5737
  msIOContext context;
5738
  msIOBuffer buffer;
5739

5740
  context.label = NULL;
×
5741
  context.write_channel = MS_TRUE;
×
5742
  context.readWriteFunc = msIO_bufferWrite;
×
5743
  context.cbData = &buffer;
×
5744
  buffer.data = NULL;
×
5745
  buffer.data_len = 0;
×
5746
  buffer.data_offset = 0;
×
5747

5748
  msIO_installHandlers(NULL, &context, NULL);
×
5749

5750
  writeLegend(stdout, -1, legend);
×
5751
  msIO_bufferWrite(&buffer, "", 1);
×
5752

5753
  msIO_installHandlers(NULL, NULL, NULL);
×
5754

5755
  return (char *)buffer.data;
×
5756
}
5757

5758
/*
5759
** Initialize, load and free a scalebarObj structure
5760
*/
5761
void initScalebar(scalebarObj *scalebar) {
3,528✔
5762
  MS_INIT_COLOR(scalebar->imagecolor, -1, -1, -1, 255);
3,528✔
5763
  scalebar->width = 200;
3,528✔
5764
  scalebar->height = 3;
3,528✔
5765
  scalebar->style = 0; /* only 2 styles at this point */
3,528✔
5766
  scalebar->intervals = 4;
3,528✔
5767
  initLabel(&scalebar->label);
3,528✔
5768
  scalebar->label.position = MS_XY; /* override */
3,528✔
5769
  MS_INIT_COLOR(scalebar->backgroundcolor, -1, -1, -1,
3,528✔
5770
                255); /* if not set, scalebar creation needs to set this to
5771
                         match the background color */
5772
  MS_INIT_COLOR(scalebar->color, 0, 0, 0, 255); /* default to black */
3,528✔
5773
  MS_INIT_COLOR(scalebar->outlinecolor, -1, -1, -1, 255);
3,528✔
5774
  scalebar->units = MS_MILES;
3,528✔
5775
  scalebar->measure = MS_SCALEBAR_MEASURE_CARTESIAN;
3,528✔
5776
  scalebar->status = MS_OFF;
3,528✔
5777
  scalebar->position = MS_LL;
3,528✔
5778
  scalebar->transparent = MS_NOOVERRIDE; /* no transparency */
3,528✔
5779
  scalebar->postlabelcache = MS_FALSE;   /* draw with labels */
3,528✔
5780
  scalebar->align = MS_ALIGN_CENTER;
3,528✔
5781
  scalebar->offsetx = 0;
3,528✔
5782
  scalebar->offsety = 0;
3,528✔
5783
}
3,528✔
5784

5785
MS_DLL_EXPORT void freeScalebar(scalebarObj *scalebar) {
3,480✔
5786
  freeLabel(&(scalebar->label));
3,480✔
5787
}
3,480✔
5788

5789
int loadScalebar(scalebarObj *scalebar) {
422✔
5790
  for (;;) {
5791
    switch (msyylex()) {
4,979✔
5792
    case (ALIGN):
×
5793
      if ((scalebar->align = getSymbol(3, MS_ALIGN_LEFT, MS_ALIGN_CENTER,
×
5794
                                       MS_ALIGN_RIGHT)) == -1)
5795
        return (-1);
5796
      break;
5797
    case (BACKGROUNDCOLOR):
415✔
5798
      if (loadColor(&(scalebar->backgroundcolor), NULL) != MS_SUCCESS)
415✔
5799
        return (-1);
5800
      break;
5801
    case (COLOR):
415✔
5802
      if (loadColor(&(scalebar->color), NULL) != MS_SUCCESS)
415✔
5803
        return (-1);
5804
      break;
5805
    case (EOF):
×
5806
      msSetError(MS_EOFERR, NULL, "loadScalebar()");
×
5807
      return (-1);
×
5808
    case (END):
5809
      return (0);
5810
      break;
5811
    case (IMAGECOLOR):
402✔
5812
      if (loadColor(&(scalebar->imagecolor), NULL) != MS_SUCCESS)
402✔
5813
        return (-1);
5814
      break;
5815
    case (INTERVALS):
13✔
5816
      if (getInteger(&(scalebar->intervals), MS_NUM_CHECK_RANGE,
13✔
5817
                     MS_SCALEBAR_INTERVALS_MIN,
5818
                     MS_SCALEBAR_INTERVALS_MAX) == -1)
5819
        return (-1);
5820
      break;
5821
    case (LABEL):
415✔
5822
      if (loadLabel(&(scalebar->label)) == -1)
415✔
5823
        return (-1);
5824
      scalebar->label.angle = 0;
415✔
5825
      break;
415✔
5826
    case (MS_STRING):
8✔
5827
      if (!EQUAL(msyystring_buffer, "MEASURE"))
8✔
NEW
5828
        goto scalebar_parse_error;
×
5829
      if (msyylex() != MS_STRING)
8✔
NEW
5830
        goto scalebar_parse_error;
×
5831
      if (EQUAL(msyystring_buffer, "CARTESIAN"))
8✔
5832
        scalebar->measure = MS_SCALEBAR_MEASURE_CARTESIAN;
1✔
5833
      else if (EQUAL(msyystring_buffer, "GEODESIC"))
7✔
5834
        scalebar->measure = MS_SCALEBAR_MEASURE_GEODESIC;
5✔
5835
      else
5836
        goto scalebar_parse_error;
2✔
5837
      break;
5838
    case (OUTLINECOLOR):
13✔
5839
      if (loadColor(&(scalebar->outlinecolor), NULL) != MS_SUCCESS)
13✔
5840
        return (-1);
5841
      break;
5842
    case (POSITION):
402✔
5843
      if ((scalebar->position =
402✔
5844
               getSymbol(6, MS_UL, MS_UR, MS_LL, MS_LR, MS_UC, MS_LC)) == -1)
402✔
5845
        return (-1);
5846
      break;
5847
    case (POSTLABELCACHE):
402✔
5848
      if ((scalebar->postlabelcache = getSymbol(2, MS_TRUE, MS_FALSE)) == -1)
402✔
5849
        return (-1);
5850
      break;
5851
    case (SCALEBAR):
5852
      break; /* for string loads */
5853
    case (SIZE):
417✔
5854
      if (getInteger(&(scalebar->width), MS_NUM_CHECK_RANGE,
417✔
5855
                     MS_SCALEBAR_WIDTH_MIN, MS_SCALEBAR_WIDTH_MAX) == -1)
5856
        return (-1);
5857
      if (getInteger(&(scalebar->height), MS_NUM_CHECK_RANGE,
417✔
5858
                     MS_SCALEBAR_HEIGHT_MIN, MS_SCALEBAR_HEIGHT_MAX) == -1)
5859
        return (-1);
5860
      break;
5861
    case (STATUS):
417✔
5862
      if ((scalebar->status = getSymbol(3, MS_ON, MS_OFF, MS_EMBED)) == -1)
417✔
5863
        return (-1);
5864
      break;
5865
    case (STYLE):
402✔
5866
      if (getInteger(&(scalebar->style), MS_NUM_CHECK_RANGE, 0, 1) == -1)
402✔
5867
        return (-1); // only 2 styles: 0 and 1
5868
      break;
5869
    case (TRANSPARENT):
415✔
5870
      if ((scalebar->transparent = getSymbol(2, MS_ON, MS_OFF)) == -1)
415✔
5871
        return (-1);
5872
      break;
5873
    case (UNITS):
417✔
5874
      if ((scalebar->units =
417✔
5875
               getSymbol(6, MS_INCHES, MS_FEET, MS_MILES, MS_METERS,
417✔
5876
                         MS_KILOMETERS, MS_NAUTICALMILES)) == -1)
5877
        return (-1);
5878
      break;
5879
    case (OFFSET):
2✔
5880
      if (getInteger(&(scalebar->offsetx), MS_NUM_CHECK_RANGE,
2✔
5881
                     MS_SCALEBAR_OFFSET_MIN, MS_SCALEBAR_OFFSET_MAX) == -1)
5882
        return (-1);
5883
      if (getInteger(&(scalebar->offsety), MS_NUM_CHECK_RANGE,
2✔
5884
                     MS_SCALEBAR_OFFSET_MIN, MS_SCALEBAR_OFFSET_MAX) == -1)
5885
        return (-1);
5886
      break;
5887
    default:
5888
    scalebar_parse_error:
2✔
5889
      if (strlen(msyystring_buffer) > 0) {
2✔
5890
        msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
2✔
5891
                   "loadScalebar()", msyystring_buffer, msyylineno);
5892
        return (-1);
2✔
5893
      } else {
5894
        return (0); /* end of a string, not an error */
5895
      }
5896
    }
5897
  } /* next token */
5898
}
5899

5900
int msUpdateScalebarFromString(scalebarObj *scalebar, char *string) {
4✔
5901
  if (!scalebar || !string)
4✔
5902
    return MS_FAILURE;
5903

5904
  msAcquireLock(TLOCK_PARSER);
4✔
5905

5906
  msyystate = MS_TOKENIZE_STRING;
4✔
5907
  msyystring = string;
4✔
5908
  msyylex(); /* sets things up, but doesn't process any tokens */
4✔
5909

5910
  msyylineno = 1; /* start at line 1 */
4✔
5911

5912
  if (loadScalebar(scalebar) == -1) {
4✔
5913
    msReleaseLock(TLOCK_PARSER);
2✔
5914
    return MS_FAILURE; /* parse error */
2✔
5915
    ;
5916
  }
5917

5918
  msyylex_destroy();
2✔
5919
  msReleaseLock(TLOCK_PARSER);
2✔
5920
  return MS_SUCCESS;
2✔
5921
}
5922

5923
static void writeScalebar(FILE *stream, int indent, scalebarObj *scalebar) {
3✔
5924
  colorObj c;
5925

5926
  indent++;
3✔
5927
  writeBlockBegin(stream, indent, "SCALEBAR");
3✔
5928
  writeKeyword(stream, indent, "ALIGN", scalebar->align, 2, MS_ALIGN_LEFT,
3✔
5929
               "LEFT", MS_ALIGN_RIGHT, "RIGHT");
5930
  writeColor(stream, indent, "BACKGROUNDCOLOR", NULL,
3✔
5931
             &(scalebar->backgroundcolor));
5932
  MS_INIT_COLOR(c, 0, 0, 0, 255);
3✔
5933
  writeColor(stream, indent, "COLOR", &c, &(scalebar->color));
3✔
5934
  writeColor(stream, indent, "IMAGECOLOR", NULL, &(scalebar->imagecolor));
3✔
5935
  writeNumber(stream, indent, "INTERVALS", -1, scalebar->intervals);
3✔
5936
  writeLabel(stream, indent, &(scalebar->label));
3✔
5937
  writeKeyword(stream, indent, "MEASURE", scalebar->measure, 2,
3✔
5938
               MS_SCALEBAR_MEASURE_CARTESIAN, "CARTESIAN",
5939
               MS_SCALEBAR_MEASURE_GEODESIC, "GEODESIC");
5940
  writeColor(stream, indent, "OUTLINECOLOR", NULL, &(scalebar->outlinecolor));
3✔
5941
  if (scalebar->status == MS_EMBED)
3✔
5942
    writeKeyword(stream, indent, "POSITION", scalebar->position, 6, MS_LL, "LL",
1✔
5943
                 MS_UL, "UL", MS_UR, "UR", MS_LR, "LR", MS_UC, "UC", MS_LC,
5944
                 "LC");
5945
  writeKeyword(stream, indent, "POSTLABELCACHE", scalebar->postlabelcache, 1,
3✔
5946
               MS_TRUE, "TRUE");
5947
  writeDimension(stream, indent, "SIZE", scalebar->width, scalebar->height,
3✔
5948
                 NULL, NULL);
5949
  writeKeyword(stream, indent, "STATUS", scalebar->status, 3, MS_ON, "ON",
3✔
5950
               MS_OFF, "OFF", MS_EMBED, "EMBED");
5951
  writeNumber(stream, indent, "STYLE", 0, scalebar->style);
3✔
5952
  writeKeyword(stream, indent, "TRANSPARENT", scalebar->transparent, 2, MS_TRUE,
3✔
5953
               "TRUE", MS_FALSE, "FALSE");
5954
  writeKeyword(stream, indent, "UNITS", scalebar->units, 6, MS_INCHES, "INCHES",
3✔
5955
               MS_FEET, "FEET", MS_MILES, "MILES", MS_METERS, "METERS",
5956
               MS_KILOMETERS, "KILOMETERS", MS_NAUTICALMILES, "NAUTICALMILES");
5957
  writeBlockEnd(stream, indent, "SCALEBAR");
3✔
5958
  writeLineFeed(stream);
5959
}
3✔
5960

5961
char *msWriteScalebarToString(scalebarObj *scalebar) {
2✔
5962
  msIOContext context;
5963
  msIOBuffer buffer;
5964

5965
  context.label = NULL;
2✔
5966
  context.write_channel = MS_TRUE;
2✔
5967
  context.readWriteFunc = msIO_bufferWrite;
2✔
5968
  context.cbData = &buffer;
2✔
5969
  buffer.data = NULL;
2✔
5970
  buffer.data_len = 0;
2✔
5971
  buffer.data_offset = 0;
2✔
5972

5973
  msIO_installHandlers(NULL, &context, NULL);
2✔
5974

5975
  writeScalebar(stdout, -1, scalebar);
2✔
5976
  msIO_bufferWrite(&buffer, "", 1);
2✔
5977

5978
  msIO_installHandlers(NULL, NULL, NULL);
2✔
5979

5980
  return (char *)buffer.data;
2✔
5981
}
5982

5983
/*
5984
** Initialize a queryMapObj structure
5985
*/
5986
void initQueryMap(queryMapObj *querymap) {
3,479✔
5987
  querymap->width = querymap->height = -1;
3,479✔
5988
  querymap->style = MS_HILITE;
3,479✔
5989
  querymap->status = MS_OFF;
3,479✔
5990
  MS_INIT_COLOR(querymap->color, 255, 255, 0, 255); /* yellow */
3,479✔
5991
}
3,479✔
5992

5993
int loadQueryMap(queryMapObj *querymap, mapObj *map) {
165✔
5994
  querymap->map = (mapObj *)map;
165✔
5995

5996
  for (;;) {
5997
    switch (msyylex()) {
406✔
5998
    case (QUERYMAP):
5999
      break; /* for string loads */
6000
    case (COLOR):
154✔
6001
      if (loadColor(&(querymap->color), NULL) != MS_SUCCESS)
154✔
6002
        return MS_FAILURE;
6003
      break;
6004
    case (EOF):
×
6005
      msSetError(MS_EOFERR, NULL, "loadQueryMap()");
×
6006
      return (-1);
×
6007
    case (END):
6008
      return (0);
6009
      break;
6010
    case (SIZE):
29✔
6011
      /*
6012
       ** we do -1 (and avoid 0) here to maintain backwards compatibility as
6013
       *older versions write "SIZE -1 -1" when saving a mapfile
6014
       */
6015
      if (getInteger(&(querymap->width), MS_NUM_CHECK_RANGE, -1,
29✔
6016
                     querymap->map->maxsize) == -1 ||
29✔
6017
          querymap->width == 0) {
29✔
6018
        msSetError(MS_MISCERR, "Invalid SIZE value (line %d)", "loadQueryMap()",
×
6019
                   msyylineno);
6020
        return (-1);
×
6021
      }
6022
      if (getInteger(&(querymap->height), MS_NUM_CHECK_RANGE, -1,
29✔
6023
                     querymap->map->maxsize) == -1 ||
29✔
6024
          querymap->height == 0) {
29✔
6025
        msSetError(MS_MISCERR, "Invalid SIZE value (line %d)", "loadQueryMap()",
×
6026
                   msyylineno);
6027
        return (-1);
×
6028
      }
6029
      break;
6030
    case (STATUS):
29✔
6031
      if ((querymap->status = getSymbol(2, MS_ON, MS_OFF)) == -1)
29✔
6032
        return (-1);
6033
      break;
6034
    case (STYLE):
29✔
6035
    case (TYPE):
6036
      if ((querymap->style = getSymbol(3, MS_NORMAL, MS_HILITE, MS_SELECTED)) ==
29✔
6037
          -1)
6038
        return (-1);
6039
      break;
6040
    default:
×
6041
      if (strlen(msyystring_buffer) > 0) {
×
6042
        msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
6043
                   "loadQueryMap()", msyystring_buffer, msyylineno);
6044
        return (-1);
×
6045
      } else {
6046
        return (0); /* end of a string, not an error */
6047
      }
6048
    }
6049
  }
6050
}
6051

6052
int msUpdateQueryMapFromString(queryMapObj *querymap, char *string) {
×
6053
  if (!querymap || !string)
×
6054
    return MS_FAILURE;
6055

6056
  msAcquireLock(TLOCK_PARSER);
×
6057

6058
  msyystate = MS_TOKENIZE_STRING;
×
6059
  msyystring = string;
×
6060
  msyylex(); /* sets things up, but doesn't process any tokens */
×
6061

6062
  msyylineno = 1; /* start at line 1 */
×
6063

6064
  if (loadQueryMap(querymap, querymap->map) == -1) {
×
6065
    msReleaseLock(TLOCK_PARSER);
×
6066
    return MS_FAILURE; /* parse error */
×
6067
    ;
6068
  }
6069

6070
  msyylex_destroy();
×
6071
  msReleaseLock(TLOCK_PARSER);
×
6072
  return MS_SUCCESS;
×
6073
}
6074

6075
static void writeQueryMap(FILE *stream, int indent, queryMapObj *querymap) {
1✔
6076
  colorObj c;
6077

6078
  indent++;
1✔
6079
  writeBlockBegin(stream, indent, "QUERYMAP");
1✔
6080
  MS_INIT_COLOR(c, 255, 255, 0, 255);
1✔
6081
  writeColor(stream, indent, "COLOR", &c, &(querymap->color));
1✔
6082
  if (querymap->width != -1 &&
1✔
6083
      querymap->height != -1) // don't write SIZE if not explicitly set
×
6084
    writeDimension(stream, indent, "SIZE", querymap->width, querymap->height,
×
6085
                   NULL, NULL);
6086
  writeKeyword(stream, indent, "STATUS", querymap->status, 2, MS_ON, "ON",
1✔
6087
               MS_OFF, "OFF");
6088
  writeKeyword(stream, indent, "STYLE", querymap->style, 3, MS_NORMAL, "NORMAL",
1✔
6089
               MS_HILITE, "HILITE", MS_SELECTED, "SELECTED");
6090
  writeBlockEnd(stream, indent, "QUERYMAP");
1✔
6091
  writeLineFeed(stream);
6092
}
1✔
6093

6094
char *msWriteQueryMapToString(queryMapObj *querymap) {
×
6095
  msIOContext context;
6096
  msIOBuffer buffer;
6097

6098
  context.label = NULL;
×
6099
  context.write_channel = MS_TRUE;
×
6100
  context.readWriteFunc = msIO_bufferWrite;
×
6101
  context.cbData = &buffer;
×
6102
  buffer.data = NULL;
×
6103
  buffer.data_len = 0;
×
6104
  buffer.data_offset = 0;
×
6105

6106
  msIO_installHandlers(NULL, &context, NULL);
×
6107

6108
  writeQueryMap(stdout, -1, querymap);
×
6109
  msIO_bufferWrite(&buffer, "", 1);
×
6110

6111
  msIO_installHandlers(NULL, NULL, NULL);
×
6112

6113
  return (char *)buffer.data;
×
6114
}
6115

6116
/*
6117
** Initialize a webObj structure
6118
*/
6119
void initWeb(webObj *web) {
3,479✔
6120
  web->template = NULL;
3,479✔
6121
  web->header = web->footer = NULL;
3,479✔
6122
  web->error = web->empty = NULL;
3,479✔
6123
  web->mintemplate = web->maxtemplate = NULL;
3,479✔
6124
  web->minscaledenom = web->maxscaledenom = -1;
3,479✔
6125
  web->imagepath = msStrdup("");
3,479✔
6126
  web->temppath = NULL;
3,479✔
6127
  web->imageurl = msStrdup("");
3,479✔
6128

6129
  initHashTable(&(web->metadata));
3,479✔
6130
  initHashTable(&(web->validation));
3,479✔
6131

6132
  web->map = NULL;
3,479✔
6133
  web->queryformat = msStrdup("text/html");
3,479✔
6134
  web->legendformat = msStrdup("text/html");
3,479✔
6135
  web->browseformat = msStrdup("text/html");
3,479✔
6136
}
3,479✔
6137

6138
void freeWeb(webObj *web) {
3,470✔
6139
  msFree(web->template);
3,470✔
6140
  msFree(web->header);
3,470✔
6141
  msFree(web->footer);
3,470✔
6142
  msFree(web->error);
3,470✔
6143
  msFree(web->empty);
3,470✔
6144
  msFree(web->maxtemplate);
3,470✔
6145
  msFree(web->mintemplate);
3,470✔
6146
  msFree(web->imagepath);
3,470✔
6147
  msFree(web->temppath);
3,470✔
6148
  msFree(web->imageurl);
3,470✔
6149
  msFree(web->queryformat);
3,470✔
6150
  msFree(web->legendformat);
3,470✔
6151
  msFree(web->browseformat);
3,470✔
6152
  msFreeHashItems(&(web->metadata));
3,470✔
6153
  msFreeHashItems(&(web->validation));
3,470✔
6154
}
3,470✔
6155

6156
static void writeWeb(FILE *stream, int indent, webObj *web) {
1✔
6157
  indent++;
1✔
6158
  writeBlockBegin(stream, indent, "WEB");
1✔
6159
  writeString(stream, indent, "BROWSEFORMAT", "text/html", web->browseformat);
1✔
6160
  writeString(stream, indent, "EMPTY", NULL, web->empty);
1✔
6161
  writeString(stream, indent, "ERROR", NULL, web->error);
1✔
6162
  writeString(stream, indent, "FOOTER", NULL, web->footer);
1✔
6163
  writeString(stream, indent, "HEADER", NULL, web->header);
1✔
6164
  writeString(stream, indent, "IMAGEPATH", "", web->imagepath);
1✔
6165
  writeString(stream, indent, "TEMPPATH", NULL, web->temppath);
1✔
6166
  writeString(stream, indent, "IMAGEURL", "", web->imageurl);
1✔
6167
  writeString(stream, indent, "LEGENDFORMAT", "text/html", web->legendformat);
1✔
6168
  writeNumber(stream, indent, "MAXSCALEDENOM", -1, web->maxscaledenom);
1✔
6169
  writeString(stream, indent, "MAXTEMPLATE", NULL, web->maxtemplate);
1✔
6170
  writeHashTable(stream, indent, "METADATA", &(web->metadata));
1✔
6171
  writeNumber(stream, indent, "MINSCALEDENOM", -1, web->minscaledenom);
1✔
6172
  writeString(stream, indent, "MINTEMPLATE", NULL, web->mintemplate);
1✔
6173
  writeString(stream, indent, "QUERYFORMAT", "text/html", web->queryformat);
1✔
6174
  writeString(stream, indent, "TEMPLATE", NULL, web->template);
1✔
6175
  writeHashTable(stream, indent, "VALIDATION", &(web->validation));
1✔
6176
  writeBlockEnd(stream, indent, "WEB");
1✔
6177
  writeLineFeed(stream);
6178
}
1✔
6179

6180
char *msWriteWebToString(webObj *web) {
×
6181
  msIOContext context;
6182
  msIOBuffer buffer;
6183

6184
  context.label = NULL;
×
6185
  context.write_channel = MS_TRUE;
×
6186
  context.readWriteFunc = msIO_bufferWrite;
×
6187
  context.cbData = &buffer;
×
6188
  buffer.data = NULL;
×
6189
  buffer.data_len = 0;
×
6190
  buffer.data_offset = 0;
×
6191

6192
  msIO_installHandlers(NULL, &context, NULL);
×
6193

6194
  writeWeb(stdout, -1, web);
×
6195
  msIO_bufferWrite(&buffer, "", 1);
×
6196

6197
  msIO_installHandlers(NULL, NULL, NULL);
×
6198

6199
  return (char *)buffer.data;
×
6200
}
6201

6202
int loadWeb(webObj *web, mapObj *map) {
2,636✔
6203
  web->map = (mapObj *)map;
2,636✔
6204

6205
  for (;;) {
6206
    switch (msyylex()) {
8,572✔
6207
    case (BROWSEFORMAT): /* change to use validation in 6.0 */
×
6208
      free(web->browseformat);
×
6209
      web->browseformat = NULL; /* there is a default */
×
6210
      if (getString(&web->browseformat) == MS_FAILURE)
×
6211
        return (-1);
6212
      break;
6213
    case (EMPTY):
38✔
6214
      if (getString(&web->empty) == MS_FAILURE)
38✔
6215
        return (-1);
6216
      break;
6217
    case (WEB):
6218
      break; /* for string loads */
6219
    case (EOF):
×
6220
      msSetError(MS_EOFERR, NULL, "loadWeb()");
×
6221
      return (-1);
×
6222
    case (END):
6223
      return (0);
6224
      break;
6225
    case (ERROR):
×
6226
      if (getString(&web->error) == MS_FAILURE)
×
6227
        return (-1);
6228
      break;
6229
    case (FOOTER):
50✔
6230
      if (getString(&web->footer) == MS_FAILURE)
50✔
6231
        return (-1); /* getString() cleans up previously allocated string */
6232
      break;
6233
    case (HEADER):
50✔
6234
      if (getString(&web->header) == MS_FAILURE)
50✔
6235
        return (-1); /* getString() cleans up previously allocated string */
6236
      break;
6237
    case (IMAGEPATH):
1,447✔
6238
      if (getString(&web->imagepath) == MS_FAILURE)
1,447✔
6239
        return (-1);
6240
      break;
6241
    case (TEMPPATH):
×
6242
      if (getString(&web->temppath) == MS_FAILURE)
×
6243
        return (-1);
6244
      break;
6245
    case (IMAGEURL):
1,386✔
6246
      if (getString(&web->imageurl) == MS_FAILURE)
1,386✔
6247
        return (-1);
6248
      break;
6249
    case (LEGENDFORMAT): /* change to use validation in 6.0 */
×
6250
      free(web->legendformat);
×
6251
      web->legendformat = NULL; /* there is a default */
×
6252
      if (getString(&web->legendformat) == MS_FAILURE)
×
6253
        return (-1);
6254
      break;
6255
    case (MAXSCALEDENOM):
144✔
6256
      if (getDouble(&web->maxscaledenom, MS_NUM_CHECK_GTE, 0, -1) == -1)
144✔
6257
        return (-1);
6258
      break;
6259
    case (MAXTEMPLATE):
×
6260
      if (getString(&web->maxtemplate) == MS_FAILURE)
×
6261
        return (-1);
6262
      break;
6263
    case (METADATA):
2,518✔
6264
      if (loadHashTable(&(web->metadata)) != MS_SUCCESS)
2,518✔
6265
        return (-1);
6266
      break;
6267
    case (MINSCALEDENOM):
144✔
6268
      if (getDouble(&web->minscaledenom, MS_NUM_CHECK_GTE, 0, -1) == -1)
144✔
6269
        return (-1);
6270
      break;
6271
    case (MINTEMPLATE):
×
6272
      if (getString(&web->mintemplate) == MS_FAILURE)
×
6273
        return (-1);
6274
      break;
6275
    case (QUERYFORMAT): /* change to use validation in 6.0 */
80✔
6276
      free(web->queryformat);
80✔
6277
      web->queryformat = NULL; /* there is a default */
80✔
6278
      if (getString(&web->queryformat) == MS_FAILURE)
80✔
6279
        return (-1);
6280
      break;
6281
    case (TEMPLATE):
×
6282
      if (getString(&web->template) == MS_FAILURE)
×
6283
        return (-1); /* getString() cleans up previously allocated string */
6284
      break;
6285
    case (VALIDATION):
79✔
6286
      if (loadHashTable(&(web->validation)) != MS_SUCCESS)
79✔
6287
        return (-1);
6288
      break;
6289
    default:
×
6290
      if (strlen(msyystring_buffer) > 0) {
×
6291
        msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
6292
                   "loadWeb()", msyystring_buffer, msyylineno);
6293
        return (-1);
×
6294
      } else {
6295
        return (0); /* end of a string, not an error */
6296
      }
6297
    }
6298
  }
6299
}
6300

6301
int msUpdateWebFromString(webObj *web, char *string) {
×
6302
  if (!web || !string)
×
6303
    return MS_FAILURE;
6304

6305
  msAcquireLock(TLOCK_PARSER);
×
6306

6307
  msyystate = MS_TOKENIZE_STRING;
×
6308
  msyystring = string;
×
6309
  msyylex(); /* sets things up, but doesn't process any tokens */
×
6310

6311
  msyylineno = 1; /* start at line 1 */
×
6312

6313
  if (loadWeb(web, web->map) == -1) {
×
6314
    msReleaseLock(TLOCK_PARSER);
×
6315
    return MS_FAILURE; /* parse error */
×
6316
    ;
6317
  }
6318

6319
  msyylex_destroy();
×
6320
  msReleaseLock(TLOCK_PARSER);
×
6321
  return MS_SUCCESS;
×
6322
}
6323

6324
/*
6325
** Initialize, load and free a mapObj structure
6326
**
6327
** This really belongs in mapobject.c, but currently it also depends on
6328
** lots of other init methods in this file.
6329
*/
6330

6331
int initMap(mapObj *map) {
3,479✔
6332
  int i = 0;
6333
  MS_REFCNT_INIT(map);
3,479✔
6334

6335
  map->debug = (int)msGetGlobalDebugLevel();
3,479✔
6336

6337
  /* Set maxlayers = 0, layers[] and layerorder[] will be allocated as needed,
6338
   * on the first call to msGrowMapLayers()
6339
   */
6340
  map->numlayers = 0;
3,479✔
6341
  map->maxlayers = 0;
3,479✔
6342
  map->layers = NULL;
3,479✔
6343
  map->layerorder =
3,479✔
6344
      NULL; /* used to modify the order in which the layers are drawn */
6345

6346
  map->status = MS_ON;
3,479✔
6347
  map->name = msStrdup("MS");
3,479✔
6348
  map->extent.minx = map->extent.miny = map->extent.maxx = map->extent.maxy =
3,479✔
6349
      -1.0;
6350

6351
  map->scaledenom = -1.0;
3,479✔
6352
  map->resolution = MS_DEFAULT_RESOLUTION;    /* pixels per inch */
3,479✔
6353
  map->defresolution = MS_DEFAULT_RESOLUTION; /* pixels per inch */
3,479✔
6354

6355
  map->height = map->width = -1;
3,479✔
6356
  map->maxsize = MS_MAXIMAGESIZE_DEFAULT;
3,479✔
6357

6358
  map->gt.need_geotransform = MS_FALSE;
3,479✔
6359
  map->gt.rotation_angle = 0.0;
3,479✔
6360

6361
  map->units = MS_METERS;
3,479✔
6362
  map->cellsize = 0;
3,479✔
6363
  map->shapepath = NULL;
3,479✔
6364
  map->mappath = NULL;
3,479✔
6365
  map->sldurl = NULL;
3,479✔
6366

6367
  MS_INIT_COLOR(map->imagecolor, 255, 255, 255, 255); /* white */
3,479✔
6368

6369
  map->numoutputformats = 0;
3,479✔
6370
  map->outputformatlist = NULL;
3,479✔
6371
  map->outputformat = NULL;
3,479✔
6372

6373
  /* map->configoptions = msCreateHashTable();; */
6374
  initHashTable(&(map->configoptions));
3,479✔
6375

6376
  map->imagetype = NULL;
3,479✔
6377

6378
  map->palette.numcolors = 0;
3,479✔
6379

6380
  for (i = 0; i < MS_MAX_LABEL_PRIORITY; i++) {
38,269✔
6381
    map->labelcache.slots[i].labels =
34,790✔
6382
        NULL; /* cache is initialize at draw time */
6383
    map->labelcache.slots[i].cachesize = 0;
34,790✔
6384
    map->labelcache.slots[i].numlabels = 0;
34,790✔
6385
    map->labelcache.slots[i].markers = NULL;
34,790✔
6386
    map->labelcache.slots[i].markercachesize = 0;
34,790✔
6387
    map->labelcache.slots[i].nummarkers = 0;
34,790✔
6388
  }
6389

6390
  map->fontset.filename = NULL;
3,479✔
6391
  map->fontset.numfonts = 0;
3,479✔
6392

6393
  /* map->fontset.fonts = NULL; */
6394
  initHashTable(&(map->fontset.fonts));
3,479✔
6395

6396
  msInitSymbolSet(&map->symbolset);
3,479✔
6397
  map->symbolset.fontset = &(map->fontset);
3,479✔
6398
  map->symbolset.map = map;
3,479✔
6399

6400
  initLegend(&map->legend);
3,479✔
6401
  initScalebar(&map->scalebar);
3,479✔
6402
  initWeb(&map->web);
3,479✔
6403
  initReferenceMap(&map->reference);
3,479✔
6404
  initQueryMap(&map->querymap);
3,479✔
6405

6406
  map->projContext = msProjectionContextGetFromPool();
3,479✔
6407

6408
  if (msInitProjection(&(map->projection)) == -1)
3,479✔
6409
    return (-1);
6410
  if (msInitProjection(&(map->latlon)) == -1)
3,479✔
6411
    return (-1);
6412

6413
  msProjectionSetContext(&(map->projection), map->projContext);
3,479✔
6414
  msProjectionSetContext(&(map->latlon), map->projContext);
3,479✔
6415

6416
  /* initialize a default "geographic" projection */
6417
  map->latlon.numargs = 2;
3,479✔
6418
  map->latlon.args[0] = msStrdup("proj=latlong");
3,479✔
6419
  map->latlon.args[1] =
6,958✔
6420
      msStrdup("ellps=WGS84"); /* probably want a different ellipsoid */
3,479✔
6421
  if (msProcessProjection(&(map->latlon)) == -1)
3,479✔
6422
    return (-1);
6423

6424
  map->templatepattern = map->datapattern = NULL;
3,479✔
6425

6426
  /* Encryption key information - see mapcrypto.c */
6427
  map->encryption_key_loaded = MS_FALSE;
3,479✔
6428

6429
  msInitQuery(&(map->query));
3,479✔
6430

6431
#ifdef USE_V8_MAPSCRIPT
6432
  map->v8context = NULL;
6433
#endif
6434

6435
  map->config = NULL;
3,479✔
6436

6437
  return (0);
3,479✔
6438
}
6439

6440
/*
6441
** Ensure there is at least one free entry in the layers and layerorder
6442
** arrays of this mapObj. Grow the allocated layers/layerorder arrays if
6443
** necessary and allocate a new layer for layers[numlayers] if there is
6444
** not already one, setting its contents to all zero bytes (i.e. does not
6445
** call initLayer() on it).
6446
**
6447
** This function is safe to use for the initial allocation of the layers[]
6448
** and layerorder[] arrays as well (i.e. when maxlayers==0 and layers==NULL)
6449
**
6450
** Returns a reference to the new layerObj on success, NULL on error.
6451
*/
6452
layerObj *msGrowMapLayers(mapObj *map) {
14,386✔
6453
  /* Do we need to increase the size of layers/layerorder by
6454
   * MS_LAYER_ALLOCSIZE?
6455
   */
6456
  if (map->numlayers == map->maxlayers) {
14,386✔
6457
    layerObj **newLayersPtr;
6458
    int *newLayerorderPtr;
6459
    int i, newsize;
6460

6461
    newsize = map->maxlayers + MS_LAYER_ALLOCSIZE;
3,417✔
6462

6463
    /* Alloc/realloc layers */
6464
    newLayersPtr =
6465
        (layerObj **)realloc(map->layers, newsize * sizeof(layerObj *));
3,417✔
6466
    MS_CHECK_ALLOC(newLayersPtr, newsize * sizeof(layerObj *), NULL);
3,417✔
6467

6468
    map->layers = newLayersPtr;
3,417✔
6469

6470
    /* Alloc/realloc layerorder */
6471
    newLayerorderPtr = (int *)realloc(map->layerorder, newsize * sizeof(int));
3,417✔
6472
    MS_CHECK_ALLOC(newLayerorderPtr, newsize * sizeof(int), NULL);
3,417✔
6473

6474
    map->layerorder = newLayerorderPtr;
3,417✔
6475

6476
    map->maxlayers = newsize;
3,417✔
6477
    for (i = map->numlayers; i < map->maxlayers; i++) {
222,105✔
6478
      map->layers[i] = NULL;
218,688✔
6479
      map->layerorder[i] = 0;
218,688✔
6480
    }
6481
  }
6482

6483
  if (map->layers[map->numlayers] == NULL) {
14,386✔
6484
    map->layers[map->numlayers] = (layerObj *)calloc(1, sizeof(layerObj));
14,386✔
6485
    MS_CHECK_ALLOC(map->layers[map->numlayers], sizeof(layerObj), NULL);
14,386✔
6486
  }
6487

6488
  return map->layers[map->numlayers];
14,386✔
6489
}
6490

6491
int msFreeLabelCacheSlot(labelCacheSlotObj *cacheslot) {
47,030✔
6492
  int i, j;
6493

6494
  /* free the labels */
6495
  if (cacheslot->labels) {
47,030✔
6496
    for (i = 0; i < cacheslot->numlabels; i++) {
16,995✔
6497

6498
      for (j = 0; j < cacheslot->labels[i].numtextsymbols; j++) {
14,718✔
6499
        freeTextSymbol(cacheslot->labels[i].textsymbols[j]);
10,053✔
6500
        free(cacheslot->labels[i].textsymbols[j]);
10,053✔
6501
      }
6502
      msFree(cacheslot->labels[i].textsymbols);
4,665✔
6503

6504
      if (cacheslot->labels[i].leaderline) {
4,665✔
6505
        msFree(cacheslot->labels[i].leaderline->point);
32✔
6506
        msFree(cacheslot->labels[i].leaderline);
32✔
6507
        msFree(cacheslot->labels[i].leaderbbox);
32✔
6508
      }
6509
    }
6510
  }
6511
  msFree(cacheslot->labels);
47,030✔
6512
  cacheslot->labels = NULL;
47,030✔
6513
  cacheslot->cachesize = 0;
47,030✔
6514
  cacheslot->numlabels = 0;
47,030✔
6515

6516
  msFree(cacheslot->markers);
47,030✔
6517
  cacheslot->markers = NULL;
47,030✔
6518
  cacheslot->markercachesize = 0;
47,030✔
6519
  cacheslot->nummarkers = 0;
47,030✔
6520

6521
  return (MS_SUCCESS);
47,030✔
6522
}
6523

6524
int msFreeLabelCache(labelCacheObj *cache) {
4,703✔
6525
  int p;
6526

6527
  for (p = 0; p < MS_MAX_LABEL_PRIORITY; p++) {
51,733✔
6528
    if (msFreeLabelCacheSlot(&(cache->slots[p])) != MS_SUCCESS)
47,030✔
6529
      return MS_FAILURE;
6530
  }
6531

6532
  cache->num_allocated_rendered_members = cache->num_rendered_members = 0;
4,703✔
6533
  msFree(cache->rendered_text_symbols);
4,703✔
6534

6535
  return MS_SUCCESS;
4,703✔
6536
}
6537

6538
int msInitLabelCacheSlot(labelCacheSlotObj *cacheslot) {
12,330✔
6539

6540
  if (cacheslot->labels || cacheslot->markers)
12,330✔
6541
    msFreeLabelCacheSlot(cacheslot);
×
6542

6543
  cacheslot->labels = (labelCacheMemberObj *)malloc(
12,330✔
6544
      sizeof(labelCacheMemberObj) * MS_LABELCACHEINITSIZE);
6545
  MS_CHECK_ALLOC(cacheslot->labels,
12,330✔
6546
                 sizeof(labelCacheMemberObj) * MS_LABELCACHEINITSIZE,
6547
                 MS_FAILURE);
6548

6549
  cacheslot->cachesize = MS_LABELCACHEINITSIZE;
12,330✔
6550
  cacheslot->numlabels = 0;
12,330✔
6551

6552
  cacheslot->markers = (markerCacheMemberObj *)malloc(
12,330✔
6553
      sizeof(markerCacheMemberObj) * MS_LABELCACHEINITSIZE);
6554
  MS_CHECK_ALLOC(cacheslot->markers,
12,330✔
6555
                 sizeof(markerCacheMemberObj) * MS_LABELCACHEINITSIZE,
6556
                 MS_FAILURE);
6557

6558
  cacheslot->markercachesize = MS_LABELCACHEINITSIZE;
12,330✔
6559
  cacheslot->nummarkers = 0;
12,330✔
6560

6561
  return (MS_SUCCESS);
12,330✔
6562
}
6563

6564
int msInitLabelCache(labelCacheObj *cache) {
1,233✔
6565
  int p;
6566

6567
  for (p = 0; p < MS_MAX_LABEL_PRIORITY; p++) {
13,563✔
6568
    if (msInitLabelCacheSlot(&(cache->slots[p])) != MS_SUCCESS)
12,330✔
6569
      return MS_FAILURE;
6570
  }
6571
  cache->gutter = 0;
1,233✔
6572
  cache->num_allocated_rendered_members = cache->num_rendered_members = 0;
1,233✔
6573
  cache->rendered_text_symbols = NULL;
1,233✔
6574

6575
  return MS_SUCCESS;
1,233✔
6576
}
6577

6578
static void writeMap(FILE *stream, int indent, mapObj *map) {
1✔
6579
  int i;
6580
  colorObj c;
6581

6582
  writeBlockBegin(stream, indent, "MAP");
1✔
6583
  writeNumber(stream, indent, "ANGLE", 0, map->gt.rotation_angle);
1✔
6584
  writeHashTableInline(stream, indent, "CONFIG", &(map->configoptions));
1✔
6585
  writeNumber(stream, indent, "DEBUG", 0, map->debug);
1✔
6586
  writeNumber(stream, indent, "DEFRESOLUTION", 72.0, map->defresolution);
1✔
6587
  writeExtent(stream, indent, "EXTENT", map->extent);
1✔
6588
  writeString(stream, indent, "FONTSET", NULL, map->fontset.filename);
1✔
6589
  MS_INIT_COLOR(c, 255, 255, 255, 255);
1✔
6590
  writeColor(stream, indent, "IMAGECOLOR", &c, &(map->imagecolor));
1✔
6591
  writeString(stream, indent, "IMAGETYPE", NULL, map->imagetype);
1✔
6592
  writeNumber(stream, indent, "MAXSIZE", MS_MAXIMAGESIZE_DEFAULT, map->maxsize);
1✔
6593
  writeString(stream, indent, "NAME", NULL, map->name);
1✔
6594
  writeNumber(stream, indent, "RESOLUTION", 72.0, map->resolution);
1✔
6595
  writeString(stream, indent, "SHAPEPATH", NULL, map->shapepath);
1✔
6596
  writeDimension(stream, indent, "SIZE", map->width, map->height, NULL, NULL);
1✔
6597
  writeKeyword(stream, indent, "STATUS", map->status, 2, MS_ON, "ON", MS_OFF,
1✔
6598
               "OFF");
6599
  writeString(stream, indent, "SYMBOLSET", NULL, map->symbolset.filename);
1✔
6600
  writeKeyword(stream, indent, "UNITS", map->units, 7, MS_INCHES, "INCHES",
1✔
6601
               MS_FEET, "FEET", MS_MILES, "MILES", MS_METERS, "METERS",
6602
               MS_KILOMETERS, "KILOMETERS", MS_NAUTICALMILES, "NAUTICALMILES",
6603
               MS_DD, "DD");
6604
  writeLineFeed(stream);
6605

6606
  writeOutputformat(stream, indent, map);
1✔
6607

6608
  /* write symbol with INLINE tag in mapfile */
6609
  for (i = 0; i < map->symbolset.numsymbols; i++) {
5✔
6610
    if (map->symbolset.symbol[i]->inmapfile)
4✔
6611
      writeSymbol(map->symbolset.symbol[i], stream);
×
6612
  }
6613

6614
  writeProjection(stream, indent, &(map->projection));
1✔
6615

6616
  writeLegend(stream, indent, &(map->legend));
1✔
6617
  writeQueryMap(stream, indent, &(map->querymap));
1✔
6618
  writeReferenceMap(stream, indent, &(map->reference));
1✔
6619
  writeScalebar(stream, indent, &(map->scalebar));
1✔
6620
  writeWeb(stream, indent, &(map->web));
1✔
6621

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

6625
  writeBlockEnd(stream, indent, "MAP");
1✔
6626
}
1✔
6627

6628
char *msWriteMapToString(mapObj *map) {
×
6629
  msIOContext context;
6630
  msIOBuffer buffer;
6631

6632
  context.label = NULL;
×
6633
  context.write_channel = MS_TRUE;
×
6634
  context.readWriteFunc = msIO_bufferWrite;
×
6635
  context.cbData = &buffer;
×
6636
  buffer.data = NULL;
×
6637
  buffer.data_len = 0;
×
6638
  buffer.data_offset = 0;
×
6639

6640
  msIO_installHandlers(NULL, &context, NULL);
×
6641

6642
  writeMap(stdout, 0, map);
×
6643
  msIO_bufferWrite(&buffer, "", 1);
×
6644

6645
  msIO_installHandlers(NULL, NULL, NULL);
×
6646

6647
  return (char *)buffer.data;
×
6648
}
6649

6650
int msSaveMap(mapObj *map, char *filename) {
1✔
6651
  FILE *stream;
6652
  char szPath[MS_MAXPATHLEN];
6653

6654
  if (!map) {
1✔
6655
    msSetError(MS_MISCERR, "Map is undefined.", "msSaveMap()");
×
6656
    return (-1);
×
6657
  }
6658

6659
  if (!filename) {
1✔
6660
    msSetError(MS_MISCERR, "Filename is undefined.", "msSaveMap()");
×
6661
    return (-1);
×
6662
  }
6663

6664
  stream = fopen(msBuildPath(szPath, map->mappath, filename), "w");
1✔
6665
  if (!stream) {
1✔
6666
    msSetErrorWithStatus(MS_IOERR, MS_HTTP_500_INTERNAL_SERVER_ERROR, "(%s)",
×
6667
                         "msSaveMap()", filename);
6668
    return (-1);
×
6669
  }
6670

6671
  writeMap(stream, 0, map);
1✔
6672
  fclose(stream);
1✔
6673

6674
  return (0);
1✔
6675
}
6676

6677
static void writeConfig(FILE *stream, int indent, configObj *config) {
×
6678
  writeBlockBegin(stream, indent, "CONFIG");
×
6679
  writeHashTable(stream, indent, "ENV", &(config->env));
×
6680
  writeHashTable(stream, indent, "MAPS", &(config->maps));
×
6681
  writeBlockEnd(stream, indent, "CONFIG");
×
6682
}
×
6683

6684
int msSaveConfig(configObj *config, const char *filename) {
×
6685
  FILE *stream;
6686

6687
  if (!config) {
×
6688
    msSetError(MS_MISCERR, "Config is undefined.", "msSaveConfigMap()");
×
6689
    return (-1);
×
6690
  }
6691

6692
  if (!filename) {
×
6693
    msSetError(MS_MISCERR, "Filename is undefined.", "msSaveConfigMap()");
×
6694
    return (-1);
×
6695
  }
6696

6697
  stream = fopen(filename, "w");
×
6698
  if (!stream) {
×
6699
    msSetErrorWithStatus(MS_IOERR, MS_HTTP_500_INTERNAL_SERVER_ERROR, "(%s)",
×
6700
                         "msSaveConfig()", filename);
6701
    return (-1);
×
6702
  }
6703

6704
  writeConfig(stream, 0, config);
×
6705
  fclose(stream);
×
6706

6707
  return (0);
×
6708
}
6709

6710
static int loadMapInternal(mapObj *map) {
3,310✔
6711
  int foundMapToken = MS_FALSE;
6712
  int foundBomToken = MS_FALSE;
6713
  int token;
6714

6715
  for (;;) {
6716

6717
    token = msyylex();
50,931✔
6718

6719
    if (!foundBomToken && token == BOM) {
50,931✔
6720
      foundBomToken = MS_TRUE;
6721
      if (!foundMapToken) {
×
6722
        continue; /*skip a leading bom*/
×
6723
      }
6724
    }
6725
    if (!foundMapToken && token != MAP) {
50,931✔
6726
      msSetError(MS_IDENTERR,
2✔
6727
                 "First token must be MAP, this doesn't look like a mapfile.",
6728
                 "msLoadMap()");
6729
      return (MS_FAILURE);
2✔
6730
    }
6731

6732
    switch (token) {
50,929✔
6733

6734
    case (CONFIG): {
90✔
6735
      char *key = NULL, *value = NULL;
90✔
6736

6737
      if (getString(&key) == MS_FAILURE)
90✔
6738
        return MS_FAILURE;
×
6739

6740
      if (getString(&value) == MS_FAILURE) {
90✔
6741
        free(key);
×
6742
        return MS_FAILURE;
×
6743
      }
6744

6745
      if (msSetConfigOption(map, key, value) == MS_FAILURE) {
90✔
6746
        free(key);
×
6747
        free(value);
×
6748
        return MS_FAILURE;
×
6749
      }
6750

6751
      free(key);
90✔
6752
      free(value);
90✔
6753
    } break;
90✔
6754
    case (DEBUG):
108✔
6755
      if ((map->debug = getSymbol(3, MS_ON, MS_OFF, MS_NUMBER)) == -1)
108✔
6756
        return MS_FAILURE;
6757
      if (map->debug == MS_NUMBER) {
108✔
6758
        if (msCheckNumber(msyynumber, MS_NUM_CHECK_RANGE, 0, 5) == MS_FAILURE) {
38✔
6759
          msSetError(MS_MISCERR,
×
6760
                     "Invalid DEBUG level, must be between 0 and 5 (line %d)",
6761
                     "msLoadMap()", msyylineno);
6762
          return (-1);
×
6763
        }
6764
        map->debug = (int)msyynumber;
38✔
6765
      }
6766
      break;
6767
    case (END):
3,306✔
6768
      if (msyyin) {
3,306✔
6769
        fclose(msyyin);
3,302✔
6770
        msyyin = NULL;
3,302✔
6771
      }
6772

6773
      /*** Make config options current ***/
6774
      msApplyMapConfigOptions(map);
3,306✔
6775

6776
      /*** Compute rotated extent info if applicable ***/
6777
      msMapComputeGeotransform(map);
3,306✔
6778

6779
      /*** OUTPUTFORMAT related setup ***/
6780
      if (msPostMapParseOutputFormatSetup(map) == MS_FAILURE)
3,306✔
6781
        return MS_FAILURE;
6782

6783
      if (loadSymbolSet(&(map->symbolset), map) == -1)
3,306✔
6784
        return MS_FAILURE;
6785

6786
      if (resolveSymbolNames(map) == MS_FAILURE)
3,306✔
6787
        return MS_FAILURE;
6788

6789
      if (msLoadFontSet(&(map->fontset), map) == -1)
3,306✔
6790
        return MS_FAILURE;
6791

6792
      return MS_SUCCESS;
6793
      break;
6794
    case (EOF):
×
6795
      msSetError(MS_EOFERR, NULL, "msLoadMap()");
×
6796
      return MS_FAILURE;
×
6797
    case (EXTENT): {
3,143✔
6798
      if (getDouble(&(map->extent.minx), MS_NUM_CHECK_NONE, -1, -1) == -1)
3,143✔
6799
        return MS_FAILURE;
6800
      if (getDouble(&(map->extent.miny), MS_NUM_CHECK_NONE, -1, -1) == -1)
3,143✔
6801
        return MS_FAILURE;
6802
      if (getDouble(&(map->extent.maxx), MS_NUM_CHECK_NONE, -1, -1) == -1)
3,143✔
6803
        return MS_FAILURE;
6804
      if (getDouble(&(map->extent.maxy), MS_NUM_CHECK_NONE, -1, -1) == -1)
3,143✔
6805
        return MS_FAILURE;
6806
      if (!MS_VALID_EXTENT(map->extent)) {
3,143✔
6807
        msSetError(MS_MISCERR,
×
6808
                   "Given map extent is invalid. Check that it "
6809
                   "is in the form: minx, miny, maxx, maxy",
6810
                   "loadMapInternal()");
6811
        return MS_FAILURE;
×
6812
      }
6813
    } break;
6814
    case (ANGLE): {
8✔
6815
      double rotation_angle;
6816
      if (getDouble(&(rotation_angle), MS_NUM_CHECK_RANGE, -360, 360) == -1)
8✔
6817
        return MS_FAILURE;
×
6818
      msMapSetRotation(map, rotation_angle);
8✔
6819
    } break;
8✔
6820
    case (FONTSET):
1,895✔
6821
      if (getString(&map->fontset.filename) == MS_FAILURE)
1,895✔
6822
        return MS_FAILURE;
6823
      break;
6824
    case (IMAGECOLOR):
2,123✔
6825
      if (loadColor(&(map->imagecolor), NULL) != MS_SUCCESS)
2,123✔
6826
        return MS_FAILURE;
6827
      break;
6828
    case (IMAGETYPE):
1,457✔
6829
      msFree(map->imagetype);
1,457✔
6830
      map->imagetype = getToken();
1,457✔
6831
      break;
1,457✔
6832
    case (LATLON):
×
6833
      if (loadProjection(&map->latlon, map) == -1)
×
6834
        return MS_FAILURE;
6835
      break;
6836
    case (LAYER):
13,959✔
6837
      if (msGrowMapLayers(map) == NULL)
13,959✔
6838
        return MS_FAILURE;
6839
      if (initLayer((GET_LAYER(map, map->numlayers)), map) == -1)
13,959✔
6840
        return MS_FAILURE;
6841
      if (loadLayer((GET_LAYER(map, map->numlayers)), map) == -1)
13,959✔
6842
        return MS_FAILURE;
6843
      GET_LAYER(map, map->numlayers)->index =
13,959✔
6844
          map->numlayers; /* save the index */
6845
      /* Update the layer order list with the layer's index. */
6846
      map->layerorder[map->numlayers] = map->numlayers;
13,959✔
6847
      map->numlayers++;
13,959✔
6848
      break;
13,959✔
6849
    case (OUTPUTFORMAT):
1,399✔
6850
      if (loadOutputFormat(map) == -1)
1,399✔
6851
        return MS_FAILURE;
6852
      break;
6853
    case (LEGEND):
480✔
6854
      if (loadLegend(&(map->legend), map) == -1)
480✔
6855
        return MS_FAILURE;
6856
      break;
6857
    case (MAP):
6858
      foundMapToken = MS_TRUE;
6859
      break;
6860
    case (MAXSIZE):
273✔
6861
      if (getInteger(&(map->maxsize), MS_NUM_CHECK_GT, 0, -1) == -1)
273✔
6862
        return MS_FAILURE;
6863
      break;
6864
    case (NAME):
2,746✔
6865
      free(map->name);
2,746✔
6866
      map->name = NULL; /* erase default */
2,746✔
6867
      if (getString(&map->name) == MS_FAILURE)
2,746✔
6868
        return MS_FAILURE;
6869
      break;
6870
    case (PROJECTION):
2,371✔
6871
      if (loadProjection(&map->projection, map) == -1)
2,371✔
6872
        return MS_FAILURE;
6873
      break;
6874
    case (QUERYMAP):
165✔
6875
      if (loadQueryMap(&(map->querymap), map) == -1)
165✔
6876
        return MS_FAILURE;
6877
      break;
6878
    case (REFERENCE):
×
6879
      if (loadReferenceMap(&(map->reference), map) == -1)
×
6880
        return MS_FAILURE;
6881
      break;
6882
    case (RESOLUTION):
25✔
6883
      if (getDouble(&(map->resolution), MS_NUM_CHECK_RANGE, MS_RESOLUTION_MIN,
25✔
6884
                    MS_RESOLUTION_MAX) == -1)
6885
        return MS_FAILURE;
6886
      break;
6887
    case (DEFRESOLUTION):
15✔
6888
      if (getDouble(&(map->defresolution), MS_NUM_CHECK_RANGE,
15✔
6889
                    MS_RESOLUTION_MIN, MS_RESOLUTION_MAX) == -1)
6890
        return MS_FAILURE;
6891
      break;
6892
    case (SCALE):
×
6893
    case (SCALEDENOM):
6894
      if (getDouble(&(map->scaledenom), MS_NUM_CHECK_GTE, 1, -1) == -1)
×
6895
        return MS_FAILURE;
6896
      break;
6897
    case (SCALEBAR):
418✔
6898
      if (loadScalebar(&(map->scalebar)) == -1)
418✔
6899
        return MS_FAILURE;
6900
      break;
6901
    case (SHAPEPATH):
1,813✔
6902
      if (getString(&map->shapepath) == MS_FAILURE)
1,813✔
6903
        return MS_FAILURE;
6904
      break;
6905
    case (SIZE):
3,168✔
6906
      if (getInteger(&(map->width), MS_NUM_CHECK_RANGE, 1, map->maxsize) == -1)
3,168✔
6907
        return MS_FAILURE;
6908
      if (getInteger(&(map->height), MS_NUM_CHECK_RANGE, 1, map->maxsize) == -1)
3,168✔
6909
        return MS_FAILURE;
6910
      break;
6911
    case (STATUS):
2,151✔
6912
      if ((map->status = getSymbol(2, MS_ON, MS_OFF)) == -1)
2,151✔
6913
        return MS_FAILURE;
6914
      break;
6915
    case (SYMBOL):
605✔
6916
      if (msGrowSymbolSet(&(map->symbolset)) == NULL)
605✔
6917
        return MS_FAILURE;
6918
      if ((loadSymbol(map->symbolset.symbol[map->symbolset.numsymbols],
605✔
6919
                      map->mappath) == -1)) {
6920
        msFreeSymbol(map->symbolset.symbol[map->symbolset.numsymbols]);
×
6921
        free(map->symbolset.symbol[map->symbolset.numsymbols]);
×
6922
        map->symbolset.symbol[map->symbolset.numsymbols] = NULL;
×
6923
        return MS_FAILURE;
×
6924
      }
6925
      map->symbolset.symbol[map->symbolset.numsymbols]->inmapfile = MS_TRUE;
605✔
6926
      map->symbolset.numsymbols++;
605✔
6927
      break;
605✔
6928
    case (SYMBOLSET):
1,630✔
6929
      if (getString(&map->symbolset.filename) == MS_FAILURE)
1,630✔
6930
        return MS_FAILURE;
6931
      break;
6932
    case (UNITS):
1,637✔
6933
      if ((int)(map->units =
1,637✔
6934
                    getSymbol(7, MS_INCHES, MS_FEET, MS_MILES, MS_METERS,
1,637✔
6935
                              MS_KILOMETERS, MS_NAUTICALMILES, MS_DD)) == -1)
6936
        return MS_FAILURE;
6937
      break;
6938
    case (WEB):
2,636✔
6939
      if (loadWeb(&(map->web), map) == -1)
2,636✔
6940
        return MS_FAILURE;
6941
      break;
6942
    default:
×
6943
      msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)",
×
6944
                 "msLoadMap()", msyystring_buffer, msyylineno);
6945
      return MS_FAILURE;
×
6946
    }
6947
  } /* next token */
6948
}
6949

6950
static bool msGetCWD(char *szBuffer, size_t nBufferSize,
3,310✔
6951
                     const char *pszFunctionName) {
6952
  if (NULL == getcwd(szBuffer, nBufferSize)) {
3,310✔
6953
#ifndef _WIN32
6954
    if (errno == EACCES)
×
6955
      msSetError(MS_MISCERR,
×
6956
                 "getcwd() failed with EACCES: you may need to force the "
6957
                 "current directory in the mapserver launcher "
6958
                 "(e.g -d option of spawn-fcgi)",
6959
                 pszFunctionName);
6960
    else if (errno == ENAMETOOLONG)
×
6961
      msSetError(MS_MISCERR, "getcwd() returned a too long path",
×
6962
                 pszFunctionName);
6963
    else
6964
      msSetError(MS_MISCERR, "getcwd() failed with errno code %d",
×
6965
                 pszFunctionName, errno);
6966
#else
6967
    msSetError(MS_MISCERR, "getcwd() returned a too long path",
6968
               pszFunctionName);
6969
#endif
6970
    return FALSE;
×
6971
  }
6972
  return TRUE;
6973
}
6974

6975
/*
6976
 * Apply any SLD styles referenced in a LAYER's STYLEITEM
6977
 */
6978
static void applyStyleItemToLayer(mapObj *map) {
3,306✔
6979

6980
  // applying SLD can create cloned layers so store the original layer count
6981
  int layerCount = map->numlayers;
3,306✔
6982

6983
  for (int i = 0; i < layerCount; i++) {
17,265✔
6984
    layerObj *layer = GET_LAYER(map, i);
13,959✔
6985

6986
    if (layer->styleitem && STARTS_WITH_CI(layer->styleitem, "sld://")) {
13,959✔
6987
      const char *filename = layer->styleitem + strlen("sld://");
123✔
6988

6989
      if (*filename == '\0') {
123✔
6990
        msSetErrorWithStatus(MS_IOERR, MS_HTTP_500_INTERNAL_SERVER_ERROR,
×
6991
                             "Empty SLD filename: \"%s\".",
6992
                             "applyLayerDefaultSubstitutions()",
6993
                             layer->styleitem);
6994
      } else {
6995
        msSLDApplyFromFile(map, layer, filename);
123✔
6996
      }
6997
    }
6998
  }
6999
}
3,306✔
7000

7001
/*
7002
** Sets up string-based mapfile loading and calls loadMapInternal to do the
7003
*work.
7004
*/
7005
mapObj *msLoadMapFromString(char *buffer, char *new_mappath,
4✔
7006
                            const configObj *config) {
7007
  mapObj *map;
7008
  struct mstimeval starttime = {0}, endtime = {0};
4✔
7009
  char szPath[MS_MAXPATHLEN], szCWDPath[MS_MAXPATHLEN];
7010
  char *mappath = NULL;
7011
  int debuglevel;
7012

7013
  debuglevel = (int)msGetGlobalDebugLevel();
4✔
7014

7015
  if (debuglevel >= MS_DEBUGLEVEL_TUNING) {
4✔
7016
    /* In debug mode, track time spent loading/parsing mapfile. */
7017
    msGettimeofday(&starttime, NULL);
×
7018
  }
7019

7020
  if (!buffer) {
4✔
7021
    msSetError(MS_MISCERR, "No buffer to load.", "msLoadMapFromString()");
×
7022
    return (NULL);
×
7023
  }
7024

7025
  /*
7026
  ** Allocate mapObj structure
7027
  */
7028
  map = (mapObj *)calloc(1, sizeof(mapObj));
4✔
7029
  MS_CHECK_ALLOC(map, sizeof(mapObj), NULL);
4✔
7030

7031
  if (initMap(map) == -1) { /* initialize this map */
4✔
7032
    msFreeMap(map);
×
7033
    return (NULL);
×
7034
  }
7035

7036
  map->config = config; // create a read-only reference
4✔
7037

7038
  msAcquireLock(TLOCK_PARSER); /* might need to move this lock a bit higher, yup
4✔
7039
                                  (bug 2108) */
7040

7041
  msyystate = MS_TOKENIZE_STRING;
4✔
7042
  msyystring = buffer;
4✔
7043
  msyylex(); /* sets things up, but doesn't process any tokens */
4✔
7044

7045
  msyylineno = 1; /* start at line 1 (do lines mean anything here?) */
4✔
7046

7047
  /* If new_mappath is provided then use it, otherwise use the CWD */
7048
  if (!msGetCWD(szCWDPath, MS_MAXPATHLEN, "msLoadMapFromString()")) {
4✔
7049
    msFreeMap(map);
×
7050
    msReleaseLock(TLOCK_PARSER);
×
7051
    return (NULL);
×
7052
  }
7053
  if (new_mappath) {
4✔
7054
    mappath = msStrdup(new_mappath);
×
7055
    map->mappath = msStrdup(msBuildPath(szPath, szCWDPath, mappath));
×
7056
  } else
7057
    map->mappath = msStrdup(szCWDPath);
4✔
7058

7059
  msyybasepath = map->mappath; /* for INCLUDEs */
4✔
7060

7061
  if (loadMapInternal(map) != MS_SUCCESS) {
4✔
7062
    msFreeMap(map);
×
7063
    msReleaseLock(TLOCK_PARSER);
×
7064
    if (mappath != NULL)
×
7065
      free(mappath);
×
7066
    return NULL;
×
7067
  }
7068

7069
  if (mappath != NULL)
4✔
7070
    free(mappath);
×
7071
  msyylex_destroy();
4✔
7072

7073
  msReleaseLock(TLOCK_PARSER);
4✔
7074

7075
  applyStyleItemToLayer(map);
4✔
7076

7077
  if (debuglevel >= MS_DEBUGLEVEL_TUNING) {
4✔
7078
    /* In debug mode, report time spent loading/parsing mapfile. */
7079
    msGettimeofday(&endtime, NULL);
×
7080
    msDebug("msLoadMapFromString(): %.3fs\n",
×
7081
            (endtime.tv_sec + endtime.tv_usec / 1.0e6) -
×
7082
                (starttime.tv_sec + starttime.tv_usec / 1.0e6));
×
7083
  }
7084

7085
  if (resolveSymbolNames(map) == MS_FAILURE)
4✔
7086
    return NULL;
7087

7088
  return map;
7089
}
7090

7091
/*
7092
** Sets up file-based mapfile loading and calls loadMapInternal to do the work.
7093
*/
7094
mapObj *msLoadMap(const char *filename, const char *new_mappath,
3,314✔
7095
                  const configObj *config) {
7096
  mapObj *map;
7097
  struct mstimeval starttime = {0}, endtime = {0};
3,314✔
7098
  char szPath[MS_MAXPATHLEN], szCWDPath[MS_MAXPATHLEN];
7099
  int debuglevel;
7100

7101
  debuglevel = (int)msGetGlobalDebugLevel();
3,314✔
7102

7103
  if (debuglevel >= MS_DEBUGLEVEL_TUNING) {
3,314✔
7104
    /* In debug mode, track time spent loading/parsing mapfile. */
7105
    msGettimeofday(&starttime, NULL);
×
7106
  }
7107

7108
  if (!filename) {
3,314✔
7109
    msSetError(MS_MISCERR, "Filename is undefined.", "msLoadMap()");
×
7110
    return (NULL);
×
7111
  }
7112

7113
  const char *ms_mapfile_pattern =
7114
      CPLGetConfigOption("MS_MAPFILE_PATTERN", MS_DEFAULT_MAPFILE_PATTERN);
3,314✔
7115
  if (msEvalRegex(ms_mapfile_pattern, filename) != MS_TRUE) {
3,314✔
7116
    msSetError(MS_REGEXERR, "Filename validation failed.", "msLoadMap()");
×
7117
    return (NULL);
×
7118
  }
7119

7120
  /*
7121
  ** Allocate mapObj structure
7122
  */
7123
  map = (mapObj *)calloc(1, sizeof(mapObj));
3,314✔
7124
  MS_CHECK_ALLOC(map, sizeof(mapObj), NULL);
3,314✔
7125

7126
  if (initMap(map) == -1) { /* initialize this map */
3,314✔
7127
    msFreeMap(map);
×
7128
    return (NULL);
×
7129
  }
7130

7131
  map->config = config; // create a read-only reference
3,314✔
7132

7133
  msAcquireLock(TLOCK_PARSER); /* Steve: might need to move this lock a bit
3,314✔
7134
                                  higher; Umberto: done */
7135

7136
#ifdef USE_XMLMAPFILE
7137
  /* If the mapfile is an xml mapfile, transform it */
7138
  const char *ms_xmlmapfile_xslt =
7139
      CPLGetConfigOption("MS_XMLMAPFILE_XSLT", NULL);
7140
  if (ms_xmlmapfile_xslt &&
7141
      (msEvalRegex(MS_DEFAULT_XMLMAPFILE_PATTERN, filename) == MS_TRUE)) {
7142

7143
    msyyin = tmpfile();
7144
    if (msyyin == NULL) {
7145
      msSetErrorWithStatus(MS_IOERR, MS_HTTP_500_INTERNAL_SERVER_ERROR,
7146
                           "tmpfile() failed to create temporary file",
7147
                           "msLoadMap()");
7148
      msReleaseLock(TLOCK_PARSER);
7149
      msFreeMap(map);
7150
      return NULL;
7151
    }
7152

7153
    if (msTransformXmlMapfile(ms_xmlmapfile_xslt, filename, msyyin) !=
7154
        MS_SUCCESS) {
7155
      fclose(msyyin);
7156
      msFreeMap(map);
7157
      return NULL;
7158
    }
7159
    fseek(msyyin, 0, SEEK_SET);
7160
  } else {
7161
#endif
7162
    if ((msyyin = fopen(filename, "r")) == NULL) {
3,314✔
7163
      msSetErrorWithStatus(MS_IOERR, MS_HTTP_500_INTERNAL_SERVER_ERROR, "(%s)",
8✔
7164
                           "msLoadMap()", filename);
7165
      msReleaseLock(TLOCK_PARSER);
8✔
7166
      msFreeMap(map);
8✔
7167
      return NULL;
8✔
7168
    }
7169
#ifdef USE_XMLMAPFILE
7170
  }
7171
#endif
7172

7173
  msyystate = MS_TOKENIZE_FILE;
3,306✔
7174
  msyylex(); /* sets things up, but doesn't process any tokens */
3,306✔
7175

7176
  msyyrestart(msyyin); /* start at line beginning, line 1 */
3,306✔
7177
  msyylineno = 1;
3,306✔
7178

7179
  /* If new_mappath is provided then use it, otherwise use the location */
7180
  /* of the mapfile as the default path */
7181
  if (!msGetCWD(szCWDPath, MS_MAXPATHLEN, "msLoadMap()")) {
3,306✔
7182
    msReleaseLock(TLOCK_PARSER);
×
7183
    msFreeMap(map);
×
7184
    return NULL;
×
7185
  }
7186

7187
  if (new_mappath)
3,306✔
7188
    map->mappath = msStrdup(msBuildPath(szPath, szCWDPath, new_mappath));
×
7189
  else {
7190
    char *path = msGetPath(filename);
3,306✔
7191
    map->mappath = msStrdup(msBuildPath(szPath, szCWDPath, path));
3,306✔
7192
    free(path);
3,306✔
7193
  }
7194

7195
  msyybasepath = map->mappath; /* for INCLUDEs */
3,306✔
7196

7197
  if (loadMapInternal(map) != MS_SUCCESS) {
3,306✔
7198
    msFreeMap(map);
4✔
7199
    if (msyyin) {
4✔
7200
      msyycleanup_includes();
4✔
7201
      fclose(msyyin);
4✔
7202
      msyyin = NULL;
4✔
7203
    }
7204
    msReleaseLock(TLOCK_PARSER);
4✔
7205
    return NULL;
4✔
7206
  }
7207
  msReleaseLock(TLOCK_PARSER);
3,302✔
7208

7209
  applyStyleItemToLayer(map);
3,302✔
7210

7211
  if (debuglevel >= MS_DEBUGLEVEL_TUNING) {
3,302✔
7212
    /* In debug mode, report time spent loading/parsing mapfile. */
7213
    msGettimeofday(&endtime, NULL);
×
7214
    msDebug("msLoadMap(): %.3fs\n",
×
7215
            (endtime.tv_sec + endtime.tv_usec / 1.0e6) -
×
7216
                (starttime.tv_sec + starttime.tv_usec / 1.0e6));
×
7217
  }
7218

7219
  return map;
7220
}
7221

7222
static void hashTableSubstituteString(hashTableObj *hash, const char *from,
487✔
7223
                                      const char *to) {
7224
  const char *key, *val;
7225
  char *new_val;
7226
  key = msFirstKeyFromHashTable(hash);
487✔
7227
  while (key != NULL) {
1,534✔
7228
    val = msLookupHashTable(hash, key);
1,047✔
7229
    if (strcasestr(val, from)) {
1,047✔
7230
      new_val = msCaseReplaceSubstring(msStrdup(val), from, to);
75✔
7231
      msInsertHashTable(hash, key, new_val);
75✔
7232
      msFree(new_val);
75✔
7233
    }
7234
    key = msNextKeyFromHashTable(hash, key);
1,047✔
7235
  }
7236
}
487✔
7237

7238
static void classSubstituteString(classObj *class, const char *from,
164✔
7239
                                  const char *to) {
7240
  if (class->expression.string)
164✔
7241
    class->expression.string =
16✔
7242
        msCaseReplaceSubstring(class->expression.string, from, to);
16✔
7243
  if (class->text.string)
164✔
7244
    class->text.string = msCaseReplaceSubstring(class->text.string, from, to);
×
7245
  if (class->title)
164✔
7246
    class->title = msCaseReplaceSubstring(class->title, from, to);
×
7247
}
164✔
7248

7249
static void layerSubstituteString(layerObj *layer, const char *from,
220✔
7250
                                  const char *to) {
7251
  int c;
7252
  if (layer->data)
220✔
7253
    layer->data = msCaseReplaceSubstring(layer->data, from, to);
218✔
7254
  if (layer->tileindex)
220✔
7255
    layer->tileindex = msCaseReplaceSubstring(layer->tileindex, from, to);
×
7256
  if (layer->connection)
220✔
7257
    layer->connection = msCaseReplaceSubstring(layer->connection, from, to);
10✔
7258
  if (layer->filter.string)
220✔
7259
    layer->filter.string =
2✔
7260
        msCaseReplaceSubstring(layer->filter.string, from, to);
2✔
7261
  if (layer->mask)
220✔
7262
    layer->mask = msCaseReplaceSubstring(layer->mask, from, to); // new for 8.0
×
7263

7264
  /* The bindvalues are most useful when able to substitute values from the URL
7265
   */
7266
  hashTableSubstituteString(&layer->bindvals, from, to);
220✔
7267
  hashTableSubstituteString(&layer->metadata, from, to);
220✔
7268
  msLayerSubstituteProcessing(layer, from, to);
220✔
7269
  for (c = 0; c < layer->numclasses; c++) {
340✔
7270
    classSubstituteString(layer->class[c], from, to);
120✔
7271
  }
7272
}
220✔
7273

7274
static void mapSubstituteString(mapObj *map, const char *from, const char *to) {
19✔
7275
  int l;
7276
  for (l = 0; l < map->numlayers; l++) {
83✔
7277
    layerSubstituteString(GET_LAYER(map, l), from, to);
64✔
7278
  }
7279

7280
  /* output formats (#3751) */
7281
  for (l = 0; l < map->numoutputformats; l++) {
39✔
7282
    int o;
7283
    for (o = 0; o < map->outputformatlist[l]->numformatoptions; o++) {
26✔
7284
      map->outputformatlist[l]->formatoptions[o] = msCaseReplaceSubstring(
6✔
7285
          map->outputformatlist[l]->formatoptions[o], from, to);
6✔
7286
    }
7287
  }
7288

7289
  hashTableSubstituteString(&map->web.metadata, from, to);
19✔
7290
  if (map->web.template)
19✔
7291
    map->web.template = msCaseReplaceSubstring(map->web.template, from, to);
×
7292
}
19✔
7293

7294
static void applyOutputFormatDefaultSubstitutions(outputFormatObj *format,
7,564✔
7295
                                                  const char *option,
7296
                                                  hashTableObj *table) {
7297
  const char *filename;
7298

7299
  filename = msGetOutputFormatOption(format, option, NULL);
7,564✔
7300
  if (filename && strlen(filename) > 0) {
7,564✔
7301
    char *tmpfilename = msStrdup(filename);
535✔
7302
    const char *default_key = msFirstKeyFromHashTable(table);
535✔
7303
    while (default_key) {
547✔
7304
      if (!strncasecmp(default_key, "default_", 8)) {
12✔
7305
        char *new_filename = NULL;
7306
        size_t buffer_size = (strlen(default_key) - 5);
6✔
7307
        char *tag = (char *)msSmallMalloc(buffer_size);
6✔
7308
        snprintf(tag, buffer_size, "%%%s%%", &(default_key[8]));
6✔
7309

7310
        new_filename = msStrdup(tmpfilename);
6✔
7311
        new_filename = msCaseReplaceSubstring(
6✔
7312
            new_filename, tag, msLookupHashTable(table, default_key));
7313
        free(tag);
6✔
7314

7315
        msSetOutputFormatOption(format, option, new_filename);
6✔
7316
        free(new_filename);
6✔
7317
      }
7318
      default_key = msNextKeyFromHashTable(table, default_key);
12✔
7319
    }
7320
    msFree(tmpfilename);
535✔
7321
  }
7322
  return;
7,564✔
7323
}
7324

7325
static void applyClassDefaultSubstitutions(classObj *class,
11,803✔
7326
                                           hashTableObj *table) {
7327
  const char *default_key = msFirstKeyFromHashTable(table);
11,803✔
7328
  while (default_key) {
11,816✔
7329
    if (!strncasecmp(default_key, "default_", 8)) {
13✔
7330
      size_t buffer_size = (strlen(default_key) - 5);
×
7331
      char *tag = (char *)msSmallMalloc(buffer_size);
×
7332
      snprintf(tag, buffer_size, "%%%s%%", &(default_key[8]));
×
7333

7334
      classSubstituteString(class, tag, msLookupHashTable(table, default_key));
×
7335
      free(tag);
×
7336
    }
7337
    default_key = msNextKeyFromHashTable(table, default_key);
13✔
7338
  }
7339
  return;
11,803✔
7340
}
7341

7342
static void applyLayerDefaultSubstitutions(layerObj *layer,
21,540✔
7343
                                           hashTableObj *table) {
7344
  int i;
7345
  const char *default_key = msFirstKeyFromHashTable(table);
21,540✔
7346
  while (default_key) {
23,163✔
7347
    if (!strncasecmp(default_key, "default_", 8)) {
1,623✔
7348
      size_t buffer_size = (strlen(default_key) - 5);
114✔
7349
      const char *to = msLookupHashTable(table, default_key);
114✔
7350
      char *tag = (char *)msSmallMalloc(buffer_size);
114✔
7351
      snprintf(tag, buffer_size, "%%%s%%", &(default_key[8]));
114✔
7352

7353
      for (i = 0; i < layer->numclasses; i++) {
156✔
7354
        classSubstituteString(layer->class[i], tag, to);
42✔
7355
      }
7356
      layerSubstituteString(layer, tag, to);
114✔
7357
      free(tag);
114✔
7358
    }
7359
    default_key = msNextKeyFromHashTable(table, default_key);
1,623✔
7360
  }
7361

7362
  return;
21,540✔
7363
}
7364

7365
static void applyHashTableDefaultSubstitutions(hashTableObj *hashTab,
2,790✔
7366
                                               hashTableObj *table) {
7367
  const char *default_key = msFirstKeyFromHashTable(table);
2,790✔
7368
  while (default_key) {
2,934✔
7369
    if (!strncasecmp(default_key, "default_", 8)) {
144✔
7370
      size_t buffer_size = (strlen(default_key) - 5);
28✔
7371
      const char *to = msLookupHashTable(table, default_key);
28✔
7372
      char *tag = (char *)msSmallMalloc(buffer_size);
28✔
7373
      snprintf(tag, buffer_size, "%%%s%%", &(default_key[8]));
28✔
7374

7375
      hashTableSubstituteString(hashTab, tag, to);
28✔
7376
      free(tag);
28✔
7377
    }
7378
    default_key = msNextKeyFromHashTable(table, default_key);
144✔
7379
  }
7380
  return;
2,790✔
7381
}
7382

7383
/*
7384
** Loop through layer metadata for keys that have a default_%key% pattern to
7385
*replace
7386
** remaining %key% entries by their default value.
7387
*/
7388
void msApplyDefaultSubstitutions(mapObj *map) {
2,790✔
7389
  int i, j;
7390

7391
  /* output formats (#3751) */
7392
  for (i = 0; i < map->numoutputformats; i++) {
6,572✔
7393
    applyOutputFormatDefaultSubstitutions(map->outputformatlist[i], "filename",
3,782✔
7394
                                          &(map->web.validation));
7395
    applyOutputFormatDefaultSubstitutions(map->outputformatlist[i], "JSONP",
3,782✔
7396
                                          &(map->web.validation));
7397
  }
7398

7399
  for (i = 0; i < map->numlayers; i++) {
13,560✔
7400
    layerObj *layer = GET_LAYER(map, i);
10,770✔
7401

7402
    for (j = 0; j < layer->numclasses;
22,573✔
7403
         j++) { /* class settings take precedence...  */
11,803✔
7404
      classObj *class = GET_CLASS(map, i, j);
11,803✔
7405
      applyClassDefaultSubstitutions(class, &(class->validation));
11,803✔
7406
    }
7407

7408
    applyLayerDefaultSubstitutions(
10,770✔
7409
        layer, &(layer->validation)); /* ...then layer settings... */
7410
    applyLayerDefaultSubstitutions(
10,770✔
7411
        layer, &(map->web.validation)); /* ...and finally web settings */
7412
  }
7413
  applyHashTableDefaultSubstitutions(&map->web.metadata,
2,790✔
7414
                                     &(map->web.validation));
7415
}
2,790✔
7416

7417
char *_get_param_value(const char *key, char **names, char **values,
1,149✔
7418
                       int npairs) {
7419
  if (npairs <= 0)
1,149✔
7420
    return NULL; // bail, no point searching
7421

7422
  if (getenv(key)) { /* environment override */
1,139✔
7423
    return getenv(key);
1✔
7424
  }
7425
  while (npairs) {
4,614✔
7426
    npairs--;
3,547✔
7427
    if (strcasecmp(key, names[npairs]) == 0) {
3,547✔
7428
      return values[npairs];
71✔
7429
    }
7430
  }
7431
  return NULL;
7432
}
7433

7434
void msApplySubstitutions(mapObj *map, char **names, char **values,
2,186✔
7435
                          int npairs) {
7436
  int l;
7437
  const char *key, *value, *validation;
7438
  char *tag;
7439
  for (l = 0; l < map->numlayers; l++) {
11,267✔
7440
    int c;
7441
    layerObj *lp = GET_LAYER(map, l);
9,081✔
7442
    for (c = 0; c < lp->numclasses; c++) {
19,159✔
7443
      classObj *cp = lp->class[c];
10,078✔
7444
      key = NULL;
7445
      while ((key = msNextKeyFromHashTable(&cp->validation, key))) {
10,091✔
7446
        value = _get_param_value(key, names, values, npairs);
13✔
7447
        if (!value)
13✔
7448
          continue; /*parameter was not in url*/
11✔
7449
        validation = msLookupHashTable(&cp->validation, key);
2✔
7450
        if (msEvalRegex(validation, value)) {
2✔
7451
          /* we've found a substitution and it validates correctly, now let's
7452
           * apply it */
7453
          tag = msSmallMalloc(strlen(key) + 3);
2✔
7454
          sprintf(tag, "%%%s%%", key);
7455
          classSubstituteString(cp, tag, value);
2✔
7456
          free(tag);
2✔
7457
        } else {
7458
          msSetError(MS_REGEXERR, "Parameter pattern validation failed.",
×
7459
                     "msApplySubstitutions()");
7460
          if (map->debug || lp->debug) {
×
7461
            msDebug("layer (%s), class %d: failed to validate (%s=%s) for "
×
7462
                    "regex (%s)\n",
7463
                    lp->name, c, key, value, validation);
7464
          }
7465
        }
7466
      }
7467
    }
7468
    key = NULL;
7469
    while ((key = msNextKeyFromHashTable(&lp->validation, key))) {
10,073✔
7470
      value = _get_param_value(key, names, values, npairs);
992✔
7471
      if (!value)
992✔
7472
        continue; /*parameter was not in url*/
948✔
7473
      validation = msLookupHashTable(&lp->validation, key);
44✔
7474
      if (msEvalRegex(validation, value)) {
44✔
7475
        /* we've found a substitution and it validates correctly, now let's
7476
         * apply it */
7477
        tag = msSmallMalloc(strlen(key) + 3);
42✔
7478
        sprintf(tag, "%%%s%%", key);
7479
        layerSubstituteString(lp, tag, value);
42✔
7480
        free(tag);
42✔
7481
      } else {
7482
        msSetError(MS_REGEXERR, "Parameter pattern validation failed.",
2✔
7483
                   "msApplySubstitutions()");
7484
        if (map->debug || lp->debug) {
2✔
7485
          msDebug("layer (%s): failed to validate (%s=%s) for regex (%s)\n",
×
7486
                  lp->name, key, value, validation);
7487
        }
7488
      }
7489
    }
7490
  }
7491
  key = NULL;
7492
  while ((key = msNextKeyFromHashTable(&map->web.validation, key))) {
2,330✔
7493
    value = _get_param_value(key, names, values, npairs);
144✔
7494
    if (!value)
144✔
7495
      continue; /*parameter was not in url*/
118✔
7496
    validation = msLookupHashTable(&map->web.validation, key);
26✔
7497
    if (msEvalRegex(validation, value)) {
26✔
7498
      /* we've found a substitution and it validates correctly, now let's apply
7499
       * it */
7500
      tag = msSmallMalloc(strlen(key) + 3);
19✔
7501
      sprintf(tag, "%%%s%%", key);
7502
      mapSubstituteString(map, tag, value);
19✔
7503
      free(tag);
19✔
7504
    } else {
7505
      msSetError(MS_REGEXERR, "Parameter pattern validation failed.",
7✔
7506
                 "msApplySubstitutions()");
7507
      if (map->debug) {
7✔
7508
        msDebug("failed to validate (%s=%s) for regex (%s)\n", key, value,
×
7509
                validation);
7510
      }
7511
    }
7512
  }
7513
}
2,186✔
7514

7515
/*
7516
** Returns an array with one entry per mapfile token.  Useful to manipulate
7517
** mapfiles in MapScript.
7518
**
7519
** The returned array should be freed using msFreeCharArray().
7520
*/
7521
static char **tokenizeMapInternal(char *filename, int *ret_numtokens) {
×
7522
  char **tokens = NULL;
7523
  int numtokens = 0, numtokens_allocated = 0;
7524
  size_t buffer_size = 0;
7525

7526
  *ret_numtokens = 0;
×
7527

7528
  if (!filename) {
×
7529
    msSetError(MS_MISCERR, "Filename is undefined.", "msTokenizeMap()");
×
7530
    return NULL;
×
7531
  }
7532

7533
  /*
7534
  ** Check map filename to make sure it's legal
7535
  */
7536
  const char *ms_mapfile_pattern =
7537
      CPLGetConfigOption("MS_MAPFILE_PATTERN", MS_DEFAULT_MAPFILE_PATTERN);
×
7538
  if (msEvalRegex(ms_mapfile_pattern, filename) != MS_TRUE) {
×
7539
    msSetError(MS_REGEXERR, "Filename validation failed.", "msTokenizeMap()");
×
7540
    return (NULL);
×
7541
  }
7542

7543
  if ((msyyin = fopen(filename, "r")) == NULL) {
×
7544
    msSetErrorWithStatus(MS_IOERR, MS_HTTP_500_INTERNAL_SERVER_ERROR, "(%s)",
×
7545
                         "msTokenizeMap()", filename);
7546
    return NULL;
×
7547
  }
7548

7549
  msyystate = MS_TOKENIZE_FILE; /* restore lexer state to INITIAL, and do return
×
7550
                                   comments */
7551
  msyylex();
×
7552
  msyyreturncomments = 1; /* want all tokens, including comments */
×
7553

7554
  msyyrestart(msyyin); /* start at line beginning, line 1 */
×
7555
  msyylineno = 1;
×
7556

7557
  numtokens = 0;
7558
  numtokens_allocated = 256;
7559
  tokens = (char **)malloc(numtokens_allocated * sizeof(char *));
×
7560
  if (tokens == NULL) {
×
7561
    msSetError(MS_MEMERR, NULL, "msTokenizeMap()");
×
7562
    fclose(msyyin);
×
7563
    return NULL;
×
7564
  }
7565

7566
  for (;;) {
7567

7568
    if (numtokens_allocated <= numtokens) {
×
7569
      numtokens_allocated *=
×
7570
          2; /* double size of the array every time we reach the limit */
7571
      char **tokensNew =
7572
          (char **)realloc(tokens, numtokens_allocated * sizeof(char *));
×
7573
      if (tokensNew == NULL) {
×
7574
        msSetError(MS_MEMERR, "Realloc() error.", "msTokenizeMap()");
×
7575
        fclose(msyyin);
×
7576
        for (int i = 0; i < numtokens; i++)
×
7577
          msFree(tokens[i]);
×
7578
        msFree(tokens);
×
7579
        return NULL;
×
7580
      }
7581
      tokens = tokensNew;
7582
    }
7583

7584
    switch (msyylex()) {
×
7585
    case (EOF): /* This is the normal way out... cleanup and exit */
×
7586
      fclose(msyyin);
×
7587
      *ret_numtokens = numtokens;
×
7588
      return (tokens);
×
7589
      break;
7590
    case (MS_STRING):
×
7591
      buffer_size = strlen(msyystring_buffer) + 2 + 1;
×
7592
      tokens[numtokens] = (char *)msSmallMalloc(buffer_size);
×
7593
      snprintf(tokens[numtokens], buffer_size, "\"%s\"", msyystring_buffer);
×
7594
      break;
7595
    case (MS_EXPRESSION):
×
7596
      buffer_size = strlen(msyystring_buffer) + 2 + 1;
×
7597
      tokens[numtokens] = (char *)msSmallMalloc(buffer_size);
×
7598
      snprintf(tokens[numtokens], buffer_size, "(%s)", msyystring_buffer);
×
7599
      break;
7600
    case (MS_REGEX):
×
7601
      buffer_size = strlen(msyystring_buffer) + 2 + 1;
×
7602
      tokens[numtokens] = (char *)msSmallMalloc(buffer_size);
×
7603
      snprintf(tokens[numtokens], buffer_size, "/%s/", msyystring_buffer);
×
7604
      break;
7605
    default:
×
7606
      tokens[numtokens] = msStrdup(msyystring_buffer);
×
7607
      break;
×
7608
    }
7609

7610
    if (tokens[numtokens] == NULL) {
×
7611
      int i;
7612
      msSetError(MS_MEMERR, NULL, "msTokenizeMap()");
×
7613
      fclose(msyyin);
×
7614
      for (i = 0; i < numtokens; i++)
×
7615
        msFree(tokens[i]);
×
7616
      msFree(tokens);
×
7617
      return NULL;
×
7618
    }
7619

7620
    numtokens++;
×
7621
  }
7622

7623
  return NULL; /* should never get here */
7624
}
7625

7626
/*
7627
** Wraps tokenizeMapInternal
7628
*/
7629
char **msTokenizeMap(char *filename, int *numtokens) {
×
7630
  char **tokens;
7631

7632
  msAcquireLock(TLOCK_PARSER);
×
7633
  tokens = tokenizeMapInternal(filename, numtokens);
×
7634
  msReleaseLock(TLOCK_PARSER);
×
7635

7636
  return tokens;
×
7637
}
7638

7639
void msCloseConnections(mapObj *map) {
3,470✔
7640
  int i;
7641
  layerObj *lp;
7642

7643
  for (i = 0; i < map->numlayers; i++) {
17,860✔
7644
    lp = (GET_LAYER(map, i));
14,390✔
7645

7646
    /* If the vtable is null, then the layer is never accessed or used -> skip
7647
     * it
7648
     */
7649
    if (lp->vtable) {
14,390✔
7650
      lp->vtable->LayerCloseConnection(lp);
3,548✔
7651
    }
7652
  }
7653
}
3,470✔
7654

7655
void initResultCache(resultCacheObj *resultcache) {
2,140✔
7656
  if (resultcache) {
2,140✔
7657
    resultcache->results = NULL;
2,140✔
7658
    resultcache->numresults = 0;
2,140✔
7659
    resultcache->hasnext = MS_UNKNOWN;
2,140✔
7660
    resultcache->cachesize = 0;
2,140✔
7661
    resultcache->bounds.minx = resultcache->bounds.miny =
2,140✔
7662
        resultcache->bounds.maxx = resultcache->bounds.maxy = -1;
2,140✔
7663
    resultcache->previousBounds = resultcache->bounds;
2,140✔
7664
  }
7665
}
2,140✔
7666

7667
void cleanupResultCache(resultCacheObj *resultcache) {
1,070✔
7668
  if (resultcache) {
1,070✔
7669
    if (resultcache->results) {
1,070✔
7670
      int i;
7671
      for (i = 0; i < resultcache->numresults; i++) {
5,247✔
7672
        if (resultcache->results[i].shape) {
4,434✔
7673
          msFreeShape(resultcache->results[i].shape);
12✔
7674
          msFree(resultcache->results[i].shape);
12✔
7675
        }
7676
      }
7677
      free(resultcache->results);
813✔
7678
    }
7679
    resultcache->results = NULL;
1,070✔
7680
    initResultCache(resultcache);
1,070✔
7681
  }
7682
}
1,070✔
7683

7684
static int resolveSymbolNames(mapObj *map) {
3,310✔
7685
  int i, j;
7686

7687
  /* step through layers and classes to resolve symbol names */
7688
  for (i = 0; i < map->numlayers; i++) {
17,271✔
7689
    for (j = 0; j < GET_LAYER(map, i)->numclasses; j++) {
27,121✔
7690
      if (classResolveSymbolNames(GET_LAYER(map, i)->class[j]) != MS_SUCCESS)
13,160✔
7691
        return MS_FAILURE;
7692
    }
7693
  }
7694

7695
  return MS_SUCCESS;
7696
}
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