From 98f64574c82ea0ef9f77d7ba11aa4ba4fe620ac6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 17 May 2020 10:46:39 +0000 Subject: [PATCH 001/160] Remove submodule name from mapscript package --- mapscript/python/mapscript/__init__.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/mapscript/python/mapscript/__init__.py b/mapscript/python/mapscript/__init__.py index a1336e76f4..fa5cbfc9f5 100644 --- a/mapscript/python/mapscript/__init__.py +++ b/mapscript/python/mapscript/__init__.py @@ -1 +1,12 @@ -from .mapscript import * \ No newline at end of file +import inspect +from .mapscript import * + +# change all the class module names from mapscript.mapscript to mapscript + +for key, value in globals().copy().items(): + if inspect.isclass(value) and value.__module__.startswith('mapscript.'): + value.__module__= 'mapscript' + +# remove the submodule name + +del mapscript From 8934254bd6c71cdc617ce6fe60e1613c6c38ef2e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 28 May 2020 10:34:42 +0000 Subject: [PATCH 002/160] Fix wrong result on '404 Not Found' symbol URL --- mapsymbol.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapsymbol.c b/mapsymbol.c index 481da5faa1..ed3bd9dd4a 100644 --- a/mapsymbol.c +++ b/mapsymbol.c @@ -399,7 +399,7 @@ int msAddImageSymbol(symbolSetObj *symbolset, char *filename) unlink(tmpfullfilename); msFree(tmpfilename); msFree(tmppath); - return MS_FAILURE; + return -1; } } msFree(tmpfilename); From 886398e175bcdd86cdd880fbfc761e4e0e0ea00a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 28 May 2020 10:35:28 +0000 Subject: [PATCH 003/160] msMSSQL2008LayerGetExtent doesn't consider NATIVE_FILTER or translated filter (#6076) --- mapmssql2008.c | 71 +++++++++++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 38 deletions(-) diff --git a/mapmssql2008.c b/mapmssql2008.c index 8fefacef9d..c9dffb1a5e 100644 --- a/mapmssql2008.c +++ b/mapmssql2008.c @@ -1122,6 +1122,24 @@ static int getMSSQLMajorVersion(layerObj* layer) return layerinfo->mssqlversion_major; } +static int addFilter(layerObj *layer, char **query) +{ + if (layer->filter.native_string) { + (*query) = msStringConcatenate(*query, " WHERE ("); + (*query) = msStringConcatenate(*query, layer->filter.native_string); + (*query) = msStringConcatenate(*query, ")"); + return MS_TRUE; + } + else if (msLayerGetProcessingKey(layer, "NATIVE_FILTER") != NULL) { + (*query) = msStringConcatenate(*query, " WHERE ("); + (*query) = msStringConcatenate(*query, msLayerGetProcessingKey(layer, "NATIVE_FILTER")); + (*query) = msStringConcatenate(*query, ")"); + return MS_TRUE; + } + + return MS_FALSE; +} + /* Get the layer extent as specified in the mapfile or a largest area */ /* covering all features */ int msMSSQL2008LayerGetExtent(layerObj *layer, rectObj *extent) @@ -1167,6 +1185,10 @@ int msMSSQL2008LayerGetExtent(layerObj *layer, rectObj *extent) } query = msStringConcatenate(query, ") AS extentcol FROM "); query = msStringConcatenate(query, layerinfo->geom_table); + + /* adding attribute filter */ + addFilter(layer, &query); + query = msStringConcatenate(query, ") SELECT extentcol.STPointN(1).STX, extentcol.STPointN(1).STY, extentcol.STPointN(3).STX, extentcol.STPointN(3).STY FROM extent"); } else { @@ -1183,6 +1205,10 @@ int msMSSQL2008LayerGetExtent(layerObj *layer, rectObj *extent) } query = msStringConcatenate(query, ".STEnvelope() as envelope from "); query = msStringConcatenate(query, layerinfo->geom_table); + + /* adding attribute filter */ + addFilter(layer, &query); + query = msStringConcatenate(query, "), CORNERS as (SELECT envelope.STPointN(1) as point from ENVELOPE UNION ALL select envelope.STPointN(3) from ENVELOPE) SELECT MIN(point.STX), MIN(point.STY), MAX(point.STX), MAX(point.STY) FROM CORNERS"); } @@ -1204,7 +1230,7 @@ int msMSSQL2008LayerGetExtent(layerObj *layer, rectObj *extent) } rc = SQLGetData(layerinfo->conn->hstmt, 1, SQL_C_CHAR, result_data, sizeof(result_data), &retLen); - if (rc == SQL_ERROR) { + if (rc == SQL_ERROR || retLen < 0) { msSetError(MS_QUERYERR, "Failed to get MinX value", "msMSSQL2008LayerGetExtent()"); return MS_FAILURE; } @@ -1214,7 +1240,7 @@ int msMSSQL2008LayerGetExtent(layerObj *layer, rectObj *extent) extent->minx = atof(result_data); rc = SQLGetData(layerinfo->conn->hstmt, 2, SQL_C_CHAR, result_data, sizeof(result_data), &retLen); - if (rc == SQL_ERROR) { + if (rc == SQL_ERROR || retLen < 0) { msSetError(MS_QUERYERR, "Failed to get MinY value", "msMSSQL2008LayerGetExtent()"); return MS_FAILURE; } @@ -1224,7 +1250,7 @@ int msMSSQL2008LayerGetExtent(layerObj *layer, rectObj *extent) extent->miny = atof(result_data); rc = SQLGetData(layerinfo->conn->hstmt, 3, SQL_C_CHAR, result_data, sizeof(result_data), &retLen); - if (rc == SQL_ERROR) { + if (rc == SQL_ERROR || retLen < 0) { msSetError(MS_QUERYERR, "Failed to get MaxX value", "msMSSQL2008LayerGetExtent()"); return MS_FAILURE; } @@ -1234,7 +1260,7 @@ int msMSSQL2008LayerGetExtent(layerObj *layer, rectObj *extent) extent->maxx = atof(result_data); rc = SQLGetData(layerinfo->conn->hstmt, 4, SQL_C_CHAR, result_data, sizeof(result_data), &retLen); - if (rc == SQL_ERROR) { + if (rc == SQL_ERROR || retLen < 0) { msSetError(MS_QUERYERR, "Failed to get MaxY value", "msMSSQL2008LayerGetExtent()"); return MS_FAILURE; } @@ -1271,16 +1297,7 @@ int msMSSQL2008LayerGetNumFeatures(layerObj *layer) query = msStringConcatenate(query, layerinfo->geom_table); /* adding attribute filter */ - if (layer->filter.native_string) { - query = msStringConcatenate(query, " WHERE ("); - query = msStringConcatenate(query, layer->filter.native_string); - query = msStringConcatenate(query, ")"); - } - else if (msLayerGetProcessingKey(layer, "NATIVE_FILTER") != NULL) { - query = msStringConcatenate(query, " WHERE ("); - query = msStringConcatenate(query, msLayerGetProcessingKey(layer, "NATIVE_FILTER")); - query = msStringConcatenate(query, ")"); - } + addFilter(layer, &query); if (!executeSQL(layerinfo->conn, query)) { msFree(query); @@ -1508,18 +1525,7 @@ static int prepare_database(layerObj *layer, rectObj rect, char **query_string) } /* adding attribute filter */ - if (layer->filter.native_string) { - query = msStringConcatenate(query, " WHERE ("); - query = msStringConcatenate(query, layer->filter.native_string); - query = msStringConcatenate(query, ")"); - hasFilter = MS_TRUE; - } - else if (msLayerGetProcessingKey(layer, "NATIVE_FILTER") != NULL) { - query = msStringConcatenate(query, " WHERE ("); - query = msStringConcatenate(query, msLayerGetProcessingKey(layer, "NATIVE_FILTER")); - query = msStringConcatenate(query, ")"); - hasFilter = MS_TRUE; - } + hasFilter = addFilter(layer, &query); if( bIsValidRect ) { /* adding spatial filter */ @@ -2583,18 +2589,7 @@ int msMSSQL2008LayerGetShapeCount(layerObj *layer, rectObj rect, projectionObj * query = msStringConcatenate(query, layerinfo->geom_table); /* adding attribute filter */ - if (layer->filter.native_string) { - query = msStringConcatenate(query, " WHERE ("); - query = msStringConcatenate(query, layer->filter.native_string); - query = msStringConcatenate(query, ")"); - hasFilter = MS_TRUE; - } - else if (msLayerGetProcessingKey(layer, "NATIVE_FILTER") != NULL) { - query = msStringConcatenate(query, " WHERE ("); - query = msStringConcatenate(query, msLayerGetProcessingKey(layer, "NATIVE_FILTER")); - query = msStringConcatenate(query, ")"); - hasFilter = MS_TRUE; - } + hasFilter = addFilter(layer, &query); if( bIsValidRect ) { /* adding spatial filter */ From c894eda521b49f54396b551f4538f96a638ce875 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 4 Jun 2020 12:00:24 +0000 Subject: [PATCH 004/160] Fix issue #5529 about GEOS error with offset on MULTILINESTRING --- mapgeos.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/mapgeos.c b/mapgeos.c index 243c352bc0..2c23c260b0 100644 --- a/mapgeos.c +++ b/mapgeos.c @@ -804,7 +804,22 @@ shapeObj *msGEOSOffsetCurve(shapeObj *p, double offset) { g1 = (GEOSGeom) p->geometry; if(!g1) return NULL; - g2 = GEOSOffsetCurve_r(handle,g1, offset, 4, GEOSBUF_JOIN_MITRE, fabs(offset*1.5)); + if (GEOSGeomTypeId_r(handle,g1) == GEOS_MULTILINESTRING) + { + GEOSGeom *lines = malloc(p->numlines*sizeof(GEOSGeom)); + if (!lines) return NULL; + for(int i=0; inumlines; i++) + { + lines[i] = GEOSOffsetCurve_r(handle, GEOSGetGeometryN_r(handle,g1,i), + offset, 4, GEOSBUF_JOIN_MITRE, fabs(offset*1.5)); + } + g2 = GEOSGeom_createCollection_r(handle,GEOS_MULTILINESTRING, lines, p->numlines); + free(lines); + } + else + { + g2 = GEOSOffsetCurve_r(handle,g1, offset, 4, GEOSBUF_JOIN_MITRE, fabs(offset*1.5)); + } return msGEOSGeometry2Shape(g2); #else msSetError(MS_GEOSERR, "GEOS Offset Curve support is not available.", "msGEOSingleSidedBuffer()"); From 015bfc5cffa9e001444546e2026cf0792d66d663 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 4 Jun 2020 14:59:42 +0000 Subject: [PATCH 005/160] Enable relative URL on SLD ExternalGraphic --- mapcopy.c | 1 + mapfile.c | 1 + mapogcsld.c | 51 ++++++++++++++++- mapserver.h | 4 ++ msautotest/sld/data/danube.sld | 55 +++++++++++++++++++ msautotest/sld/expected/sld_url_linemark.png | Bin 0 -> 48423 bytes msautotest/sld/linemark.map | 6 +- 7 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 msautotest/sld/data/danube.sld create mode 100644 msautotest/sld/expected/sld_url_linemark.png diff --git a/mapcopy.c b/mapcopy.c index f06d348620..3ea0fd1d5e 100644 --- a/mapcopy.c +++ b/mapcopy.c @@ -1217,6 +1217,7 @@ int msCopyMap(mapObj *dst, mapObj *src) MS_COPYSTELEM(resolution); MS_COPYSTRING(dst->shapepath, src->shapepath); MS_COPYSTRING(dst->mappath, src->mappath); + MS_COPYSTELEM(sldurl); MS_COPYCOLOR(&(dst->imagecolor), &(src->imagecolor)); diff --git a/mapfile.c b/mapfile.c index cf222f4c3c..da7be007d7 100644 --- a/mapfile.c +++ b/mapfile.c @@ -5890,6 +5890,7 @@ int initMap(mapObj *map) map->cellsize = 0; map->shapepath = NULL; map->mappath = NULL; + map->sldurl = NULL; MS_INIT_COLOR(map->imagecolor, 255,255,255,255); /* white */ diff --git a/mapogcsld.c b/mapogcsld.c index 199a8eecde..9eefa6924f 100644 --- a/mapogcsld.c +++ b/mapogcsld.c @@ -72,6 +72,7 @@ int msSLDApplySLDURL(mapObj *map, const char *szURL, int iLayer, int nStatus = MS_FAILURE; if (map && szURL) { + map->sldurl = (char*)szURL; pszSLDTmpFile = msTmpFile(map, map->mappath, NULL, "sld.xml"); if (pszSLDTmpFile == NULL) { pszSLDTmpFile = msTmpFile(map, NULL, NULL, "sld.xml" ); @@ -110,6 +111,7 @@ int msSLDApplySLDURL(mapObj *map, const char *szURL, int iLayer, if (pszSLDbuf) nStatus = msSLDApplySLD(map, pszSLDbuf, iLayer, pszStyleLayerName, ppszLayerNames); } + map->sldurl = NULL; } msFree(pszSLDbuf); @@ -2255,10 +2257,54 @@ int msSLDParseExternalGraphic(CPLXMLNode *psExternalGraphic, if (psTmp && psTmp->psChild) { pszURL = (char*)psTmp->psChild->pszValue; + char *symbolurl = NULL; + // Handle relative URL for ExternalGraphic + if (map->sldurl && !strstr(pszURL,"://")) + { + char *baseurl = NULL; + char *relpath = NULL; + symbolurl = malloc(sizeof(char)*MS_MAXPATHLEN); + if (pszURL[0] != '/') { + // Symbol file is relative to SLD file + // e.g. SLD : http://example.com/path/to/sld.xml + // and symbol: assets/symbol.svg + // lead to: http://example.com/path/to/assets/symbol.svg + baseurl = msGetPath(map->sldurl); + relpath = pszURL; + } + else + { + // Symbol file is relative to the root of SLD server + // e.g. SLD : http://example.com/path/to/sld.xml + // and symbol: /path/to/assets/symbol.svg + // lead to: http://example.com/path/to/assets/symbol.svg + baseurl = msStrdup(map->sldurl); + relpath = pszURL+1; + char * sep = strstr(baseurl,"://"); + if (sep) + sep += 3; + else + sep = baseurl; + sep = strchr(sep,'/'); + if (!sep) + sep = baseurl + strlen(baseurl); + sep[1] = '\0'; + } + msBuildPath(symbolurl,baseurl,relpath); + msFree(baseurl); + } + else + { + // Absolute URL + // e.g. symbol: http://example.com/path/to/assets/symbol.svg + symbolurl = msStrdup(pszURL); + } + /* validate the ExternalGraphic parameter */ - if(msValidateParameter(pszURL, msLookupHashTable(&(map->web.validation), "sld_external_graphic"), + if(msValidateParameter(symbolurl, msLookupHashTable(&(map->web.validation), "sld_external_graphic"), NULL, NULL, NULL) != MS_SUCCESS) { msSetError(MS_WEBERR, "SLD ExternalGraphic OnlineResource value fails to validate against sld_external_graphic VALIDATION", "mapserv()"); + msFree(symbolurl); return MS_FAILURE; } @@ -2266,8 +2312,9 @@ int msSLDParseExternalGraphic(CPLXMLNode *psExternalGraphic, /*external symbols using http will be automaticallly downloaded. The file should be saved in a temporary directory (msAddImageSymbol) #2305*/ psStyle->symbol = msGetSymbolIndex(&map->symbolset, - pszURL, + symbolurl, MS_TRUE); + msFree(symbolurl); if (psStyle->symbol > 0 && psStyle->symbol < map->symbolset.numsymbols) psStyle->symbolname = msStrdup(map->symbolset.symbol[psStyle->symbol]->name); diff --git a/mapserver.h b/mapserver.h index d607aa92b0..2b387cd83e 100644 --- a/mapserver.h +++ b/mapserver.h @@ -1902,6 +1902,10 @@ void msPopulateTextSymbolForLabelAndString(textSymbolObj *ts, labelObj *l, char char *shapepath; /* where are the shape files located */ char *mappath; /* path of the mapfile, all path are relative to this path */ + char *sldurl; // URL of SLD document as specified with "&SLD=..." + // WMS parameter. Currently this reference is used + // only in mapogcsld.c and has a NULL value + // outside that context. #ifndef SWIG paletteObj palette; /* holds a map palette */ diff --git a/msautotest/sld/data/danube.sld b/msautotest/sld/data/danube.sld new file mode 100644 index 0000000000..c99c333f56 --- /dev/null +++ b/msautotest/sld/data/danube.sld @@ -0,0 +1,55 @@ + + + danube + + + + + + 5 + #0000FF + + + + + + + 10 + + circle + + #FFFF00 + 2 + + + + 80 + 40 + + + + + + + + 30 + + + + image/svg+xml + + + 80 + 0 + + + + + + + + diff --git a/msautotest/sld/expected/sld_url_linemark.png b/msautotest/sld/expected/sld_url_linemark.png new file mode 100644 index 0000000000000000000000000000000000000000..13ef5b8f3b974ed8b2dab2022e1f505faf2b349b GIT binary patch literal 48423 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 literal 0 HcmV?d00001 diff --git a/msautotest/sld/linemark.map b/msautotest/sld/linemark.map index f3f1b50110..4f1bd7c90f 100644 --- a/msautotest/sld/linemark.map +++ b/msautotest/sld/linemark.map @@ -9,10 +9,14 @@ # RUN_PARMS: map_linemark.png [MAPSERV] QUERY_STRING="map=[MAPFILE]&SERVICE=WMS&VERSION=1.3&REQUEST=GetMap&CRS=EPSG:4326&BBOX=40,7,50,31&FORMAT=image/png&WIDTH=720&HEIGHT=300&LAYERS=bg,danube" > [RESULT_DEMIME] # # -# --- Reference output rendered with SLD +# --- Reference output rendered with SLD body # # RUN_PARMS: sld_linemark.png [MAPSERV] QUERY_STRING="map=[MAPFILE]&SERVICE=WMS&VERSION=1.3&REQUEST=GetMap&CRS=EPSG:4326&BBOX=40,7,50,31&FORMAT=image/png&WIDTH=720&HEIGHT=300&LAYERS=bg,danube&SLD_BODY=danube5#0000FF10circle#FFFF002804030image/svg%2Bxml800" > [RESULT_DEMIME] # +# --- Reference output rendered with SLD URL +# +# RUN_PARMS: sld_url_linemark.png [MAPSERV] QUERY_STRING="map=[MAPFILE]&SERVICE=WMS&VERSION=1.3&REQUEST=GetMap&CRS=EPSG:4326&BBOX=40,7,50,31&FORMAT=image/png&WIDTH=720&HEIGHT=300&LAYERS=bg,danube&SLD=http://localhost:8000/sld/data/danube.sld" > [RESULT_DEMIME] +# From d4dc036e24cc45580f2b3bd51ff6a9a9ea9770cc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 5 Jun 2020 15:02:50 +0000 Subject: [PATCH 006/160] WFS server: add support for in --- mapogcfilter.c | 2 + .../wfs_filter_200_intersects_envelope.xml | 51 +++++++++++++++++++ msautotest/wxs/wfs_filter.map | 4 ++ 3 files changed, 57 insertions(+) create mode 100644 msautotest/wxs/expected/wfs_filter_200_intersects_envelope.xml diff --git a/mapogcfilter.c b/mapogcfilter.c index 838a28d804..d6e1df9e94 100644 --- a/mapogcfilter.c +++ b/mapogcfilter.c @@ -1038,6 +1038,8 @@ static CPLXMLNode* FLTFindGeometryNode(CPLXMLNode* psXMLNode, *pbPolygon = 1; else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "Box"))) *pbPolygon = 1; + else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "Envelope"))) + *pbPolygon = 1; else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "LineString"))) *pbLine = 1; else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "MultiLineString"))) diff --git a/msautotest/wxs/expected/wfs_filter_200_intersects_envelope.xml b/msautotest/wxs/expected/wfs_filter_200_intersects_envelope.xml new file mode 100644 index 0000000000..2326bb3901 --- /dev/null +++ b/msautotest/wxs/expected/wfs_filter_200_intersects_envelope.xml @@ -0,0 +1,51 @@ +Content-Type: text/xml; subtype="gml/3.2.1"; charset=UTF-8 + + + + + + 44.61104 -65.81602 + 44.61104 -65.81602 + + + + + + + + 44.61104 -65.81602 + 44.61104 -65.81602 + + + + + 44.61104 -65.81602 + + + 0.000 + 0.000 + 118 + 2 + CAJOA + Digby + + + + + 12 + 021A12 + 443700 + 654600 + 1203006 + 0 + 2 + + + + diff --git a/msautotest/wxs/wfs_filter.map b/msautotest/wxs/wfs_filter.map index b13f686280..3120279659 100644 --- a/msautotest/wxs/wfs_filter.map +++ b/msautotest/wxs/wfs_filter.map @@ -131,6 +131,10 @@ # RUN_PARMS: wfs_filter_intersects.xml [MAPSERV] QUERY_STRING="map=[MAPFILE]&SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME=popplace&FILTER=Geometry-61.63,45.04 -60.78,45.04 -60.78,46.08 -61.63,46.08" > [RESULT] # # RUN_PARMS: wfs_filter_200_intersects.xml [MAPSERV] [POST]Geometry-61.63 45.04 -60.78 45.04 -60.78 46.08 -61.63 46.08[/POST] > [RESULT_DEVERSION] + +# +# RUN_PARMS: wfs_filter_200_intersects_envelope.xml [MAPSERV] [POST]Geometry272868.16 4938053.09281261.3 4951109.39[/POST] > [RESULT_DEVERSION] + # # Verify DWITHIN Result: Sydney # RUN_PARMS: wfs_filter_dwithin.xml [MAPSERV] QUERY_STRING="map=[MAPFILE]&SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME=popplace&FILTER=Geometry-60.18,46.100.05" > [RESULT] From 16eb9de0cb06bbe40d09ecb5eb1ddd0e9e81e85f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 12 Jun 2020 17:58:31 +0000 Subject: [PATCH 007/160] Fix eol of mapscript python examples to LF instead of CRLF --- mapscript/python/examples/project_csv.py | 144 +++++++-------- mapscript/python/examples/shpdump.py | 188 +++++++++---------- mapscript/python/examples/shpinfo.py | 218 +++++++++++------------ mapscript/python/examples/wxs.py | 122 ++++++------- 4 files changed, 336 insertions(+), 336 deletions(-) diff --git a/mapscript/python/examples/project_csv.py b/mapscript/python/examples/project_csv.py index 83f6ffa580..6a740f67b3 100755 --- a/mapscript/python/examples/project_csv.py +++ b/mapscript/python/examples/project_csv.py @@ -1,72 +1,72 @@ -#!/usr/bin/env python - -""" -Simple example to read a csv file and reproject point x/y data - -Usage: - -project_csv.py cities.csv 2 1 EPSG:4326 EPSG:3857 - -""" - -import sys -import csv -from io import open -import mapscript - - -def main(input_file, x_field_idx, y_field_idx, input_proj, output_proj): - - # set input and output projections - proj_in = mapscript.projectionObj(input_proj) - proj_out = mapscript.projectionObj(output_proj) - - # open file - with open(input_file, encoding='utf-8') as f: - # read csv - csv_in = csv.reader(f) - headers = next(csv_in) - - # setup output - csv_out = csv.writer(sys.stdout) - csv_out.writerow(headers) - - for row in csv_in: - # set pointObj - point = mapscript.pointObj(float(row[x_field_idx]), float(row[y_field_idx])) - # project - point.project(proj_in, proj_out) - - # update with reprojected coordinates - row[x_field_idx] = point.x - row[y_field_idx] = point.y - - csv_out.writerow(row) - - -def usage(): - """ - Display usage if program is used incorrectly - """ - print("Syntax: %s " % sys.argv[0]) - sys.exit(2) - - -# check input parameters - -if (len(sys.argv) != 6): - usage() - - -input_file = sys.argv[1] - -# set x and y indices - -x_field_idx = int(sys.argv[2]) -y_field_idx = int(sys.argv[3]) - -# get projection codes - -input_proj = "init="+sys.argv[4].lower() -output_proj = "init="+sys.argv[5].lower() -main(input_file, x_field_idx, y_field_idx, input_proj, output_proj) +#!/usr/bin/env python + +""" +Simple example to read a csv file and reproject point x/y data + +Usage: + +project_csv.py cities.csv 2 1 EPSG:4326 EPSG:3857 + +""" + +import sys +import csv +from io import open +import mapscript + + +def main(input_file, x_field_idx, y_field_idx, input_proj, output_proj): + + # set input and output projections + proj_in = mapscript.projectionObj(input_proj) + proj_out = mapscript.projectionObj(output_proj) + + # open file + with open(input_file, encoding='utf-8') as f: + # read csv + csv_in = csv.reader(f) + headers = next(csv_in) + + # setup output + csv_out = csv.writer(sys.stdout) + csv_out.writerow(headers) + + for row in csv_in: + # set pointObj + point = mapscript.pointObj(float(row[x_field_idx]), float(row[y_field_idx])) + # project + point.project(proj_in, proj_out) + + # update with reprojected coordinates + row[x_field_idx] = point.x + row[y_field_idx] = point.y + + csv_out.writerow(row) + + +def usage(): + """ + Display usage if program is used incorrectly + """ + print("Syntax: %s " % sys.argv[0]) + sys.exit(2) + + +# check input parameters + +if (len(sys.argv) != 6): + usage() + + +input_file = sys.argv[1] + +# set x and y indices + +x_field_idx = int(sys.argv[2]) +y_field_idx = int(sys.argv[3]) + +# get projection codes + +input_proj = "init="+sys.argv[4].lower() +output_proj = "init="+sys.argv[5].lower() +main(input_file, x_field_idx, y_field_idx, input_proj, output_proj) diff --git a/mapscript/python/examples/shpdump.py b/mapscript/python/examples/shpdump.py index 4349b62ba0..25d241acfc 100755 --- a/mapscript/python/examples/shpdump.py +++ b/mapscript/python/examples/shpdump.py @@ -1,94 +1,94 @@ -#!/usr/bin/env python - -""" -Dump the contents of the passed in Shapefile - -Usage: - -python shpdump.py polygon.shp - -""" - -import mapscript -import sys -import os - - -def plural(x): - """ - Returns an 's' if plural - - Useful in print statements to avoid something like 'point(s)' - """ - if x > 1: - return 's' - return '' - - -def get_shapefile_object(sf_path): - - # make sure can access .shp file, create shapefileObj - - if os.access(sf_path, os.F_OK): - sf_obj = mapscript.shapefileObj(sf_path, -1) - else: - print("Can't access {}".format(sf_path)) - sys.exit(2) - - return sf_obj - - -def main(sf_path): - - if not sf_path.lower().endswith(".shp"): - sf_path += ".shp" - - sf_obj = get_shapefile_object(sf_path) - - # create an empty Shapefile object - - s_obj = mapscript.shapeObj() - - # loop through each shape in the original Shapefile - - for i in range(sf_obj.numshapes): - - # get the object at index i - sf_obj.get(i, s_obj) - print("Shape %i has %i part%s" % (i, s_obj.numlines, plural(s_obj.numlines))) - print("Bounds (%f, %f) (%f, %f)" % (s_obj.bounds.minx, s_obj.bounds.miny, s_obj.bounds.maxx, s_obj.bounds.maxy)) - - # loop through parts of each shape - - for j in range(s_obj.numlines): - - # get the jth part of the ith object - - part = s_obj.get(j) - print("Part %i has %i point%s" % (j, part.numpoints, plural(part.numpoints))) - - # loop through points in each part - - for k in range(part.numpoints): - - # get the kth point of the jth part of the ith shape - - point = part.get(k) - print("%i: %f, %f" % (k, point.x, point.y)) - - -def usage(): - """ - Display usage if program is used incorrectly - """ - print("Syntax: %s " % sys.argv[0]) - sys.exit(2) - - -# make sure passing in filename argument -if len(sys.argv) != 2: - usage() - - -sf_path = sys.argv[1] -main(sf_path) +#!/usr/bin/env python + +""" +Dump the contents of the passed in Shapefile + +Usage: + +python shpdump.py polygon.shp + +""" + +import mapscript +import sys +import os + + +def plural(x): + """ + Returns an 's' if plural + + Useful in print statements to avoid something like 'point(s)' + """ + if x > 1: + return 's' + return '' + + +def get_shapefile_object(sf_path): + + # make sure can access .shp file, create shapefileObj + + if os.access(sf_path, os.F_OK): + sf_obj = mapscript.shapefileObj(sf_path, -1) + else: + print("Can't access {}".format(sf_path)) + sys.exit(2) + + return sf_obj + + +def main(sf_path): + + if not sf_path.lower().endswith(".shp"): + sf_path += ".shp" + + sf_obj = get_shapefile_object(sf_path) + + # create an empty Shapefile object + + s_obj = mapscript.shapeObj() + + # loop through each shape in the original Shapefile + + for i in range(sf_obj.numshapes): + + # get the object at index i + sf_obj.get(i, s_obj) + print("Shape %i has %i part%s" % (i, s_obj.numlines, plural(s_obj.numlines))) + print("Bounds (%f, %f) (%f, %f)" % (s_obj.bounds.minx, s_obj.bounds.miny, s_obj.bounds.maxx, s_obj.bounds.maxy)) + + # loop through parts of each shape + + for j in range(s_obj.numlines): + + # get the jth part of the ith object + + part = s_obj.get(j) + print("Part %i has %i point%s" % (j, part.numpoints, plural(part.numpoints))) + + # loop through points in each part + + for k in range(part.numpoints): + + # get the kth point of the jth part of the ith shape + + point = part.get(k) + print("%i: %f, %f" % (k, point.x, point.y)) + + +def usage(): + """ + Display usage if program is used incorrectly + """ + print("Syntax: %s " % sys.argv[0]) + sys.exit(2) + + +# make sure passing in filename argument +if len(sys.argv) != 2: + usage() + + +sf_path = sys.argv[1] +main(sf_path) diff --git a/mapscript/python/examples/shpinfo.py b/mapscript/python/examples/shpinfo.py index 488dd2e187..58297dd5e5 100755 --- a/mapscript/python/examples/shpinfo.py +++ b/mapscript/python/examples/shpinfo.py @@ -1,109 +1,109 @@ -#!/usr/bin/env python - -""" -Extracts basic descriptive information from a Shapefile - -Usage: - -python shpinfo.py point.shp - -""" -import mapscript -import sys -import os - - -def get_shapefile_object(sf_path): - - # make sure can access .shp file, create shapefileObj - - if os.access(sf_path, os.F_OK): - sf_obj = mapscript.shapefileObj(sf_path) - else: - print("Can't access {}".format(sf_path)) - sys.exit(2) - - return sf_obj - - -def get_shapefile_details(sf_obj): - - # dictionary of shapefile types - - types = { - mapscript.MS_SHAPEFILE_POINT: 'Point', - mapscript.MS_SHAPEFILE_ARC: 'Line', - mapscript.MS_SHAPEFILE_POLYGON: 'Polygon', - mapscript.MS_SHAPEFILE_MULTIPOINT: 'Multipoint', - - mapscript.MS_SHP_POINTZ: 'PointZ', - mapscript.MS_SHP_ARCZ: 'LineZ', - mapscript.MS_SHP_POLYGONZ: 'PolygonZ', - mapscript.MS_SHP_MULTIPOINTZ: 'MultipointZ', - - mapscript.MS_SHP_POINTM: 'Multipoint', - mapscript.MS_SHP_ARCM: 'LineM', - mapscript.MS_SHP_POLYGONM: 'PolygonM', - mapscript.MS_SHP_MULTIPOINTM: 'MultipointM' - } - - # print out basic information that is part of the shapefile object - - print("\tType: %s" % types[sf_obj.type]) - - print("\tBounds: (%f, %f) (%f, %f)" % (sf_obj.bounds.minx, - sf_obj.bounds.miny, - sf_obj.bounds.maxx, - sf_obj.bounds.maxy)) - - print("\tNumber of features: %i" % sf_obj.numshapes) - - -def get_dbf_details(sf_obj): - - # get DBF object - - dbf_obj = sf_obj.getDBF() - - # print out table characteristics - - print("\tNumber of records in DBF: %i" % dbf_obj.nRecords) - print("\tNumber of fields: %i" % dbf_obj.nFields) - print("") - print("\t%-20s %12s %8s %10s" % ("Name", "Type", "Length", "Decimals")) - print("\t-----------------------------------------------------") - - # print out field characteristics - - for idx in range(0, dbf_obj.nFields): - print("\t%-20s %12s %8d %10d" % (dbf_obj.getFieldName(idx), dbf_obj.getFieldType(idx), - dbf_obj.getFieldWidth(idx), dbf_obj.getFieldDecimals(idx))) - - -def main(sf_path): - - if not sf_path.lower().endswith(".shp"): - sf_path += ".shp" - - sf_obj = get_shapefile_object(sf_path) - - print("Shapefile %s:" % sf_path) - print("") - - get_shapefile_details(sf_obj) - get_dbf_details(sf_obj) - - -def usage(): - """ - Display usage if program is used incorrectly - """ - print("Syntax: %s " % sys.argv[0]) - sys.exit(2) - - -# make sure a filename argument is provided -if len(sys.argv) != 2: - usage() - -main(sys.argv[1]) +#!/usr/bin/env python + +""" +Extracts basic descriptive information from a Shapefile + +Usage: + +python shpinfo.py point.shp + +""" +import mapscript +import sys +import os + + +def get_shapefile_object(sf_path): + + # make sure can access .shp file, create shapefileObj + + if os.access(sf_path, os.F_OK): + sf_obj = mapscript.shapefileObj(sf_path) + else: + print("Can't access {}".format(sf_path)) + sys.exit(2) + + return sf_obj + + +def get_shapefile_details(sf_obj): + + # dictionary of shapefile types + + types = { + mapscript.MS_SHAPEFILE_POINT: 'Point', + mapscript.MS_SHAPEFILE_ARC: 'Line', + mapscript.MS_SHAPEFILE_POLYGON: 'Polygon', + mapscript.MS_SHAPEFILE_MULTIPOINT: 'Multipoint', + + mapscript.MS_SHP_POINTZ: 'PointZ', + mapscript.MS_SHP_ARCZ: 'LineZ', + mapscript.MS_SHP_POLYGONZ: 'PolygonZ', + mapscript.MS_SHP_MULTIPOINTZ: 'MultipointZ', + + mapscript.MS_SHP_POINTM: 'Multipoint', + mapscript.MS_SHP_ARCM: 'LineM', + mapscript.MS_SHP_POLYGONM: 'PolygonM', + mapscript.MS_SHP_MULTIPOINTM: 'MultipointM' + } + + # print out basic information that is part of the shapefile object + + print("\tType: %s" % types[sf_obj.type]) + + print("\tBounds: (%f, %f) (%f, %f)" % (sf_obj.bounds.minx, + sf_obj.bounds.miny, + sf_obj.bounds.maxx, + sf_obj.bounds.maxy)) + + print("\tNumber of features: %i" % sf_obj.numshapes) + + +def get_dbf_details(sf_obj): + + # get DBF object + + dbf_obj = sf_obj.getDBF() + + # print out table characteristics + + print("\tNumber of records in DBF: %i" % dbf_obj.nRecords) + print("\tNumber of fields: %i" % dbf_obj.nFields) + print("") + print("\t%-20s %12s %8s %10s" % ("Name", "Type", "Length", "Decimals")) + print("\t-----------------------------------------------------") + + # print out field characteristics + + for idx in range(0, dbf_obj.nFields): + print("\t%-20s %12s %8d %10d" % (dbf_obj.getFieldName(idx), dbf_obj.getFieldType(idx), + dbf_obj.getFieldWidth(idx), dbf_obj.getFieldDecimals(idx))) + + +def main(sf_path): + + if not sf_path.lower().endswith(".shp"): + sf_path += ".shp" + + sf_obj = get_shapefile_object(sf_path) + + print("Shapefile %s:" % sf_path) + print("") + + get_shapefile_details(sf_obj) + get_dbf_details(sf_obj) + + +def usage(): + """ + Display usage if program is used incorrectly + """ + print("Syntax: %s " % sys.argv[0]) + sys.exit(2) + + +# make sure a filename argument is provided +if len(sys.argv) != 2: + usage() + +main(sys.argv[1]) diff --git a/mapscript/python/examples/wxs.py b/mapscript/python/examples/wxs.py index 10bacb6a3c..812f1f77b5 100644 --- a/mapscript/python/examples/wxs.py +++ b/mapscript/python/examples/wxs.py @@ -1,61 +1,61 @@ -#!/usr/bin/env python - -""" -Output WMS GetCapabilities response for a Mapfile - -Usage: - -python wxs.py test.map - -""" -import sys -import xml.dom.minidom -import mapscript - - -def main(map_file): - - map = mapscript.mapObj(map_file) - map.setMetaData("ows_onlineresource", "http://dummy.org/") - ows_req = mapscript.OWSRequest() - - ows_req.type = mapscript.MS_GET_REQUEST - - ows_req.setParameter("SERVICE", "WMS") - ows_req.setParameter("VERSION", "1.1.0") - ows_req.setParameter("REQUEST", "GetCapabilities") - - mapscript.msIO_installStdoutToBuffer() - dispatch_status = map.OWSDispatch(ows_req) - - if dispatch_status != mapscript.MS_SUCCESS: - print("An error occurred") - - content_type = mapscript.msIO_stripStdoutBufferContentType() - mapscript.msIO_stripStdoutBufferContentHeaders() - result = mapscript.msIO_getStdoutBufferBytes() - - # [('Content-Type', 'application/vnd.ogc.wms_xml; charset=UTF-8'), ('Content-Length', '11385')] - response_headers = [('Content-Type', content_type), - ('Content-Length', str(len(result)))] - - assert int(response_headers[1][1]) > 0 - - dom = xml.dom.minidom.parseString(result) - print(dom.toprettyxml(indent="", newl="")) - - -def usage(): - """ - Display usage if program is used incorrectly - """ - print("Syntax: %s " % sys.argv[0]) - sys.exit(2) - - -# make sure a filename argument is provided -if len(sys.argv) != 2: - usage() - -map_file = sys.argv[1] -main(map_file) +#!/usr/bin/env python + +""" +Output WMS GetCapabilities response for a Mapfile + +Usage: + +python wxs.py test.map + +""" +import sys +import xml.dom.minidom +import mapscript + + +def main(map_file): + + map = mapscript.mapObj(map_file) + map.setMetaData("ows_onlineresource", "http://dummy.org/") + ows_req = mapscript.OWSRequest() + + ows_req.type = mapscript.MS_GET_REQUEST + + ows_req.setParameter("SERVICE", "WMS") + ows_req.setParameter("VERSION", "1.1.0") + ows_req.setParameter("REQUEST", "GetCapabilities") + + mapscript.msIO_installStdoutToBuffer() + dispatch_status = map.OWSDispatch(ows_req) + + if dispatch_status != mapscript.MS_SUCCESS: + print("An error occurred") + + content_type = mapscript.msIO_stripStdoutBufferContentType() + mapscript.msIO_stripStdoutBufferContentHeaders() + result = mapscript.msIO_getStdoutBufferBytes() + + # [('Content-Type', 'application/vnd.ogc.wms_xml; charset=UTF-8'), ('Content-Length', '11385')] + response_headers = [('Content-Type', content_type), + ('Content-Length', str(len(result)))] + + assert int(response_headers[1][1]) > 0 + + dom = xml.dom.minidom.parseString(result) + print(dom.toprettyxml(indent="", newl="")) + + +def usage(): + """ + Display usage if program is used incorrectly + """ + print("Syntax: %s " % sys.argv[0]) + sys.exit(2) + + +# make sure a filename argument is provided +if len(sys.argv) != 2: + usage() + +map_file = sys.argv[1] +main(map_file) From 610a8b6cbf2ad4c1fac12adb23287d24b2836970 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 28 Jun 2020 11:48:22 +0000 Subject: [PATCH 008/160] install PHPNG required file 'mapscript.php' --- mapscript/phpng/CMakeLists.txt | 2 ++ mapscript/phpng/php7module.i | 2 ++ 2 files changed, 4 insertions(+) diff --git a/mapscript/phpng/CMakeLists.txt b/mapscript/phpng/CMakeLists.txt index 8ba74df7e0..f7333b8dab 100644 --- a/mapscript/phpng/CMakeLists.txt +++ b/mapscript/phpng/CMakeLists.txt @@ -84,4 +84,6 @@ set_target_properties(${SWIG_MODULE_mapscript_REAL_NAME} PROPERTIES PREFIX "") if(NOT WIN32) install(TARGETS php_mapscriptng DESTINATION ${PHP_EXTENSION_DIR}) + # install the required file containing MapServer constants and functions + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mapscript.php DESTINATION ${PHP_EXTENSION_DIR} COMPONENT dev) endif() diff --git a/mapscript/phpng/php7module.i b/mapscript/phpng/php7module.i index aefdab1cbe..4269c3fee3 100644 --- a/mapscript/phpng/php7module.i +++ b/mapscript/phpng/php7module.i @@ -1,3 +1,5 @@ +%module mapscriptng; + %pragma(php) phpinfo=" php_info_print_table_start(); php_info_print_table_row(2, \"MapServer Version\", msGetVersion()); From 79652b113a9945da1586b5ed63c115b04f8fee9d Mon Sep 17 00:00:00 2001 From: MapServer-backport-bot <63150209+MapServer-backport-bot@users.noreply.github.com> Date: Tue, 7 Jul 2020 10:52:00 -0300 Subject: [PATCH 009/160] Add py37 MapScript build and testing (#6107) Co-authored-by: github-actions[bot] --- appveyor.yml | 14 ++++++++++++-- mapscript/python/CMakeLists.txt | 4 +++- mapscript/python/pymodule.i | 6 ++++-- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index bd606dcb6e..7962664ae1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,6 +12,7 @@ cache: environment: global: + SWIG_VER: swigwin-4.0.1 TWINE_USERNAME: mapserver TWINE_PASSWORD: secure: mHoJHeXdXbBNoDf7MA4ZEg== @@ -24,10 +25,10 @@ environment: SWIG_VER: swigwin-3.0.12 - platform: x64 PYTHON_EXECUTABLE: c:/python27-x64/python.exe - SWIG_VER: swigwin-4.0.1 - platform: x64 PYTHON_EXECUTABLE: c:/python36-x64/python.exe - SWIG_VER: swigwin-4.0.1 + - platform: x64 + PYTHON_EXECUTABLE: c:/python37-x64/python.exe services: - mssql2017 @@ -38,6 +39,15 @@ matrix: shallow_clone: false clone_depth: 5 +init: + - ps: | + if ($env:APPVEYOR_REPO_TAG -ne $TRUE) { + if ("c:/python27-x64/python.exe","c:/python36-x64/python.exe" -contains $env:PYTHON_EXECUTABLE -eq $TRUE) { + Write-Host "Skipping build, not a tagged release." + Exit-AppVeyorBuild + } + } + build_script: - set "BUILD_FOLDER=%APPVEYOR_BUILD_FOLDER:\=/%" - if "%platform%" == "x64" SET VS_FULL=%VS_VERSION% Win64 diff --git a/mapscript/python/CMakeLists.txt b/mapscript/python/CMakeLists.txt index 678856ce36..5b14ca9693 100644 --- a/mapscript/python/CMakeLists.txt +++ b/mapscript/python/CMakeLists.txt @@ -108,7 +108,9 @@ add_custom_command( DEPENDS mapscriptwheel.stamp OUTPUT mapscripttests.stamp COMMAND ${PYTHON_VENV_SCRIPTS}/pip install --no-index --find-links=${OUTPUT_FOLDER}/dist mapscript - COMMAND ${PYTHON_VENV_SCRIPTS}/pytest --pyargs mapscript.tests + # ERROR: file or package not found: mapscript.tests (missing __init__.py?) is caused by + # ImportError: DLL load failed while importing _mapscript: The specified module could not be found. + COMMAND ${PYTHON_VENV_SCRIPTS}/python -m pytest --pyargs mapscript.tests COMMAND ${PYTHON_VENV_SCRIPTS}/python -m mapscript.examples.project_csv ${PROJECT_SOURCE_DIR}/tests/cities.csv 2 1 EPSG:4326 EPSG:3857 > test.csv COMMAND ${PYTHON_VENV_SCRIPTS}/python -m mapscript.examples.shpdump ${PROJECT_SOURCE_DIR}/tests/polygon.shp > shpdump.txt COMMAND ${PYTHON_VENV_SCRIPTS}/python -m mapscript.examples.shpinfo ${PROJECT_SOURCE_DIR}/tests/polygon.shp > shpinfo.txt diff --git a/mapscript/python/pymodule.i b/mapscript/python/pymodule.i index 6524a17259..51e070dbf1 100644 --- a/mapscript/python/pymodule.i +++ b/mapscript/python/pymodule.i @@ -114,8 +114,10 @@ CreateTupleFromDoubleArray( double *first, unsigned int size ) { PyObject* val = PyList_GetItem(values, i); %#if PY_MAJOR_VERSION >= 3 - $1[i] = PyUnicode_AsUTF8(key); - $2[i] = PyUnicode_AsUTF8(val); + // Changed in version 3.7: The return type is now const char * rather than char * + // avoid warning C4090: '=': different 'const' qualifiers + $1[i] = (char *)PyUnicode_AsUTF8(key); + $2[i] = (char *)PyUnicode_AsUTF8(val); %#else $1[i] = PyString_AsString(key); $2[i] = PyString_AsString(val); From d08e128029c6608d7362dfd218a91d95005d126b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 8 Jul 2020 17:07:32 +0000 Subject: [PATCH 010/160] Fix 2 warnings raised by gcc 7.5 --- mapwms.c | 17 +++++++++-------- renderers/agg/include/agg_conv_curve.h | 4 ++-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/mapwms.c b/mapwms.c index 20ddfcc0b3..73fd55131e 100644 --- a/mapwms.c +++ b/mapwms.c @@ -1021,17 +1021,18 @@ int msWMSLoadGetMapParams(mapObj *map, int nVersion, if (strcasecmp(names[i], "SLD") == 0 || strcasecmp(names[i], "SLD_BODY") == 0) { sldenabled = msOWSLookupMetadata(&(map->web.metadata), "MO", "sld_enabled"); - if (sldenabled == NULL) + if (sldenabled == NULL) { sldenabled = "true"; + } - if (strcasecmp(sldenabled, "true") == 0) { - if (strcasecmp(names[i], "SLD") == 0) { - sld_url = values[i]; - } - if (strcasecmp(names[i], "SLD_BODY") == 0) { - sld_body = values[i]; - } + if (strcasecmp(sldenabled, "true") == 0) { + if (strcasecmp(names[i], "SLD") == 0) { + sld_url = values[i]; } + if (strcasecmp(names[i], "SLD_BODY") == 0) { + sld_body = values[i]; + } + } } } diff --git a/renderers/agg/include/agg_conv_curve.h b/renderers/agg/include/agg_conv_curve.h index dfaabf5c91..8df16f0c21 100644 --- a/renderers/agg/include/agg_conv_curve.h +++ b/renderers/agg/include/agg_conv_curve.h @@ -156,8 +156,8 @@ namespace mapserver double ct2_x = 0; double ct2_y = 0; - double end_x; - double end_y; + double end_x = 0; + double end_y = 0; unsigned cmd = m_source->vertex(x, y); switch(cmd) From 676691a9bdc0866aeecfb1e39852049947f9f437 Mon Sep 17 00:00:00 2001 From: MapServer-backport-bot <63150209+MapServer-backport-bot@users.noreply.github.com> Date: Thu, 9 Jul 2020 12:01:38 -0300 Subject: [PATCH 011/160] Hide some SWIG Java warnings (#6112) Co-authored-by: github-actions[bot] --- mapscript/java/CMakeLists.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mapscript/java/CMakeLists.txt b/mapscript/java/CMakeLists.txt index cdc918e47f..92731bb585 100644 --- a/mapscript/java/CMakeLists.txt +++ b/mapscript/java/CMakeLists.txt @@ -4,7 +4,7 @@ include(${SWIG_USE_FILE}) find_package(JNI) find_package(Java) if(NOT JNI_INCLUDE_DIRS OR NOT Java_JAVAC_EXECUTABLE OR NOT Java_JAR_EXECUTABLE) - message(SEND_ERROR "Could not find required Java componenents. Try setting the JAVA_HOME environment variable (required on e.g. Ubuntu)") + message(SEND_ERROR "Could not find required Java components. Try setting the JAVA_HOME environment variable (required on e.g. Ubuntu)") endif(NOT JNI_INCLUDE_DIRS OR NOT Java_JAVAC_EXECUTABLE OR NOT Java_JAR_EXECUTABLE) include_directories(${JNI_INCLUDE_DIRS}) @@ -14,6 +14,12 @@ include_directories(${PROJECT_SOURCE_DIR}/mapscript/java) set (CMAKE_SWIG_OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/edu/umn/gis/mapscript") set(CMAKE_SWIG_FLAGS -package edu.umn.gis.mapscript) +# hide warnings +if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID STREQUAL "Clang") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-strict-aliasing") +endif () + + if (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} VERSION_GREATER 3.7) swig_add_library(javamapscript TYPE MODULE LANGUAGE java SOURCES ../mapscript.i) else () From 3d2af949110dc8789196a8d9a8721ec27228d13f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 10 Jul 2020 20:04:10 +0000 Subject: [PATCH 012/160] Fix memory corruption when GEOMTRANSFORM and SHADOWCOLOR is used in label simultaneously #6114 --- maplabel.c | 10 ++++++++++ mapserver.h | 1 + 2 files changed, 11 insertions(+) diff --git a/maplabel.c b/maplabel.c index 52b84cfaa4..cfa2ed0bfe 100644 --- a/maplabel.c +++ b/maplabel.c @@ -143,6 +143,16 @@ void msCopyTextSymbol(textSymbolObj *dst, textSymbolObj *src) { dst->textpath = msSmallMalloc(sizeof(textPathObj)); msCopyTextPath(dst->textpath,src->textpath); } + if(dst->style_bounds) { + int i; + dst->style_bounds = msSmallCalloc(src->label->numstyles, sizeof(label_bounds*)); + for(i=0; ilabel->numstyles; i++) { + if(src->style_bounds[i]) { + dst->style_bounds[i] = msSmallMalloc(sizeof(label_bounds)); + copyLabelBounds(dst->style_bounds[i], src->style_bounds[i]); + } + } + } } static int labelNeedsDeepCopy(labelObj *label) { diff --git a/mapserver.h b/mapserver.h index 2b387cd83e..bef6245028 100644 --- a/mapserver.h +++ b/mapserver.h @@ -1843,6 +1843,7 @@ void msCopyTextPath(textPathObj *dst, textPathObj *src); void freeTextPath(textPathObj *tp); void initTextSymbol(textSymbolObj *ts); void freeTextSymbol(textSymbolObj *ts); +void copyLabelBounds(label_bounds *dst, label_bounds *src); void msCopyTextSymbol(textSymbolObj *dst, textSymbolObj *src); void msPopulateTextSymbolForLabelAndString(textSymbolObj *ts, labelObj *l, char *string, double scalefactor, double resolutionfactor, label_cache_mode cache); #endif /* SWIG */ From 1094e81315b6d95ec60050590c75ece9986563be Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 17 Jul 2020 11:35:22 +0000 Subject: [PATCH 013/160] Build error if USE_LIBXML2, USE_WMS_SVR, USE_WFS_SVR not defined (#6119) --- mapmetadata.c | 3 +++ mapogcfilter.c | 2 +- mapows.c | 2 ++ mapows.h | 4 ++-- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/mapmetadata.c b/mapmetadata.c index 1654dca584..4ce677d8a7 100644 --- a/mapmetadata.c +++ b/mapmetadata.c @@ -31,6 +31,7 @@ #include "mapowscommon.h" #include "maplibxml2.h" +#ifdef USE_LIBXML2 /************************************************************************/ /* _msMetadataGetCharacterString */ @@ -860,6 +861,8 @@ int msMetadataDispatch(mapObj *map, cgiRequestObj *cgi_request, owsRequestObj *o return status; } +#endif /* USE_LIBXML2 */ + /************************************************************************/ /* msMetadataCreateParamsObj */ /* */ diff --git a/mapogcfilter.c b/mapogcfilter.c index d6e1df9e94..2131d38651 100644 --- a/mapogcfilter.c +++ b/mapogcfilter.c @@ -2269,7 +2269,7 @@ char *FLTGetSQLExpression(FilterEncodingNode *psFilterNode, layerObj *lp) #else msSetError(MS_MISCERR, "OWS support is not available.", "FLTGetSQLExpression()"); - return(MS_FAILURE); + return NULL; #endif } diff --git a/mapows.c b/mapows.c index fbd98e48c6..c9be7598c0 100644 --- a/mapows.c +++ b/mapows.c @@ -261,11 +261,13 @@ int msOWSDispatch(mapObj *map, cgiRequestObj *request, int ows_mode) } if (ows_request.service == NULL) { +#ifdef USE_LIBXML2 if (ows_request.request && EQUAL(ows_request.request, "GetMetadata")) { status = msMetadataDispatch(map, request, &ows_request); msOWSClearRequestObj(&ows_request); return status; } +#endif #ifdef USE_WFS_SVR if( msOWSLookupMetadata(&(map->web.metadata), "FO", "cite_wfs2") != NULL ) { status = msWFSException(map, "service", MS_OWS_ERROR_MISSING_PARAMETER_VALUE, NULL ); diff --git a/mapows.h b/mapows.h index ed0402ec95..6c5bdc8d17 100644 --- a/mapows.h +++ b/mapows.h @@ -410,12 +410,12 @@ typedef struct { int numnamespaces; } gmlNamespaceListObj; +MS_DLL_EXPORT gmlItemListObj *msGMLGetItems(layerObj *layer, const char *metadata_namespaces); +MS_DLL_EXPORT void msGMLFreeItems(gmlItemListObj *itemList); #if defined(USE_WMS_SVR) || defined (USE_WFS_SVR) MS_DLL_EXPORT int msItemInGroups(const char *name, gmlGroupListObj *groupList); -MS_DLL_EXPORT gmlItemListObj *msGMLGetItems(layerObj *layer, const char *metadata_namespaces); -MS_DLL_EXPORT void msGMLFreeItems(gmlItemListObj *itemList); MS_DLL_EXPORT gmlConstantListObj *msGMLGetConstants(layerObj *layer, const char *metadata_namespaces); MS_DLL_EXPORT void msGMLFreeConstants(gmlConstantListObj *constantList); MS_DLL_EXPORT gmlGeometryListObj *msGMLGetGeometries(layerObj *layer, const char *metadata_namespaces, int bWithDefaultGeom); From d001ac686ea620ef482b51f6426c1aab4380bc11 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 21 Jul 2020 13:01:23 +0000 Subject: [PATCH 014/160] Don't use CIRCULARSTRING when fetching geography data from SQL Server. --- mapmssql2008.c | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/mapmssql2008.c b/mapmssql2008.c index c9dffb1a5e..6cb8515a3c 100644 --- a/mapmssql2008.c +++ b/mapmssql2008.c @@ -1341,9 +1341,15 @@ static int prepare_database(layerObj *layer, rectObj rect, char **query_string) /* "Geometry::STGeomFromText('POLYGON(())',)" + terminator = 40 chars Plus 10 formatted doubles (15 digits of precision, a decimal point, a space/comma delimiter each = 17 chars each) + Plus SRID + comma - if SRID is a long...we'll be safe with 10 chars + + or for geography columns + + "Geography::STGeomFromText('CURVEPOLYGON(())',)" + terminator = 46 chars + Plus 18 formatted doubles (15 digits of precision, a decimal point, a space/comma delimiter each = 17 chars each) Plus SRID + comma - if SRID is a long...we'll be safe with 10 chars */ - char box3d[40 + 10 * 22 + 11]; + char box3d[46 + 18 * 22 + 11]; int t; char *pos_from, *pos_ftab, *pos_space, *pos_paren; @@ -1399,10 +1405,25 @@ static int prepare_database(layerObj *layer, rectObj rect, char **query_string) /* create point shape for rectangles with zero area */ sprintf(box3d, "%s::STGeomFromText('POINT(%.15g %.15g)',%s)", /* %s.STSrid)", */ layerinfo->geom_column_type, rect.minx, rect.miny, layerinfo->user_srid); - } - else { - sprintf(box3d, "%s::STGeomFromText('POLYGON((%.15g %.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g))',%s)", /* %s.STSrid)", */ - layerinfo->geom_column_type, + } else if (strcasecmp(layerinfo->geom_column_type, "geography") == 0) { + /* SQL Server has a problem when x is -180 or 180 */ + double minx = rect.minx <= -180? -179.999: rect.minx; + double maxx = rect.maxx >= 180? 179.999: rect.maxx; + double miny = rect.miny < -90? -90: rect.miny; + double maxy = rect.maxy > 90? 90: rect.maxy; + sprintf(box3d, "Geography::STGeomFromText('CURVEPOLYGON((%.15g %.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g))',%s)", /* %s.STSrid)", */ + minx, miny, + minx + (maxx - minx) / 2, miny, + maxx, miny, + maxx, miny + (maxy - miny) / 2, + maxx, maxy, + minx + (maxx - minx) / 2, maxy, + minx, maxy, + minx, miny + (maxy - miny) / 2, + minx, miny, + layerinfo->user_srid); + } else { + sprintf(box3d, "Geometry::STGeomFromText('POLYGON((%.15g %.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g))',%s)", /* %s.STSrid)", */ rect.minx, rect.miny, rect.maxx, rect.miny, rect.maxx, rect.maxy, From d4facd1f2c27926d1ecd01469e1e33ffbd7102c4 Mon Sep 17 00:00:00 2001 From: jmckenna Date: Fri, 31 Jul 2020 11:56:16 -0300 Subject: [PATCH 015/160] manually apply PR 6124 --- mapogr.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mapogr.cpp b/mapogr.cpp index 1c25b3c523..c157ebe4a3 100644 --- a/mapogr.cpp +++ b/mapogr.cpp @@ -3447,7 +3447,11 @@ static int msOGRExtractTopSpatialFilter( msOGRFileInfo *info, pSpatialFilterNode); } - if( (expr->m_nToken == MS_TOKEN_COMPARISON_INTERSECTS || expr->m_nToken == MS_TOKEN_COMPARISON_CONTAINS ) && + if( (expr->m_nToken == MS_TOKEN_COMPARISON_INTERSECTS || + expr->m_nToken == MS_TOKEN_COMPARISON_OVERLAPS || + expr->m_nToken == MS_TOKEN_COMPARISON_CROSSES || + expr->m_nToken == MS_TOKEN_COMPARISON_WITHIN || + expr->m_nToken == MS_TOKEN_COMPARISON_CONTAINS) && expr->m_aoChildren.size() == 2 && expr->m_aoChildren[1]->m_nToken == MS_TOKEN_LITERAL_SHAPE ) { From 74ae370a132f68ebeb60b48567e174cf9be2b5c1 Mon Sep 17 00:00:00 2001 From: Jeff McKenna Date: Fri, 31 Jul 2020 14:11:56 -0300 Subject: [PATCH 016/160] update for 7.6.1 release --- CMakeLists.txt | 2 +- HISTORY.TXT | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 65e0944b8a..2d9ea8b592 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ include(CheckCSourceCompiles) set (MapServer_VERSION_MAJOR 7) set (MapServer_VERSION_MINOR 6) -set (MapServer_VERSION_REVISION 0) +set (MapServer_VERSION_REVISION 1) set (MapServer_VERSION_SUFFIX "") # Set C++ version diff --git a/HISTORY.TXT b/HISTORY.TXT index e3067cf725..76b3d98058 100644 --- a/HISTORY.TXT +++ b/HISTORY.TXT @@ -12,6 +12,11 @@ For a complete change history, please see the Git log comments. For more details about recent point releases, please see the online changelog at: http://mapserver.org/development/changelog/ +7.6.1 release (2020-07-31) +------------------------- + +- No major changes, see detailed changelog for bug fixes + 7.6.0 release (2020-05-08) -------------------------- From 9236ef68527c99a36b154a1d470058a06232aa92 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 7 Aug 2020 13:52:00 +0000 Subject: [PATCH 017/160] add content encoding to OL template --- maptemplate.c | 1 + 1 file changed, 1 insertion(+) diff --git a/maptemplate.c b/maptemplate.c index 896929814f..4610cad94c 100644 --- a/maptemplate.c +++ b/maptemplate.c @@ -47,6 +47,7 @@ static char *olUrl = "//www.mapserver.org/lib/OpenLayers-ms60.js"; static char *olTemplate = \ "\n" "\n" + "\n" " MapServer Simple Viewer\n" " \n" " \n" From 05a60e5b259af85db39c60fefd2cc3b6fb1a990e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 7 Aug 2020 17:04:47 +0000 Subject: [PATCH 018/160] add favicon to OL template --- maptemplate.c | 1 + 1 file changed, 1 insertion(+) diff --git a/maptemplate.c b/maptemplate.c index 4610cad94c..b2cb2a0c34 100644 --- a/maptemplate.c +++ b/maptemplate.c @@ -50,6 +50,7 @@ static char *olTemplate = \ "\n" " MapServer Simple Viewer\n" " \n" + " \n" " \n" " \n" "
\n" From 4da50ee43b7b011640e6d5b82e9e8ddfa26899c8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 4 Sep 2020 07:47:34 +0000 Subject: [PATCH 019/160] WCS 1.1 and 2.0: fix support of netCDF output (complementary fix to refs #5968) --- mapgdal.c | 18 +++++++++++++----- mapgdal.h | 46 ++++++++++++++++++++++++++++++++++++++++++++++ mapwcs11.c | 5 ++--- mapwcs20.c | 4 ++-- 4 files changed, 63 insertions(+), 10 deletions(-) create mode 100644 mapgdal.h diff --git a/mapgdal.c b/mapgdal.c index 630f163f55..a4641ac075 100644 --- a/mapgdal.c +++ b/mapgdal.c @@ -29,6 +29,7 @@ #include "mapserver.h" #include "mapthread.h" +#include "mapgdal.h" #include @@ -184,11 +185,7 @@ int msSaveImageGDAL( mapObj *map, imageObj *image, const char *filenameIn ) pszExtension = "img.tmp"; if( bUseXmp == MS_FALSE && - GDALGetMetadataItem( hOutputDriver, GDAL_DCAP_VIRTUALIO, NULL ) != NULL && - /* We need special testing here for the netCDF driver, since recent */ - /* GDAL versions advertize VirtualIO support, but this is only for the */ - /* read-side of the driver, not the write-side. */ - !EQUAL(gdal_driver_shortname, "netCDF") ) { + msGDALDriverSupportsVirtualIOOutput(hOutputDriver) ) { msCleanVSIDir( "/vsimem/msout" ); filenameToFree = msTmpFile(map, NULL, "/vsimem/msout/", pszExtension ); } @@ -653,4 +650,15 @@ char *msProjectionObj2OGCWKT( projectionObj *projection ) return NULL; } +/************************************************************************/ +/* msGDALDriverSupportsVirtualIOOutput() */ +/************************************************************************/ +int msGDALDriverSupportsVirtualIOOutput( GDALDriverH hDriver ) +{ + /* We need special testing here for the netCDF driver, since recent */ + /* GDAL versions advertize VirtualIO support, but this is only for the */ + /* read-side of the driver, not the write-side. */ + return GDALGetMetadataItem( hDriver, GDAL_DCAP_VIRTUALIO, NULL ) != NULL && + !EQUAL(GDALGetDescription(hDriver), "netCDF"); +} diff --git a/mapgdal.h b/mapgdal.h new file mode 100644 index 0000000000..45ec7eb26b --- /dev/null +++ b/mapgdal.h @@ -0,0 +1,46 @@ +/****************************************************************************** + * $Id$ + * + * Project: MapServer + * Purpose: GDAL interface + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (c) 2020, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies of this Software or works derived from this Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef MAPGDAL_H +#define MAPGDAL_H + +#include "mapserver.h" +#include "gdal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int msGDALDriverSupportsVirtualIOOutput( GDALDriverH hDriver ); + +#ifdef __cplusplus +} +#endif + +#endif /* MAPGDAL_H */ diff --git a/mapwcs11.c b/mapwcs11.c index f5fe2b37cb..2bfa73118e 100644 --- a/mapwcs11.c +++ b/mapwcs11.c @@ -35,7 +35,7 @@ #include "mapthread.h" #include "mapows.h" #include "mapwcs.h" - +#include "mapgdal.h" #if defined(USE_WCS_SVR) @@ -1188,8 +1188,7 @@ int msWCSReturnCoverage11( wcsParamsObj *params, mapObj *map, if( pszExtension == NULL ) pszExtension = "img.tmp"; - if( GDALGetMetadataItem( hDriver, GDAL_DCAP_VIRTUALIO, NULL ) - != NULL ) { + if( msGDALDriverSupportsVirtualIOOutput(hDriver) ) { base_dir = msTmpFile(map, map->mappath, "/vsimem/wcsout", NULL); if( fo_filename ) filename = msStrdup(CPLFormFilename(base_dir, diff --git a/mapwcs20.c b/mapwcs20.c index a258fdcaa6..37078ac2e8 100644 --- a/mapwcs20.c +++ b/mapwcs20.c @@ -38,6 +38,7 @@ #include "mapthread.h" #include "mapows.h" #include "mapwcs.h" +#include "mapgdal.h" #include #include "gdal.h" #include "cpl_port.h" @@ -2328,8 +2329,7 @@ static int msWCSWriteFile20(mapObj* map, imageObj* image, wcs20ParamsObjPtr para if( pszExtension == NULL ) pszExtension = "img.tmp"; - if( GDALGetMetadataItem( hDriver, GDAL_DCAP_VIRTUALIO, NULL ) - != NULL ) { + if( msGDALDriverSupportsVirtualIOOutput(hDriver) ) { base_dir = msTmpFile(map, map->mappath, "/vsimem/wcsout", NULL); if( fo_filename ) filename = msStrdup(CPLFormFilename(base_dir, From 636d506a8bdfa8a9725a64234ae44912b516b67f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 24 Sep 2020 11:04:30 +0000 Subject: [PATCH 020/160] FindOracle.cmake: add support for OCI 19 --- cmake/FindOracle.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/FindOracle.cmake b/cmake/FindOracle.cmake index 2568b5e735..4a8697a2d9 100644 --- a/cmake/FindOracle.cmake +++ b/cmake/FindOracle.cmake @@ -37,7 +37,7 @@ if(DEFINED ENV{ORACLE_HOME}) ${ORACLE_HOME}/OCI/include) # Oracle XE on Windows set(ORACLE_OCI_NAMES clntsh libclntsh oci) - set(ORACLE_NNZ_NAMES nnz10 libnnz10 nnz11 libnnz11 nnz12 libnnz12 nnz18 libnnz18 ociw32) + set(ORACLE_NNZ_NAMES nnz10 libnnz10 nnz11 libnnz11 nnz12 libnnz12 nnz18 libnnz18 ociw32 libnnz19) set(ORACLE_OCCI_NAMES libocci occi oraocci10 oraocci11 oraocci12) set(ORACLE_LIB_DIR From a8e9a24e18ab36fe63cee601dc61cd60917397b8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 3 Oct 2020 21:55:47 +0000 Subject: [PATCH 021/160] Apply CONFIG PROJ_LIB to GDAL/OGR PROJ settings --- mapproject.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/mapproject.c b/mapproject.c index 5659c59fea..e432ae1ef7 100644 --- a/mapproject.c +++ b/mapproject.c @@ -36,6 +36,8 @@ #include #include "mapaxisorder.h" +#include "ogr_srs_api.h" + static char *ms_proj_lib = NULL; #if PROJ_VERSION_MAJOR >= 6 static unsigned ms_proj_lib_change_counter = 0; @@ -2399,6 +2401,14 @@ void msSetPROJ_LIB( const char *proj_lib, const char *pszRelToPath ) #endif msReleaseLock( TLOCK_PROJ ); +#if GDAL_VERSION_MAJOR >= 3 + if( ms_proj_lib != NULL ) + { + const char* const apszPaths[] = { ms_proj_lib, NULL }; + OSRSetPROJSearchPaths(apszPaths); + } +#endif + if ( extended_path ) msFree( extended_path ); } From 2039bcacc9e0fe079899bd127adb82f51ac9708b Mon Sep 17 00:00:00 2001 From: Anders Samuelsson Date: Mon, 28 Oct 2019 07:57:00 +0100 Subject: [PATCH 022/160] Changes to locking with USE_THREAD --- fontcache.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/fontcache.c b/fontcache.c index f29ed46b73..28c5c56899 100644 --- a/fontcache.c +++ b/fontcache.c @@ -194,6 +194,10 @@ unsigned int msGetGlyphIndex(face_element *face, unsigned int unicode) { if(face->face->charmap && face->face->charmap->encoding == FT_ENCODING_MS_SYMBOL) { unicode |= 0xf000; /* why? */ } +#ifdef USE_THREAD + if (use_global_ft_cache) + msAcquireLock(TLOCK_TTF); +#endif UT_HASH_FIND_INT(face->index_cache,&unicode,ic); if(!ic) { ic = msSmallMalloc(sizeof(index_element)); @@ -201,6 +205,10 @@ unsigned int msGetGlyphIndex(face_element *face, unsigned int unicode) { ic->unicode = unicode; UT_HASH_ADD_INT(face->index_cache,unicode,ic); } +#ifdef USE_THREAD + if (use_global_ft_cache) + msReleaseLock(TLOCK_TTF); +#endif return ic->codepoint; } @@ -267,6 +275,10 @@ glyph_element* msGetGlyphByIndex(face_element *face, unsigned int size, unsigned memset(&key,0,sizeof(glyph_element_key)); key.codepoint = codepoint; key.size = size; +#ifdef USE_THREAD + if (use_global_ft_cache) + msAcquireLock(TLOCK_TTF); +#endif UT_HASH_FIND(hh,face->glyph_cache,&key,sizeof(glyph_element_key),gc); if(!gc) { FT_Error error; @@ -278,6 +290,10 @@ glyph_element* msGetGlyphByIndex(face_element *face, unsigned int size, unsigned if(error) { msSetError(MS_MISCERR, "unable to load glyph %ud for font \"%s\"", "msGetGlyphByIndex()",key.codepoint, face->font); free(gc); +#ifdef USE_THREAD + if (use_global_ft_cache) + msReleaseLock(TLOCK_TTF); +#endif return NULL; } gc->metrics.minx = face->face->glyph->metrics.horiBearingX / 64.0; @@ -288,6 +304,10 @@ glyph_element* msGetGlyphByIndex(face_element *face, unsigned int size, unsigned gc->key = key; UT_HASH_ADD(hh,face->glyph_cache,key,sizeof(glyph_element_key), gc); } +#ifdef USE_THREAD + if (use_global_ft_cache) + msReleaseLock(TLOCK_TTF); +#endif return gc; } From 57aba2019797b604962e7bad875b783b3fde9cca Mon Sep 17 00:00:00 2001 From: Krister Wicksell Date: Thu, 24 Sep 2020 13:08:10 +0200 Subject: [PATCH 023/160] Don't remove all memory files before creating a new. When WMS layers use memory files as temporary storage we can't delete all existing memory files before creating a new. This will generate a lot of "Unable to access file..." errors in a multi-threded application that uses MapScript. However, we can always remove the temporary memory files even if the layer has debug enabled. --- mapwmslayer.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mapwmslayer.c b/mapwmslayer.c index 899db5e91f..4e1bcb93c7 100755 --- a/mapwmslayer.c +++ b/mapwmslayer.c @@ -1414,7 +1414,6 @@ int msDrawWMSLayerLow(int nLayerId, httpRequestObj *pasReqInfo, * to attach a "VSI" name to this buffer. * ------------------------------------------------------------------ */ if( pasReqInfo[iReq].pszOutputFile == NULL ) { - msCleanVSIDir( "/vsimem/msout" ); mem_filename = msTmpFile(map, NULL, "/vsimem/msout/", "img.tmp" ); VSIFCloseL( @@ -1496,7 +1495,7 @@ int msDrawWMSLayerLow(int nLayerId, httpRequestObj *pasReqInfo, if (msDrawLayer(map, lp, img) != 0) status = MS_FAILURE; - if (!lp->debug) + if (!lp->debug || mem_filename != NULL) VSIUnlink( wldfile ); } else { msSetError(MS_WMSCONNERR, @@ -1508,7 +1507,7 @@ int msDrawWMSLayerLow(int nLayerId, httpRequestObj *pasReqInfo, } /* We're done with the remote server's response... delete it. */ - if (!lp->debug) + if (!lp->debug || mem_filename != NULL) VSIUnlink(lp->data); /* restore prveious type */ From 1efc1402ae6458c8bfe8cad5e9ced9a906eb6bad Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 4 Oct 2020 11:46:45 +0000 Subject: [PATCH 024/160] Enable check-crlf github action --- .github/workflows/check-crlf.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/workflows/check-crlf.yml diff --git a/.github/workflows/check-crlf.yml b/.github/workflows/check-crlf.yml new file mode 100644 index 0000000000..0d2c314785 --- /dev/null +++ b/.github/workflows/check-crlf.yml @@ -0,0 +1,20 @@ +# check for Windows CRLF in files +# homepage: https://github.com/marketplace/actions/check-crlf + +name: Check CRLF + +on: [push, pull_request] + +jobs: + Check-CRLF: + name: verify that only LF linefeeds are used + runs-on: ubuntu-18.04 + + steps: + - name: Checkout repository contents + uses: actions/checkout@v1 + + - name: Use action to check for CRLF endings + uses: erclu/check-crlf@v1.1.2 + with: # ignore directories containing *.pdf and *.tab + exclude: msautotest/misc/data/ /msautotest/renderers/expected/ \ No newline at end of file From 85badb62f7e4b22d2bedce3cf5cbc04a591523c3 Mon Sep 17 00:00:00 2001 From: jmckenna Date: Sun, 4 Oct 2020 09:21:30 -0300 Subject: [PATCH 025/160] change to LF --- cmake/FindProj.cmake | 80 +- mapscript/csharp/examples/drawmapDirect.cs | 200 +- .../csharp/examples/drawmapDirectPrint.cs | 232 +- mapscript/csharp/examples/inline.cs | 270 +- msautotest/misc/ogr_direct.map | 58 +- msautotest/mssql/create_mssql_db.bat | 14 +- .../mssql/include/bdry_counpy2_mssql.map | 26 +- msautotest/mssql/include/cities_mssql.map | 12 +- renderers/agg/include/agg_conv_clipper.h | 598 +- renderers/agg/include/clipper.hpp | 604 +- renderers/agg/src/clipper.cpp | 6604 ++++++++--------- version.rc.in | 104 +- 12 files changed, 4401 insertions(+), 4401 deletions(-) diff --git a/cmake/FindProj.cmake b/cmake/FindProj.cmake index 22dee8dffb..e2b64a68bc 100644 --- a/cmake/FindProj.cmake +++ b/cmake/FindProj.cmake @@ -1,40 +1,40 @@ -# Find Proj -# -# If it's found it sets PROJ_FOUND to TRUE -# and following variables are set: -# PROJ_INCLUDE_DIR -# PROJ_LIBRARY - - -FIND_PATH(PROJ_INCLUDE_DIR proj_api.h) - -FIND_LIBRARY(PROJ_LIBRARY NAMES proj proj_i) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(PROJ DEFAULT_MSG PROJ_LIBRARY PROJ_INCLUDE_DIR) -mark_as_advanced(PROJ_LIBRARY PROJ_INCLUDE_DIR) - - -IF (PROJ_INCLUDE_DIR AND PROJ_LIBRARY) - SET(PROJ_FOUND TRUE) -ENDIF (PROJ_INCLUDE_DIR AND PROJ_LIBRARY) - -IF (PROJ_FOUND) - IF (EXISTS ${PROJ_INCLUDE_DIR}/proj.h) - FILE(READ ${PROJ_INCLUDE_DIR}/proj.h proj_version) - STRING(REGEX REPLACE "^.*PROJ_VERSION_MAJOR +([0-9]+).*$" "\\1" PROJ_VERSION_MAJOR "${proj_version}") - STRING(REGEX REPLACE "^.*PROJ_VERSION_MINOR +([0-9]+).*$" "\\1" PROJ_VERSION_MINOR "${proj_version}") - STRING(REGEX REPLACE "^.*PROJ_VERSION_PATCH +([0-9]+).*$" "\\1" PROJ_VERSION_PATCH "${proj_version}") - - MESSAGE(STATUS "Found Proj ${PROJ_VERSION_MAJOR}.${PROJ_VERSION_MINOR}") - - IF ((PROJ_VERSION_MAJOR EQUAL 6) AND (PROJ_VERSION_MINOR EQUAL 3) AND (PROJ_VERSION_PATCH EQUAL 0)) - MESSAGE (FATAL_ERROR "MapServer known to crash with PROJ 6.3.0. Use 6.3.1 or higher.") - ENDIF ((PROJ_VERSION_MAJOR EQUAL 6) AND (PROJ_VERSION_MINOR EQUAL 3) AND (PROJ_VERSION_PATCH EQUAL 0)) - - ADD_DEFINITIONS(-DPROJ_VERSION_MAJOR=${PROJ_VERSION_MAJOR}) - ELSE() - MESSAGE(STATUS "Found Proj 4.x") - ADD_DEFINITIONS(-DPROJ_VERSION_MAJOR=4) - ENDIF() -ENDIF (PROJ_FOUND) +# Find Proj +# +# If it's found it sets PROJ_FOUND to TRUE +# and following variables are set: +# PROJ_INCLUDE_DIR +# PROJ_LIBRARY + + +FIND_PATH(PROJ_INCLUDE_DIR proj_api.h) + +FIND_LIBRARY(PROJ_LIBRARY NAMES proj proj_i) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(PROJ DEFAULT_MSG PROJ_LIBRARY PROJ_INCLUDE_DIR) +mark_as_advanced(PROJ_LIBRARY PROJ_INCLUDE_DIR) + + +IF (PROJ_INCLUDE_DIR AND PROJ_LIBRARY) + SET(PROJ_FOUND TRUE) +ENDIF (PROJ_INCLUDE_DIR AND PROJ_LIBRARY) + +IF (PROJ_FOUND) + IF (EXISTS ${PROJ_INCLUDE_DIR}/proj.h) + FILE(READ ${PROJ_INCLUDE_DIR}/proj.h proj_version) + STRING(REGEX REPLACE "^.*PROJ_VERSION_MAJOR +([0-9]+).*$" "\\1" PROJ_VERSION_MAJOR "${proj_version}") + STRING(REGEX REPLACE "^.*PROJ_VERSION_MINOR +([0-9]+).*$" "\\1" PROJ_VERSION_MINOR "${proj_version}") + STRING(REGEX REPLACE "^.*PROJ_VERSION_PATCH +([0-9]+).*$" "\\1" PROJ_VERSION_PATCH "${proj_version}") + + MESSAGE(STATUS "Found Proj ${PROJ_VERSION_MAJOR}.${PROJ_VERSION_MINOR}") + + IF ((PROJ_VERSION_MAJOR EQUAL 6) AND (PROJ_VERSION_MINOR EQUAL 3) AND (PROJ_VERSION_PATCH EQUAL 0)) + MESSAGE (FATAL_ERROR "MapServer known to crash with PROJ 6.3.0. Use 6.3.1 or higher.") + ENDIF ((PROJ_VERSION_MAJOR EQUAL 6) AND (PROJ_VERSION_MINOR EQUAL 3) AND (PROJ_VERSION_PATCH EQUAL 0)) + + ADD_DEFINITIONS(-DPROJ_VERSION_MAJOR=${PROJ_VERSION_MAJOR}) + ELSE() + MESSAGE(STATUS "Found Proj 4.x") + ADD_DEFINITIONS(-DPROJ_VERSION_MAJOR=4) + ENDIF() +ENDIF (PROJ_FOUND) diff --git a/mapscript/csharp/examples/drawmapDirect.cs b/mapscript/csharp/examples/drawmapDirect.cs index 6edc6112eb..dc4be33ffc 100644 --- a/mapscript/csharp/examples/drawmapDirect.cs +++ b/mapscript/csharp/examples/drawmapDirect.cs @@ -1,100 +1,100 @@ -/****************************************************************************** - * $Id$ - * - * Project: MapServer - * Purpose: A C# based mapscript example to draw the map directly onto a GDI - * device context. - * Author: Tamas Szekeres, szekerest@gmail.com - * - ****************************************************************************** - * Copyright (c) 1996-2008 Regents of the University of Minnesota. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies of this Software or works derived from this Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - *****************************************************************************/ - -using System; -using System.Drawing; -using System.Drawing.Imaging; -using OSGeo.MapServer; - -/// -/// A C# based mapscript example to draw the map directly onto a GDI device context. -/// -class DrawMap -{ - public static void usage() - { - Console.WriteLine("usage: DrawMapDirect {mapfile} {outfile}"); - System.Environment.Exit(-1); - } - - public static void Main(string[] args) - { - Console.WriteLine(""); - if (args.Length < 2) usage(); - - mapObj map = new mapObj(args[0]); - - Console.WriteLine("# Map layers " + map.numlayers + "; Map name = " + map.name); - for (int i = 0; i < map.numlayers; i++) - { - Console.WriteLine("Layer [" + i + "] name: " + map.getLayer(i).name); - } - - try - { - // Create the output format - outputFormatObj of = new outputFormatObj("CAIRO/WINGDI", "cairowinGDI"); - map.appendOutputFormat(of); - map.selectOutputFormat("cairowinGDI"); - - Bitmap mapImage = new Bitmap(map.width, map.height, PixelFormat.Format32bppRgb); - - using (Graphics g = Graphics.FromImage(mapImage)) - { - IntPtr hdc = g.GetHdc(); - try - { - // Attach the device to the outputformat for drawing - of.attachDevice(hdc); - // Drawing directly to the GDI context - using (imageObj image = map.draw()) { }; - } - finally - { - of.attachDevice(IntPtr.Zero); - g.ReleaseHdc(hdc); - } - } - - mapImage.Save(args[1]); - } - catch (Exception ex) - { - Console.WriteLine( "\nMessage ---\n{0}", ex.Message ); - Console.WriteLine( - "\nHelpLink ---\n{0}", ex.HelpLink ); - Console.WriteLine( "\nSource ---\n{0}", ex.Source ); - Console.WriteLine( - "\nStackTrace ---\n{0}", ex.StackTrace ); - Console.WriteLine( - "\nTargetSite ---\n{0}", ex.TargetSite ); } - } -} - +/****************************************************************************** + * $Id$ + * + * Project: MapServer + * Purpose: A C# based mapscript example to draw the map directly onto a GDI + * device context. + * Author: Tamas Szekeres, szekerest@gmail.com + * + ****************************************************************************** + * Copyright (c) 1996-2008 Regents of the University of Minnesota. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies of this Software or works derived from this Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +using System; +using System.Drawing; +using System.Drawing.Imaging; +using OSGeo.MapServer; + +/// +/// A C# based mapscript example to draw the map directly onto a GDI device context. +/// +class DrawMap +{ + public static void usage() + { + Console.WriteLine("usage: DrawMapDirect {mapfile} {outfile}"); + System.Environment.Exit(-1); + } + + public static void Main(string[] args) + { + Console.WriteLine(""); + if (args.Length < 2) usage(); + + mapObj map = new mapObj(args[0]); + + Console.WriteLine("# Map layers " + map.numlayers + "; Map name = " + map.name); + for (int i = 0; i < map.numlayers; i++) + { + Console.WriteLine("Layer [" + i + "] name: " + map.getLayer(i).name); + } + + try + { + // Create the output format + outputFormatObj of = new outputFormatObj("CAIRO/WINGDI", "cairowinGDI"); + map.appendOutputFormat(of); + map.selectOutputFormat("cairowinGDI"); + + Bitmap mapImage = new Bitmap(map.width, map.height, PixelFormat.Format32bppRgb); + + using (Graphics g = Graphics.FromImage(mapImage)) + { + IntPtr hdc = g.GetHdc(); + try + { + // Attach the device to the outputformat for drawing + of.attachDevice(hdc); + // Drawing directly to the GDI context + using (imageObj image = map.draw()) { }; + } + finally + { + of.attachDevice(IntPtr.Zero); + g.ReleaseHdc(hdc); + } + } + + mapImage.Save(args[1]); + } + catch (Exception ex) + { + Console.WriteLine( "\nMessage ---\n{0}", ex.Message ); + Console.WriteLine( + "\nHelpLink ---\n{0}", ex.HelpLink ); + Console.WriteLine( "\nSource ---\n{0}", ex.Source ); + Console.WriteLine( + "\nStackTrace ---\n{0}", ex.StackTrace ); + Console.WriteLine( + "\nTargetSite ---\n{0}", ex.TargetSite ); } + } +} + diff --git a/mapscript/csharp/examples/drawmapDirectPrint.cs b/mapscript/csharp/examples/drawmapDirectPrint.cs index 330b7abbcf..1b5c9800b6 100644 --- a/mapscript/csharp/examples/drawmapDirectPrint.cs +++ b/mapscript/csharp/examples/drawmapDirectPrint.cs @@ -1,116 +1,116 @@ -/****************************************************************************** - * $Id$ - * - * Project: MapServer - * Purpose: A C# based mapscript example to draw the map directly onto a GDI - * printing device context. - * Author: Tamas Szekeres, szekerest@gmail.com - * - ****************************************************************************** - * Copyright (c) 1996-2008 Regents of the University of Minnesota. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies of this Software or works derived from this Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - *****************************************************************************/ - -using System; -using System.Drawing; -using System.Drawing.Printing; -using OSGeo.MapServer; - -/// -/// A C# based mapscript example to draw the map directly onto a GDI printing device context. -/// -class DrawMap -{ - public static void usage() - { - Console.WriteLine("usage: DrawMapDirectPrint {mapfile} {printername}"); - System.Environment.Exit(-1); - } - - static mapObj map; - - public static void Main(string[] args) - { - Console.WriteLine(""); - if (args.Length < 2) usage(); - - map = new mapObj(args[0]); - - Console.WriteLine("# Map layers " + map.numlayers + "; Map name = " + map.name); - for (int i = 0; i < map.numlayers; i++) - { - Console.WriteLine("Layer [" + i + "] name: " + map.getLayer(i).name); - } - - try - { - PrintDocument doc = new PrintDocument(); - - doc.PrintPage += new PrintPageEventHandler(doc_PrintPage); - - // Specify the printer to use. - doc.PrinterSettings.PrinterName = args[1]; - - doc.Print(); - } - catch (Exception ex) - { - Console.WriteLine( "\nMessage ---\n{0}", ex.Message ); - Console.WriteLine( - "\nHelpLink ---\n{0}", ex.HelpLink ); - Console.WriteLine( "\nSource ---\n{0}", ex.Source ); - Console.WriteLine( - "\nStackTrace ---\n{0}", ex.StackTrace ); - Console.WriteLine( - "\nTargetSite ---\n{0}", ex.TargetSite ); } - } - - static void doc_PrintPage(object sender, PrintPageEventArgs e) - { - // Create the output format - outputFormatObj of = new outputFormatObj("CAIRO/WINGDIPRINT", "cairowinGDIPrint"); - map.appendOutputFormat(of); - map.selectOutputFormat("cairowinGDIPrint"); - map.resolution = e.Graphics.DpiX; - Console.WriteLine("map resolution = " + map.resolution.ToString() + "DPI defresolution = " + map.defresolution.ToString() + " DPI"); - // Calculating the desired image size to cover the entire area; - map.width = Convert.ToInt32(e.PageBounds.Width * e.Graphics.DpiX / 100); - map.height = Convert.ToInt32(e.PageBounds.Height * e.Graphics.DpiY / 100); - - Console.WriteLine("map size = " + map.width.ToString() + " * " + map.height.ToString() + " pixels"); - - IntPtr hdc = e.Graphics.GetHdc(); - try - { - // Attach the device to the outputformat for drawing - of.attachDevice(hdc); - // Drawing directly to the GDI context - using (imageObj image = map.draw()) { }; - } - finally - { - of.attachDevice(IntPtr.Zero); - e.Graphics.ReleaseHdc(hdc); - } - - e.HasMorePages = false; - } -} - +/****************************************************************************** + * $Id$ + * + * Project: MapServer + * Purpose: A C# based mapscript example to draw the map directly onto a GDI + * printing device context. + * Author: Tamas Szekeres, szekerest@gmail.com + * + ****************************************************************************** + * Copyright (c) 1996-2008 Regents of the University of Minnesota. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies of this Software or works derived from this Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +using System; +using System.Drawing; +using System.Drawing.Printing; +using OSGeo.MapServer; + +/// +/// A C# based mapscript example to draw the map directly onto a GDI printing device context. +/// +class DrawMap +{ + public static void usage() + { + Console.WriteLine("usage: DrawMapDirectPrint {mapfile} {printername}"); + System.Environment.Exit(-1); + } + + static mapObj map; + + public static void Main(string[] args) + { + Console.WriteLine(""); + if (args.Length < 2) usage(); + + map = new mapObj(args[0]); + + Console.WriteLine("# Map layers " + map.numlayers + "; Map name = " + map.name); + for (int i = 0; i < map.numlayers; i++) + { + Console.WriteLine("Layer [" + i + "] name: " + map.getLayer(i).name); + } + + try + { + PrintDocument doc = new PrintDocument(); + + doc.PrintPage += new PrintPageEventHandler(doc_PrintPage); + + // Specify the printer to use. + doc.PrinterSettings.PrinterName = args[1]; + + doc.Print(); + } + catch (Exception ex) + { + Console.WriteLine( "\nMessage ---\n{0}", ex.Message ); + Console.WriteLine( + "\nHelpLink ---\n{0}", ex.HelpLink ); + Console.WriteLine( "\nSource ---\n{0}", ex.Source ); + Console.WriteLine( + "\nStackTrace ---\n{0}", ex.StackTrace ); + Console.WriteLine( + "\nTargetSite ---\n{0}", ex.TargetSite ); } + } + + static void doc_PrintPage(object sender, PrintPageEventArgs e) + { + // Create the output format + outputFormatObj of = new outputFormatObj("CAIRO/WINGDIPRINT", "cairowinGDIPrint"); + map.appendOutputFormat(of); + map.selectOutputFormat("cairowinGDIPrint"); + map.resolution = e.Graphics.DpiX; + Console.WriteLine("map resolution = " + map.resolution.ToString() + "DPI defresolution = " + map.defresolution.ToString() + " DPI"); + // Calculating the desired image size to cover the entire area; + map.width = Convert.ToInt32(e.PageBounds.Width * e.Graphics.DpiX / 100); + map.height = Convert.ToInt32(e.PageBounds.Height * e.Graphics.DpiY / 100); + + Console.WriteLine("map size = " + map.width.ToString() + " * " + map.height.ToString() + " pixels"); + + IntPtr hdc = e.Graphics.GetHdc(); + try + { + // Attach the device to the outputformat for drawing + of.attachDevice(hdc); + // Drawing directly to the GDI context + using (imageObj image = map.draw()) { }; + } + finally + { + of.attachDevice(IntPtr.Zero); + e.Graphics.ReleaseHdc(hdc); + } + + e.HasMorePages = false; + } +} + diff --git a/mapscript/csharp/examples/inline.cs b/mapscript/csharp/examples/inline.cs index 6c29207a95..43302158c3 100644 --- a/mapscript/csharp/examples/inline.cs +++ b/mapscript/csharp/examples/inline.cs @@ -1,136 +1,136 @@ -/****************************************************************************** - * $Id: shapeinfo.cs 7418 2008-02-29 00:02:49Z nsavard $ - * - * Project: MapServer - * Purpose: A C# based based mapscript example to dump information from - * a shapefile. - * Author: Tamas Szekeres, szekerest@gmail.com - * - ****************************************************************************** - * Copyright (c) 1996-2008 Regents of the University of Minnesota. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies of this Software or works derived from this Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - *****************************************************************************/ - -using System; -using System.Collections; -using OSGeo.MapServer; - -/// -/// A MapScript application for creating inline layers with annotations. -/// -class Inline { - - public static void usage() - { - Console.WriteLine("usage: inline [outformat] [outfile]"); - System.Environment.Exit(-1); - } - - public static void Main(string[] args) - { - - if (args.Length < 2) usage(); - - // creating a new map from scratch - mapObj map = new mapObj(null); - // adding a layer - layerObj layer = new layerObj(map); - layer.type = MS_LAYER_TYPE.MS_LAYER_POINT; - layer.status = mapscript.MS_ON; - layer.connectiontype = MS_CONNECTION_TYPE.MS_INLINE; - // define the attribute names from the inline layer - layer.addProcessing("ITEMS=attribute1,attribute2,attribute3"); - // define the class - classObj classobj = new classObj(layer); - classobj.template = "query"; // making the layer queryable - // setting up the text based on multiple attributes - classobj.setText("('Shape:' + '[attribute1]' + ' Color:' + '[attribute2]' + ' Size:' + '[attribute3]')"); - // define the label - classobj.label.outlinecolor = new colorObj(255, 255, 255, 0); - classobj.label.force = mapscript.MS_TRUE; - classobj.label.size = (double)MS_BITMAP_FONT_SIZES.MS_MEDIUM; - classobj.label.position = (int)MS_POSITIONS_ENUM.MS_LC; - classobj.label.wrap = ' '; - // set up attribute binding - classobj.label.setBinding((int)MS_LABEL_BINDING_ENUM.MS_LABEL_BINDING_COLOR, "attribute2"); - // define the style - styleObj style = new styleObj(classobj); - style.color = new colorObj(0, 255, 255, 0); - style.setBinding((int)MS_STYLE_BINDING_ENUM.MS_STYLE_BINDING_COLOR, "attribute2"); - style.setBinding((int)MS_STYLE_BINDING_ENUM.MS_STYLE_BINDING_SIZE, "attribute3"); - - Random rand = new Random((int)DateTime.Now.ToFileTime()); ; - - // creating the shapes - for (int i = 0; i < 10; i++) - { - shapeObj shape = new shapeObj((int)MS_SHAPE_TYPE.MS_SHAPE_POINT); - - // setting the shape attributes - shape.initValues(4); - shape.setValue(0, Convert.ToString(i)); - shape.setValue(1, new colorObj(rand.Next(255), rand.Next(255), rand.Next(255), 0).toHex()); - shape.setValue(2, Convert.ToString(rand.Next(25) + 5)); - - lineObj line = new lineObj(); - line.add(new pointObj(rand.Next(400) + 25, rand.Next(400) + 25, 0, 0)); - shape.add(line); - layer.addFeature(shape); - } - - map.width = 500; - map.height = 500; - map.setExtent(0,0,450,450); - map.selectOutputFormat(args[0]); - imageObj image = map.draw(); - image.save(args[1], map); - - //perform a query - layer.queryByRect(map, new rectObj(0, 0, 450, 450, 0)); - - resultObj res; - shapeObj feature; - using (resultCacheObj results = layer.getResults()) - { - if (results != null && results.numresults > 0) - { - // extracting the features found - layer.open(); - for (int j = 0; j < results.numresults; j++) - { - res = results.getResult(j); - feature = layer.getShape(res); - if (feature != null) - { - Console.WriteLine(" Feature: shapeindex=" + res.shapeindex + " tileindex=" + res.tileindex); - for (int k = 0; k < layer.numitems; k++) - { - Console.Write(" " + layer.getItem(k)); - Console.Write(" = "); - Console.Write(feature.getValue(k)); - Console.WriteLine(); - } - } - } - layer.close(); - } - } - } +/****************************************************************************** + * $Id: shapeinfo.cs 7418 2008-02-29 00:02:49Z nsavard $ + * + * Project: MapServer + * Purpose: A C# based based mapscript example to dump information from + * a shapefile. + * Author: Tamas Szekeres, szekerest@gmail.com + * + ****************************************************************************** + * Copyright (c) 1996-2008 Regents of the University of Minnesota. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies of this Software or works derived from this Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +using System; +using System.Collections; +using OSGeo.MapServer; + +/// +/// A MapScript application for creating inline layers with annotations. +/// +class Inline { + + public static void usage() + { + Console.WriteLine("usage: inline [outformat] [outfile]"); + System.Environment.Exit(-1); + } + + public static void Main(string[] args) + { + + if (args.Length < 2) usage(); + + // creating a new map from scratch + mapObj map = new mapObj(null); + // adding a layer + layerObj layer = new layerObj(map); + layer.type = MS_LAYER_TYPE.MS_LAYER_POINT; + layer.status = mapscript.MS_ON; + layer.connectiontype = MS_CONNECTION_TYPE.MS_INLINE; + // define the attribute names from the inline layer + layer.addProcessing("ITEMS=attribute1,attribute2,attribute3"); + // define the class + classObj classobj = new classObj(layer); + classobj.template = "query"; // making the layer queryable + // setting up the text based on multiple attributes + classobj.setText("('Shape:' + '[attribute1]' + ' Color:' + '[attribute2]' + ' Size:' + '[attribute3]')"); + // define the label + classobj.label.outlinecolor = new colorObj(255, 255, 255, 0); + classobj.label.force = mapscript.MS_TRUE; + classobj.label.size = (double)MS_BITMAP_FONT_SIZES.MS_MEDIUM; + classobj.label.position = (int)MS_POSITIONS_ENUM.MS_LC; + classobj.label.wrap = ' '; + // set up attribute binding + classobj.label.setBinding((int)MS_LABEL_BINDING_ENUM.MS_LABEL_BINDING_COLOR, "attribute2"); + // define the style + styleObj style = new styleObj(classobj); + style.color = new colorObj(0, 255, 255, 0); + style.setBinding((int)MS_STYLE_BINDING_ENUM.MS_STYLE_BINDING_COLOR, "attribute2"); + style.setBinding((int)MS_STYLE_BINDING_ENUM.MS_STYLE_BINDING_SIZE, "attribute3"); + + Random rand = new Random((int)DateTime.Now.ToFileTime()); ; + + // creating the shapes + for (int i = 0; i < 10; i++) + { + shapeObj shape = new shapeObj((int)MS_SHAPE_TYPE.MS_SHAPE_POINT); + + // setting the shape attributes + shape.initValues(4); + shape.setValue(0, Convert.ToString(i)); + shape.setValue(1, new colorObj(rand.Next(255), rand.Next(255), rand.Next(255), 0).toHex()); + shape.setValue(2, Convert.ToString(rand.Next(25) + 5)); + + lineObj line = new lineObj(); + line.add(new pointObj(rand.Next(400) + 25, rand.Next(400) + 25, 0, 0)); + shape.add(line); + layer.addFeature(shape); + } + + map.width = 500; + map.height = 500; + map.setExtent(0,0,450,450); + map.selectOutputFormat(args[0]); + imageObj image = map.draw(); + image.save(args[1], map); + + //perform a query + layer.queryByRect(map, new rectObj(0, 0, 450, 450, 0)); + + resultObj res; + shapeObj feature; + using (resultCacheObj results = layer.getResults()) + { + if (results != null && results.numresults > 0) + { + // extracting the features found + layer.open(); + for (int j = 0; j < results.numresults; j++) + { + res = results.getResult(j); + feature = layer.getShape(res); + if (feature != null) + { + Console.WriteLine(" Feature: shapeindex=" + res.shapeindex + " tileindex=" + res.tileindex); + for (int k = 0; k < layer.numitems; k++) + { + Console.Write(" " + layer.getItem(k)); + Console.Write(" = "); + Console.Write(feature.getValue(k)); + Console.WriteLine(); + } + } + } + layer.close(); + } + } + } } \ No newline at end of file diff --git a/msautotest/misc/ogr_direct.map b/msautotest/misc/ogr_direct.map index f07b817da4..01db41d120 100644 --- a/msautotest/misc/ogr_direct.map +++ b/msautotest/misc/ogr_direct.map @@ -1,29 +1,29 @@ -# -# Simple OGR Render. -# -# REQUIRES: INPUT=OGR OUTPUT=PNG -# -MAP - -STATUS ON -EXTENT 478300 4762880 481650 4765610 -SIZE 400 300 - -IMAGETYPE png - -LAYER - NAME shppoly - TYPE polygon - CONNECTIONTYPE OGR - CONNECTION "data/shppoly/poly.shp" - DATA "poly" - STATUS default - CLASSITEM "AREA" - CLASS - NAME "test1" - COLOR 0 255 0 - OUTLINECOLOR 255 0 0 - END -END - -END +# +# Simple OGR Render. +# +# REQUIRES: INPUT=OGR OUTPUT=PNG +# +MAP + +STATUS ON +EXTENT 478300 4762880 481650 4765610 +SIZE 400 300 + +IMAGETYPE png + +LAYER + NAME shppoly + TYPE polygon + CONNECTIONTYPE OGR + CONNECTION "data/shppoly/poly.shp" + DATA "poly" + STATUS default + CLASSITEM "AREA" + CLASS + NAME "test1" + COLOR 0 255 0 + OUTLINECOLOR 255 0 0 + END +END + +END diff --git a/msautotest/mssql/create_mssql_db.bat b/msautotest/mssql/create_mssql_db.bat index cb31f0f8c8..0518df1e88 100644 --- a/msautotest/mssql/create_mssql_db.bat +++ b/msautotest/mssql/create_mssql_db.bat @@ -1,7 +1,7 @@ -set SQLPASSWORD=Password12! -set SERVER=(local)\SQL2017 - -sqlcmd -S "%SERVER%" -Q "USE [master]; CREATE DATABASE msautotest;" - -ogr2ogr -s_srs epsg:26915 -t_srs epsg:26915 -f MSSQLSpatial "MSSQL:server=%SERVER%;database=msautotest;User Id=sa;Password=%SQLPASSWORD%;" "query/data/bdry_counpy2.shp" -nln "bdry_counpy2" -ogr2ogr -s_srs epsg:3857 -t_srs epsg:3857 -f MSSQLSpatial "MSSQL:server=%SERVER%;database=msautotest;User Id=sa;Password=%SQLPASSWORD%;" "renderers/data/cities.shp" -nln "cities" +set SQLPASSWORD=Password12! +set SERVER=(local)\SQL2017 + +sqlcmd -S "%SERVER%" -Q "USE [master]; CREATE DATABASE msautotest;" + +ogr2ogr -s_srs epsg:26915 -t_srs epsg:26915 -f MSSQLSpatial "MSSQL:server=%SERVER%;database=msautotest;User Id=sa;Password=%SQLPASSWORD%;" "query/data/bdry_counpy2.shp" -nln "bdry_counpy2" +ogr2ogr -s_srs epsg:3857 -t_srs epsg:3857 -f MSSQLSpatial "MSSQL:server=%SERVER%;database=msautotest;User Id=sa;Password=%SQLPASSWORD%;" "renderers/data/cities.shp" -nln "cities" diff --git a/msautotest/mssql/include/bdry_counpy2_mssql.map b/msautotest/mssql/include/bdry_counpy2_mssql.map index 236e7c52c2..55e5a9d8ab 100644 --- a/msautotest/mssql/include/bdry_counpy2_mssql.map +++ b/msautotest/mssql/include/bdry_counpy2_mssql.map @@ -1,13 +1,13 @@ - CONNECTIONTYPE PLUGIN - PLUGIN "C:\projects\mapserver\build\Release\msplugin_mssql2008.dll" - CONNECTION "SERVER=(local)\SQL2017;DATABASE=msautotest;uid=sa;pwd=Password12!;" - DATA "ogr_geometry from (select * from bdry_counpy2) as foo USING UNIQUE ogr_fid USING SRID=26915" - STATUS OFF - TYPE POLYGON - CLASS - STYLE - COLOR 255 100 100 - OUTLINECOLOR 181 181 181 - END - END - TEMPLATE 'void' + CONNECTIONTYPE PLUGIN + PLUGIN "C:\projects\mapserver\build\Release\msplugin_mssql2008.dll" + CONNECTION "SERVER=(local)\SQL2017;DATABASE=msautotest;uid=sa;pwd=Password12!;" + DATA "ogr_geometry from (select * from bdry_counpy2) as foo USING UNIQUE ogr_fid USING SRID=26915" + STATUS OFF + TYPE POLYGON + CLASS + STYLE + COLOR 255 100 100 + OUTLINECOLOR 181 181 181 + END + END + TEMPLATE 'void' diff --git a/msautotest/mssql/include/cities_mssql.map b/msautotest/mssql/include/cities_mssql.map index 9f53a270b3..9582749413 100644 --- a/msautotest/mssql/include/cities_mssql.map +++ b/msautotest/mssql/include/cities_mssql.map @@ -1,6 +1,6 @@ - CONNECTIONTYPE PLUGIN - PLUGIN "C:\projects\mapserver\build\Release\msplugin_mssql2008.dll" - CONNECTION "SERVER=(local)\SQL2017;DATABASE=msautotest;uid=sa;pwd=Password12!;" - STATUS OFF - TYPE POINT - + CONNECTIONTYPE PLUGIN + PLUGIN "C:\projects\mapserver\build\Release\msplugin_mssql2008.dll" + CONNECTION "SERVER=(local)\SQL2017;DATABASE=msautotest;uid=sa;pwd=Password12!;" + STATUS OFF + TYPE POINT + diff --git a/renderers/agg/include/agg_conv_clipper.h b/renderers/agg/include/agg_conv_clipper.h index 4095766d23..fcc0f101d3 100644 --- a/renderers/agg/include/agg_conv_clipper.h +++ b/renderers/agg/include/agg_conv_clipper.h @@ -1,299 +1,299 @@ -/******************************************************************************* -* * -* Author : Angus Johnson * -* Version : 1.1 * -* Date : 4 April 2011 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2011 * -* * -* License: * -* Use, modification & distribution is subject to Boost Software License Ver 1. * -* http://www.boost.org/LICENSE_1_0.txt * -* * -*******************************************************************************/ - -#ifndef AGG_CONV_CLIPPER_INCLUDED -#define AGG_CONV_CLIPPER_INCLUDED - -#if defined(_MSC_VER) && (_MSC_VER >= 1800) -#include -#endif - -#include -#include "agg_basics.h" -#include "agg_array.h" -#include "clipper.hpp" - -namespace mapserver -{ - enum clipper_op_e { clipper_or, - clipper_and, clipper_xor, clipper_a_minus_b, clipper_b_minus_a }; - enum clipper_PolyFillType {clipper_even_odd, clipper_non_zero, clipper_positive, clipper_negative}; - - template class conv_clipper - { - enum status { status_move_to, status_line_to, status_stop }; - typedef VSA source_a_type; - typedef VSB source_b_type; - typedef conv_clipper self_type; - - private: - source_a_type* m_src_a; - source_b_type* m_src_b; - status m_status; - int m_vertex; - int m_contour; - int m_scaling_factor; - clipper_op_e m_operation; - pod_bvector m_vertex_accumulator; - ClipperLib::Polygons m_poly_a; - ClipperLib::Polygons m_poly_b; - ClipperLib::Polygons m_result; - ClipperLib::Clipper m_clipper; - clipper_PolyFillType m_subjFillType; - clipper_PolyFillType m_clipFillType; - - int Round(double val) - { - if ((val < 0)) return (int)(val - 0.5); else return (int)(val + 0.5); - } - - public: - conv_clipper(source_a_type &a, source_b_type &b, - clipper_op_e op = clipper_or, - clipper_PolyFillType subjFillType = clipper_even_odd, - clipper_PolyFillType clipFillType = clipper_even_odd, - int scaling_factor = 2) : - m_src_a(&a), - m_src_b(&b), - m_status(status_move_to), - m_vertex(-1), - m_contour(-1), - m_operation(op), - m_subjFillType(subjFillType), - m_clipFillType(clipFillType) - { - m_scaling_factor = std::max(std::min(scaling_factor, 6),0); - m_scaling_factor = Round(std::pow((double)10, m_scaling_factor)); - } - - ~conv_clipper() - { - } - - void attach1(VSA &source, clipper_PolyFillType subjFillType = clipper_even_odd) - { m_src_a = &source; m_subjFillType = subjFillType; } - void attach2(VSB &source, clipper_PolyFillType clipFillType = clipper_even_odd) - { m_src_b = &source; m_clipFillType = clipFillType; } - - void operation(clipper_op_e v) { m_operation = v; } - - void rewind(unsigned path_id); - unsigned vertex(double* x, double* y); - - bool next_contour(); - bool next_vertex(double* x, double* y); - void start_extracting(); - void add_vertex_(double &x, double &y); - void end_contour(ClipperLib::Polygons &p); - - template void add(VS &src, ClipperLib::Polygons &p){ - unsigned cmd; - double x; double y; double start_x; double start_y; - bool starting_first_line; - - start_x = 0.0; - start_y = 0.0; - starting_first_line = true; - p.resize(0); - - cmd = src->vertex( &x , &y ); - while(!is_stop(cmd)) - { - if(is_vertex(cmd)) - { - if(is_move_to(cmd)) - { - if(!starting_first_line ) end_contour(p); - start_x = x; - start_y = y; - } - add_vertex_( x, y ); - starting_first_line = false; - } - else if(is_end_poly(cmd)) - { - if(!starting_first_line && is_closed(cmd)) - add_vertex_( start_x, start_y ); - } - cmd = src->vertex( &x, &y ); - } - end_contour(p); - } - }; - - //------------------------------------------------------------------------ - - template - void conv_clipper::start_extracting() - { - m_status = status_move_to; - m_contour = -1; - m_vertex = -1; - } - //------------------------------------------------------------------------------ - - template - void conv_clipper::rewind(unsigned path_id) - { - m_src_a->rewind( path_id ); - m_src_b->rewind( path_id ); - - add( m_src_a , m_poly_a ); - add( m_src_b , m_poly_b ); - m_result.resize(0); - - ClipperLib::PolyFillType pftSubj, pftClip; - switch (m_subjFillType) - { - case clipper_even_odd: pftSubj = ClipperLib::pftEvenOdd; break; - case clipper_non_zero: pftSubj = ClipperLib::pftNonZero; break; - case clipper_positive: pftSubj = ClipperLib::pftPositive; break; - default: pftSubj = ClipperLib::pftNegative; - } - switch (m_clipFillType) - { - case clipper_even_odd: pftClip = ClipperLib::pftEvenOdd; break; - case clipper_non_zero: pftClip = ClipperLib::pftNonZero; break; - case clipper_positive: pftClip = ClipperLib::pftPositive; break; - default: pftClip = ClipperLib::pftNegative; - } - - m_clipper.Clear(); - switch( m_operation ) { - case clipper_or: - { - m_clipper.AddPolygons( m_poly_a , ClipperLib::ptSubject ); - m_clipper.AddPolygons( m_poly_b , ClipperLib::ptClip ); - m_clipper.Execute( ClipperLib::ctUnion , m_result , pftSubj, pftClip); - break; - } - case clipper_and: - { - m_clipper.AddPolygons( m_poly_a , ClipperLib::ptSubject ); - m_clipper.AddPolygons( m_poly_b , ClipperLib::ptClip ); - m_clipper.Execute( ClipperLib::ctIntersection , m_result, pftSubj, pftClip ); - break; - } - case clipper_xor: - { - m_clipper.AddPolygons( m_poly_a , ClipperLib::ptSubject ); - m_clipper.AddPolygons( m_poly_b , ClipperLib::ptClip ); - m_clipper.Execute( ClipperLib::ctXor , m_result, pftSubj, pftClip ); - break; - } - case clipper_a_minus_b: - { - m_clipper.AddPolygons( m_poly_a , ClipperLib::ptSubject ); - m_clipper.AddPolygons( m_poly_b , ClipperLib::ptClip ); - m_clipper.Execute( ClipperLib::ctDifference , m_result, pftSubj, pftClip ); - break; - } - case clipper_b_minus_a: - { - m_clipper.AddPolygons( m_poly_b , ClipperLib::ptSubject ); - m_clipper.AddPolygons( m_poly_a , ClipperLib::ptClip ); - m_clipper.Execute( ClipperLib::ctDifference , m_result, pftSubj, pftClip ); - break; - } - } - start_extracting(); - } - //------------------------------------------------------------------------------ - - template - void conv_clipper::end_contour( ClipperLib::Polygons &p) - { - unsigned i, len; - - if( m_vertex_accumulator.size() < 3 ) return; - len = p.size(); - p.resize(len+1); - p[len].resize(m_vertex_accumulator.size()); - for( i = 0 ; i < m_vertex_accumulator.size() ; i++ ) - p[len][i] = m_vertex_accumulator[i]; - m_vertex_accumulator.remove_all(); - } - //------------------------------------------------------------------------------ - - template - void conv_clipper::add_vertex_(double &x, double &y) - { - ClipperLib::IntPoint v; - - v.X = Round(x * m_scaling_factor); - v.Y = Round(y * m_scaling_factor); - m_vertex_accumulator.add( v ); - } - //------------------------------------------------------------------------------ - - template - bool conv_clipper::next_contour() - { - m_contour++; - if(m_contour >= (int)m_result.size()) return false; - m_vertex =-1; - return true; -} -//------------------------------------------------------------------------------ - - template - bool conv_clipper::next_vertex(double *x, double *y) - { - m_vertex++; - if(m_vertex >= (int)m_result[m_contour].size()) return false; - *x = (double)m_result[ m_contour ][ m_vertex ].X / m_scaling_factor; - *y = (double)m_result[ m_contour ][ m_vertex ].Y / m_scaling_factor; - return true; - } - //------------------------------------------------------------------------------ - - template - unsigned conv_clipper::vertex(double *x, double *y) -{ - if( m_status == status_move_to ) - { - if( next_contour() ) - { - if( next_vertex( x, y ) ) - { - m_status =status_line_to; - return path_cmd_move_to; - } - else - { - m_status = status_stop; - return path_cmd_end_poly | path_flags_close; - } - } - else - return path_cmd_stop; - } - else - { - if( next_vertex( x, y ) ) - { - return path_cmd_line_to; - } - else - { - m_status = status_move_to; - return path_cmd_end_poly | path_flags_close; - } - } -} -//------------------------------------------------------------------------------ - - -} //namespace agg -#endif //AGG_CONV_CLIPPER_INCLUDED +/******************************************************************************* +* * +* Author : Angus Johnson * +* Version : 1.1 * +* Date : 4 April 2011 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2011 * +* * +* License: * +* Use, modification & distribution is subject to Boost Software License Ver 1. * +* http://www.boost.org/LICENSE_1_0.txt * +* * +*******************************************************************************/ + +#ifndef AGG_CONV_CLIPPER_INCLUDED +#define AGG_CONV_CLIPPER_INCLUDED + +#if defined(_MSC_VER) && (_MSC_VER >= 1800) +#include +#endif + +#include +#include "agg_basics.h" +#include "agg_array.h" +#include "clipper.hpp" + +namespace mapserver +{ + enum clipper_op_e { clipper_or, + clipper_and, clipper_xor, clipper_a_minus_b, clipper_b_minus_a }; + enum clipper_PolyFillType {clipper_even_odd, clipper_non_zero, clipper_positive, clipper_negative}; + + template class conv_clipper + { + enum status { status_move_to, status_line_to, status_stop }; + typedef VSA source_a_type; + typedef VSB source_b_type; + typedef conv_clipper self_type; + + private: + source_a_type* m_src_a; + source_b_type* m_src_b; + status m_status; + int m_vertex; + int m_contour; + int m_scaling_factor; + clipper_op_e m_operation; + pod_bvector m_vertex_accumulator; + ClipperLib::Polygons m_poly_a; + ClipperLib::Polygons m_poly_b; + ClipperLib::Polygons m_result; + ClipperLib::Clipper m_clipper; + clipper_PolyFillType m_subjFillType; + clipper_PolyFillType m_clipFillType; + + int Round(double val) + { + if ((val < 0)) return (int)(val - 0.5); else return (int)(val + 0.5); + } + + public: + conv_clipper(source_a_type &a, source_b_type &b, + clipper_op_e op = clipper_or, + clipper_PolyFillType subjFillType = clipper_even_odd, + clipper_PolyFillType clipFillType = clipper_even_odd, + int scaling_factor = 2) : + m_src_a(&a), + m_src_b(&b), + m_status(status_move_to), + m_vertex(-1), + m_contour(-1), + m_operation(op), + m_subjFillType(subjFillType), + m_clipFillType(clipFillType) + { + m_scaling_factor = std::max(std::min(scaling_factor, 6),0); + m_scaling_factor = Round(std::pow((double)10, m_scaling_factor)); + } + + ~conv_clipper() + { + } + + void attach1(VSA &source, clipper_PolyFillType subjFillType = clipper_even_odd) + { m_src_a = &source; m_subjFillType = subjFillType; } + void attach2(VSB &source, clipper_PolyFillType clipFillType = clipper_even_odd) + { m_src_b = &source; m_clipFillType = clipFillType; } + + void operation(clipper_op_e v) { m_operation = v; } + + void rewind(unsigned path_id); + unsigned vertex(double* x, double* y); + + bool next_contour(); + bool next_vertex(double* x, double* y); + void start_extracting(); + void add_vertex_(double &x, double &y); + void end_contour(ClipperLib::Polygons &p); + + template void add(VS &src, ClipperLib::Polygons &p){ + unsigned cmd; + double x; double y; double start_x; double start_y; + bool starting_first_line; + + start_x = 0.0; + start_y = 0.0; + starting_first_line = true; + p.resize(0); + + cmd = src->vertex( &x , &y ); + while(!is_stop(cmd)) + { + if(is_vertex(cmd)) + { + if(is_move_to(cmd)) + { + if(!starting_first_line ) end_contour(p); + start_x = x; + start_y = y; + } + add_vertex_( x, y ); + starting_first_line = false; + } + else if(is_end_poly(cmd)) + { + if(!starting_first_line && is_closed(cmd)) + add_vertex_( start_x, start_y ); + } + cmd = src->vertex( &x, &y ); + } + end_contour(p); + } + }; + + //------------------------------------------------------------------------ + + template + void conv_clipper::start_extracting() + { + m_status = status_move_to; + m_contour = -1; + m_vertex = -1; + } + //------------------------------------------------------------------------------ + + template + void conv_clipper::rewind(unsigned path_id) + { + m_src_a->rewind( path_id ); + m_src_b->rewind( path_id ); + + add( m_src_a , m_poly_a ); + add( m_src_b , m_poly_b ); + m_result.resize(0); + + ClipperLib::PolyFillType pftSubj, pftClip; + switch (m_subjFillType) + { + case clipper_even_odd: pftSubj = ClipperLib::pftEvenOdd; break; + case clipper_non_zero: pftSubj = ClipperLib::pftNonZero; break; + case clipper_positive: pftSubj = ClipperLib::pftPositive; break; + default: pftSubj = ClipperLib::pftNegative; + } + switch (m_clipFillType) + { + case clipper_even_odd: pftClip = ClipperLib::pftEvenOdd; break; + case clipper_non_zero: pftClip = ClipperLib::pftNonZero; break; + case clipper_positive: pftClip = ClipperLib::pftPositive; break; + default: pftClip = ClipperLib::pftNegative; + } + + m_clipper.Clear(); + switch( m_operation ) { + case clipper_or: + { + m_clipper.AddPolygons( m_poly_a , ClipperLib::ptSubject ); + m_clipper.AddPolygons( m_poly_b , ClipperLib::ptClip ); + m_clipper.Execute( ClipperLib::ctUnion , m_result , pftSubj, pftClip); + break; + } + case clipper_and: + { + m_clipper.AddPolygons( m_poly_a , ClipperLib::ptSubject ); + m_clipper.AddPolygons( m_poly_b , ClipperLib::ptClip ); + m_clipper.Execute( ClipperLib::ctIntersection , m_result, pftSubj, pftClip ); + break; + } + case clipper_xor: + { + m_clipper.AddPolygons( m_poly_a , ClipperLib::ptSubject ); + m_clipper.AddPolygons( m_poly_b , ClipperLib::ptClip ); + m_clipper.Execute( ClipperLib::ctXor , m_result, pftSubj, pftClip ); + break; + } + case clipper_a_minus_b: + { + m_clipper.AddPolygons( m_poly_a , ClipperLib::ptSubject ); + m_clipper.AddPolygons( m_poly_b , ClipperLib::ptClip ); + m_clipper.Execute( ClipperLib::ctDifference , m_result, pftSubj, pftClip ); + break; + } + case clipper_b_minus_a: + { + m_clipper.AddPolygons( m_poly_b , ClipperLib::ptSubject ); + m_clipper.AddPolygons( m_poly_a , ClipperLib::ptClip ); + m_clipper.Execute( ClipperLib::ctDifference , m_result, pftSubj, pftClip ); + break; + } + } + start_extracting(); + } + //------------------------------------------------------------------------------ + + template + void conv_clipper::end_contour( ClipperLib::Polygons &p) + { + unsigned i, len; + + if( m_vertex_accumulator.size() < 3 ) return; + len = p.size(); + p.resize(len+1); + p[len].resize(m_vertex_accumulator.size()); + for( i = 0 ; i < m_vertex_accumulator.size() ; i++ ) + p[len][i] = m_vertex_accumulator[i]; + m_vertex_accumulator.remove_all(); + } + //------------------------------------------------------------------------------ + + template + void conv_clipper::add_vertex_(double &x, double &y) + { + ClipperLib::IntPoint v; + + v.X = Round(x * m_scaling_factor); + v.Y = Round(y * m_scaling_factor); + m_vertex_accumulator.add( v ); + } + //------------------------------------------------------------------------------ + + template + bool conv_clipper::next_contour() + { + m_contour++; + if(m_contour >= (int)m_result.size()) return false; + m_vertex =-1; + return true; +} +//------------------------------------------------------------------------------ + + template + bool conv_clipper::next_vertex(double *x, double *y) + { + m_vertex++; + if(m_vertex >= (int)m_result[m_contour].size()) return false; + *x = (double)m_result[ m_contour ][ m_vertex ].X / m_scaling_factor; + *y = (double)m_result[ m_contour ][ m_vertex ].Y / m_scaling_factor; + return true; + } + //------------------------------------------------------------------------------ + + template + unsigned conv_clipper::vertex(double *x, double *y) +{ + if( m_status == status_move_to ) + { + if( next_contour() ) + { + if( next_vertex( x, y ) ) + { + m_status =status_line_to; + return path_cmd_move_to; + } + else + { + m_status = status_stop; + return path_cmd_end_poly | path_flags_close; + } + } + else + return path_cmd_stop; + } + else + { + if( next_vertex( x, y ) ) + { + return path_cmd_line_to; + } + else + { + m_status = status_move_to; + return path_cmd_end_poly | path_flags_close; + } + } +} +//------------------------------------------------------------------------------ + + +} //namespace agg +#endif //AGG_CONV_CLIPPER_INCLUDED diff --git a/renderers/agg/include/clipper.hpp b/renderers/agg/include/clipper.hpp index a476a2f0d7..76cdc21d7d 100644 --- a/renderers/agg/include/clipper.hpp +++ b/renderers/agg/include/clipper.hpp @@ -1,302 +1,302 @@ -/******************************************************************************* -* * -* Author : Angus Johnson * -* Version : 4.6.3 * -* Date : 11 November 2011 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2011 * -* * -* License: * -* Use, modification & distribution is subject to Boost Software License Ver 1. * -* http://www.boost.org/LICENSE_1_0.txt * -* * -* Attributions: * -* The code in this library is an extension of Bala Vatti's clipping algorithm: * -* "A generic solution to polygon clipping" * -* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * -* http://portal.acm.org/citation.cfm?id=129906 * -* * -* Computer graphics and geometric modeling: implementation and algorithms * -* By Max K. Agoston * -* Springer; 1 edition (January 4, 2005) * -* http://books.google.com/books?q=vatti+clipping+agoston * -* * -* See also: * -* "Polygon Offsetting by Computing Winding Numbers" * -* Paper no. DETC2005-85513 pp. 565-575 * -* ASME 2005 International Design Engineering Technical Conferences * -* and Computers and Information in Engineering Conference (IDETC/CIE2005) * -* September 24-28, 2005 , Long Beach, California, USA * -* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * -* * -*******************************************************************************/ - -#ifndef clipper_hpp -#define clipper_hpp - -#include -#include -#include -#include -#include - -namespace ClipperLib { - -enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor }; -enum PolyType { ptSubject, ptClip }; -//By far the most widely used winding rules for polygon filling are -//EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32) -//Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL) -//see http://glprogramming.com/red/chapter11.html -enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; - -typedef signed long long long64; -typedef unsigned long long ulong64; - -struct IntPoint { -public: - long64 X; - long64 Y; - IntPoint(long64 x = 0, long64 y = 0): X(x), Y(y) {}; - friend std::ostream& operator <<(std::ostream &s, IntPoint &p); -}; - -typedef std::vector< IntPoint > Polygon; -typedef std::vector< Polygon > Polygons; - -std::ostream& operator <<(std::ostream &s, Polygon &p); -std::ostream& operator <<(std::ostream &s, Polygons &p); - -struct ExPolygon { - Polygon outer; - Polygons holes; -}; -typedef std::vector< ExPolygon > ExPolygons; - -enum JoinType { jtSquare, jtMiter, jtRound }; - -bool Orientation(const Polygon &poly); -double Area(const Polygon &poly); -void OffsetPolygons(const Polygons &in_polys, Polygons &out_polys, - double delta, JoinType jointype = jtSquare, double MiterLimit = 2); - -void ReversePoints(Polygon& p); -void ReversePoints(Polygons& p); - -//used internally ... -enum EdgeSide { esLeft, esRight }; -enum IntersectProtects { ipNone = 0, ipLeft = 1, ipRight = 2, ipBoth = 3 }; - -struct TEdge { - long64 xbot; - long64 ybot; - long64 xcurr; - long64 ycurr; - long64 xtop; - long64 ytop; - double dx; - long64 tmpX; - PolyType polyType; - EdgeSide side; - int windDelta; //1 or -1 depending on winding direction - int windCnt; - int windCnt2; //winding count of the opposite polytype - int outIdx; - TEdge *next; - TEdge *prev; - TEdge *nextInLML; - TEdge *nextInAEL; - TEdge *prevInAEL; - TEdge *nextInSEL; - TEdge *prevInSEL; -}; - -struct IntersectNode { - TEdge *edge1; - TEdge *edge2; - IntPoint pt; - IntersectNode *next; -}; - -struct LocalMinima { - long64 Y; - TEdge *leftBound; - TEdge *rightBound; - LocalMinima *next; -}; - -struct Scanbeam { - long64 Y; - Scanbeam *next; -}; - -struct OutPt; //forward declaration - -struct OutRec { - int idx; - bool isHole; - OutRec *FirstLeft; - OutRec *AppendLink; - OutPt *pts; - OutPt *bottomPt; - TEdge *bottomE1; - TEdge *bottomE2; -}; - -struct OutPt { - int idx; - IntPoint pt; - OutPt *next; - OutPt *prev; -}; - -struct JoinRec { - IntPoint pt1a; - IntPoint pt1b; - int poly1Idx; - IntPoint pt2a; - IntPoint pt2b; - int poly2Idx; -}; - -struct HorzJoinRec { - TEdge *edge; - int savedIdx; -}; - -struct IntRect { long64 left; long64 top; long64 right; long64 bottom; }; - -typedef std::vector < OutRec* > PolyOutList; -typedef std::vector < TEdge* > EdgeList; -typedef std::vector < JoinRec* > JoinList; -typedef std::vector < HorzJoinRec* > HorzJoinList; - -//ClipperBase is the ancestor to the Clipper class. It should not be -//instantiated directly. This class simply abstracts the conversion of sets of -//polygon coordinates into edge objects that are stored in a LocalMinima list. -class ClipperBase -{ -public: - ClipperBase(); - virtual ~ClipperBase(); - bool AddPolygon(const Polygon &pg, PolyType polyType); - bool AddPolygons( const Polygons &ppg, PolyType polyType); - virtual void Clear(); - IntRect GetBounds(); -protected: - void DisposeLocalMinimaList(); - TEdge* AddBoundsToLML(TEdge *e); - void PopLocalMinima(); - virtual void Reset(); - void InsertLocalMinima(LocalMinima *newLm); - LocalMinima *m_CurrentLM; - LocalMinima *m_MinimaList; - bool m_UseFullRange; - EdgeList m_edges; -}; - -class Clipper : public virtual ClipperBase -{ -public: - Clipper(); - ~Clipper(); - bool Execute(ClipType clipType, - Polygons &solution, - PolyFillType subjFillType = pftEvenOdd, - PolyFillType clipFillType = pftEvenOdd); - bool Execute(ClipType clipType, - ExPolygons &solution, - PolyFillType subjFillType = pftEvenOdd, - PolyFillType clipFillType = pftEvenOdd); - void Clear(); - bool ReverseSolution() {return m_ReverseOutput;}; - void ReverseSolution(bool value) {m_ReverseOutput = value;}; -protected: - void Reset(); - virtual bool ExecuteInternal(bool fixHoleLinkages); -private: - PolyOutList m_PolyOuts; - JoinList m_Joins; - HorzJoinList m_HorizJoins; - ClipType m_ClipType; - Scanbeam *m_Scanbeam; - TEdge *m_ActiveEdges; - TEdge *m_SortedEdges; - IntersectNode *m_IntersectNodes; - bool m_ExecuteLocked; - PolyFillType m_ClipFillType; - PolyFillType m_SubjFillType; - bool m_ReverseOutput; - void DisposeScanbeamList(); - void SetWindingCount(TEdge& edge); - bool IsEvenOddFillType(const TEdge& edge) const; - bool IsEvenOddAltFillType(const TEdge& edge) const; - void InsertScanbeam(const long64 Y); - long64 PopScanbeam(); - void InsertLocalMinimaIntoAEL(const long64 botY); - void InsertEdgeIntoAEL(TEdge *edge); - void AddEdgeToSEL(TEdge *edge); - void CopyAELToSEL(); - void DeleteFromSEL(TEdge *e); - void DeleteFromAEL(TEdge *e); - void UpdateEdgeIntoAEL(TEdge *&e); - void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2); - bool IsContributing(const TEdge& edge) const; - bool IsTopHorz(const long64 XPos); - void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); - void DoMaxima(TEdge *e, long64 topY); - void ProcessHorizontals(); - void ProcessHorizontal(TEdge *horzEdge); - void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); - void AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); - void AppendPolygon(TEdge *e1, TEdge *e2); - void DoEdge1(TEdge *edge1, TEdge *edge2, const IntPoint &pt); - void DoEdge2(TEdge *edge1, TEdge *edge2, const IntPoint &pt); - void DoBothEdges(TEdge *edge1, TEdge *edge2, const IntPoint &pt); - void IntersectEdges(TEdge *e1, TEdge *e2, - const IntPoint &pt, IntersectProtects protects); - OutRec* CreateOutRec(); - void AddOutPt(TEdge *e, TEdge *altE, const IntPoint &pt); - void DisposeAllPolyPts(); - void DisposeOutRec(PolyOutList::size_type index, bool ignorePts = false); - bool ProcessIntersections(const long64 botY, const long64 topY); - void AddIntersectNode(TEdge *e1, TEdge *e2, const IntPoint &pt); - void BuildIntersectList(const long64 botY, const long64 topY); - void ProcessIntersectList(); - void ProcessEdgesAtTopOfScanbeam(const long64 topY); - void BuildResult(Polygons& polys); - void BuildResultEx(ExPolygons& polys); - void SetHoleState(TEdge *e, OutRec *OutRec); - void DisposeIntersectNodes(); - bool FixupIntersections(); - void FixupOutPolygon(OutRec &outRec); - bool IsHole(TEdge *e); - void FixHoleLinkage(OutRec *outRec); - void CheckHoleLinkages1(OutRec *outRec1, OutRec *outRec2); - void CheckHoleLinkages2(OutRec *outRec1, OutRec *outRec2); - void AddJoin(TEdge *e1, TEdge *e2, int e1OutIdx = -1, int e2OutIdx = -1); - void ClearJoins(); - void AddHorzJoin(TEdge *e, int idx); - void ClearHorzJoins(); - void JoinCommonEdges(bool fixHoleLinkages); -}; - -//------------------------------------------------------------------------------ -//------------------------------------------------------------------------------ - -class clipperException : public std::exception -{ - public: - clipperException(const char* description): m_descr(description) {} - virtual ~clipperException() throw() {} - virtual const char* what() const throw() {return m_descr.c_str();} - private: - std::string m_descr; -}; -//------------------------------------------------------------------------------ - -} //ClipperLib namespace - -#endif //clipper_hpp - - +/******************************************************************************* +* * +* Author : Angus Johnson * +* Version : 4.6.3 * +* Date : 11 November 2011 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2011 * +* * +* License: * +* Use, modification & distribution is subject to Boost Software License Ver 1. * +* http://www.boost.org/LICENSE_1_0.txt * +* * +* Attributions: * +* The code in this library is an extension of Bala Vatti's clipping algorithm: * +* "A generic solution to polygon clipping" * +* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * +* http://portal.acm.org/citation.cfm?id=129906 * +* * +* Computer graphics and geometric modeling: implementation and algorithms * +* By Max K. Agoston * +* Springer; 1 edition (January 4, 2005) * +* http://books.google.com/books?q=vatti+clipping+agoston * +* * +* See also: * +* "Polygon Offsetting by Computing Winding Numbers" * +* Paper no. DETC2005-85513 pp. 565-575 * +* ASME 2005 International Design Engineering Technical Conferences * +* and Computers and Information in Engineering Conference (IDETC/CIE2005) * +* September 24-28, 2005 , Long Beach, California, USA * +* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * +* * +*******************************************************************************/ + +#ifndef clipper_hpp +#define clipper_hpp + +#include +#include +#include +#include +#include + +namespace ClipperLib { + +enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor }; +enum PolyType { ptSubject, ptClip }; +//By far the most widely used winding rules for polygon filling are +//EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32) +//Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL) +//see http://glprogramming.com/red/chapter11.html +enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; + +typedef signed long long long64; +typedef unsigned long long ulong64; + +struct IntPoint { +public: + long64 X; + long64 Y; + IntPoint(long64 x = 0, long64 y = 0): X(x), Y(y) {}; + friend std::ostream& operator <<(std::ostream &s, IntPoint &p); +}; + +typedef std::vector< IntPoint > Polygon; +typedef std::vector< Polygon > Polygons; + +std::ostream& operator <<(std::ostream &s, Polygon &p); +std::ostream& operator <<(std::ostream &s, Polygons &p); + +struct ExPolygon { + Polygon outer; + Polygons holes; +}; +typedef std::vector< ExPolygon > ExPolygons; + +enum JoinType { jtSquare, jtMiter, jtRound }; + +bool Orientation(const Polygon &poly); +double Area(const Polygon &poly); +void OffsetPolygons(const Polygons &in_polys, Polygons &out_polys, + double delta, JoinType jointype = jtSquare, double MiterLimit = 2); + +void ReversePoints(Polygon& p); +void ReversePoints(Polygons& p); + +//used internally ... +enum EdgeSide { esLeft, esRight }; +enum IntersectProtects { ipNone = 0, ipLeft = 1, ipRight = 2, ipBoth = 3 }; + +struct TEdge { + long64 xbot; + long64 ybot; + long64 xcurr; + long64 ycurr; + long64 xtop; + long64 ytop; + double dx; + long64 tmpX; + PolyType polyType; + EdgeSide side; + int windDelta; //1 or -1 depending on winding direction + int windCnt; + int windCnt2; //winding count of the opposite polytype + int outIdx; + TEdge *next; + TEdge *prev; + TEdge *nextInLML; + TEdge *nextInAEL; + TEdge *prevInAEL; + TEdge *nextInSEL; + TEdge *prevInSEL; +}; + +struct IntersectNode { + TEdge *edge1; + TEdge *edge2; + IntPoint pt; + IntersectNode *next; +}; + +struct LocalMinima { + long64 Y; + TEdge *leftBound; + TEdge *rightBound; + LocalMinima *next; +}; + +struct Scanbeam { + long64 Y; + Scanbeam *next; +}; + +struct OutPt; //forward declaration + +struct OutRec { + int idx; + bool isHole; + OutRec *FirstLeft; + OutRec *AppendLink; + OutPt *pts; + OutPt *bottomPt; + TEdge *bottomE1; + TEdge *bottomE2; +}; + +struct OutPt { + int idx; + IntPoint pt; + OutPt *next; + OutPt *prev; +}; + +struct JoinRec { + IntPoint pt1a; + IntPoint pt1b; + int poly1Idx; + IntPoint pt2a; + IntPoint pt2b; + int poly2Idx; +}; + +struct HorzJoinRec { + TEdge *edge; + int savedIdx; +}; + +struct IntRect { long64 left; long64 top; long64 right; long64 bottom; }; + +typedef std::vector < OutRec* > PolyOutList; +typedef std::vector < TEdge* > EdgeList; +typedef std::vector < JoinRec* > JoinList; +typedef std::vector < HorzJoinRec* > HorzJoinList; + +//ClipperBase is the ancestor to the Clipper class. It should not be +//instantiated directly. This class simply abstracts the conversion of sets of +//polygon coordinates into edge objects that are stored in a LocalMinima list. +class ClipperBase +{ +public: + ClipperBase(); + virtual ~ClipperBase(); + bool AddPolygon(const Polygon &pg, PolyType polyType); + bool AddPolygons( const Polygons &ppg, PolyType polyType); + virtual void Clear(); + IntRect GetBounds(); +protected: + void DisposeLocalMinimaList(); + TEdge* AddBoundsToLML(TEdge *e); + void PopLocalMinima(); + virtual void Reset(); + void InsertLocalMinima(LocalMinima *newLm); + LocalMinima *m_CurrentLM; + LocalMinima *m_MinimaList; + bool m_UseFullRange; + EdgeList m_edges; +}; + +class Clipper : public virtual ClipperBase +{ +public: + Clipper(); + ~Clipper(); + bool Execute(ClipType clipType, + Polygons &solution, + PolyFillType subjFillType = pftEvenOdd, + PolyFillType clipFillType = pftEvenOdd); + bool Execute(ClipType clipType, + ExPolygons &solution, + PolyFillType subjFillType = pftEvenOdd, + PolyFillType clipFillType = pftEvenOdd); + void Clear(); + bool ReverseSolution() {return m_ReverseOutput;}; + void ReverseSolution(bool value) {m_ReverseOutput = value;}; +protected: + void Reset(); + virtual bool ExecuteInternal(bool fixHoleLinkages); +private: + PolyOutList m_PolyOuts; + JoinList m_Joins; + HorzJoinList m_HorizJoins; + ClipType m_ClipType; + Scanbeam *m_Scanbeam; + TEdge *m_ActiveEdges; + TEdge *m_SortedEdges; + IntersectNode *m_IntersectNodes; + bool m_ExecuteLocked; + PolyFillType m_ClipFillType; + PolyFillType m_SubjFillType; + bool m_ReverseOutput; + void DisposeScanbeamList(); + void SetWindingCount(TEdge& edge); + bool IsEvenOddFillType(const TEdge& edge) const; + bool IsEvenOddAltFillType(const TEdge& edge) const; + void InsertScanbeam(const long64 Y); + long64 PopScanbeam(); + void InsertLocalMinimaIntoAEL(const long64 botY); + void InsertEdgeIntoAEL(TEdge *edge); + void AddEdgeToSEL(TEdge *edge); + void CopyAELToSEL(); + void DeleteFromSEL(TEdge *e); + void DeleteFromAEL(TEdge *e); + void UpdateEdgeIntoAEL(TEdge *&e); + void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2); + bool IsContributing(const TEdge& edge) const; + bool IsTopHorz(const long64 XPos); + void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); + void DoMaxima(TEdge *e, long64 topY); + void ProcessHorizontals(); + void ProcessHorizontal(TEdge *horzEdge); + void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); + void AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); + void AppendPolygon(TEdge *e1, TEdge *e2); + void DoEdge1(TEdge *edge1, TEdge *edge2, const IntPoint &pt); + void DoEdge2(TEdge *edge1, TEdge *edge2, const IntPoint &pt); + void DoBothEdges(TEdge *edge1, TEdge *edge2, const IntPoint &pt); + void IntersectEdges(TEdge *e1, TEdge *e2, + const IntPoint &pt, IntersectProtects protects); + OutRec* CreateOutRec(); + void AddOutPt(TEdge *e, TEdge *altE, const IntPoint &pt); + void DisposeAllPolyPts(); + void DisposeOutRec(PolyOutList::size_type index, bool ignorePts = false); + bool ProcessIntersections(const long64 botY, const long64 topY); + void AddIntersectNode(TEdge *e1, TEdge *e2, const IntPoint &pt); + void BuildIntersectList(const long64 botY, const long64 topY); + void ProcessIntersectList(); + void ProcessEdgesAtTopOfScanbeam(const long64 topY); + void BuildResult(Polygons& polys); + void BuildResultEx(ExPolygons& polys); + void SetHoleState(TEdge *e, OutRec *OutRec); + void DisposeIntersectNodes(); + bool FixupIntersections(); + void FixupOutPolygon(OutRec &outRec); + bool IsHole(TEdge *e); + void FixHoleLinkage(OutRec *outRec); + void CheckHoleLinkages1(OutRec *outRec1, OutRec *outRec2); + void CheckHoleLinkages2(OutRec *outRec1, OutRec *outRec2); + void AddJoin(TEdge *e1, TEdge *e2, int e1OutIdx = -1, int e2OutIdx = -1); + void ClearJoins(); + void AddHorzJoin(TEdge *e, int idx); + void ClearHorzJoins(); + void JoinCommonEdges(bool fixHoleLinkages); +}; + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +class clipperException : public std::exception +{ + public: + clipperException(const char* description): m_descr(description) {} + virtual ~clipperException() throw() {} + virtual const char* what() const throw() {return m_descr.c_str();} + private: + std::string m_descr; +}; +//------------------------------------------------------------------------------ + +} //ClipperLib namespace + +#endif //clipper_hpp + + diff --git a/renderers/agg/src/clipper.cpp b/renderers/agg/src/clipper.cpp index ec7c15bd29..e6ad1c2260 100644 --- a/renderers/agg/src/clipper.cpp +++ b/renderers/agg/src/clipper.cpp @@ -1,3302 +1,3302 @@ -/******************************************************************************* -* * -* Author : Angus Johnson * -* Version : 4.6.3 * -* Date : 11 November 2011 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2011 * -* * -* License: * -* Use, modification & distribution is subject to Boost Software License Ver 1. * -* http://www.boost.org/LICENSE_1_0.txt * -* * -* Attributions: * -* The code in this library is an extension of Bala Vatti's clipping algorithm: * -* "A generic solution to polygon clipping" * -* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * -* http://portal.acm.org/citation.cfm?id=129906 * -* * -* Computer graphics and geometric modeling: implementation and algorithms * -* By Max K. Agoston * -* Springer; 1 edition (January 4, 2005) * -* http://books.google.com/books?q=vatti+clipping+agoston * -* * -* See also: * -* "Polygon Offsetting by Computing Winding Numbers" * -* Paper no. DETC2005-85513 pp. 565-575 * -* ASME 2005 International Design Engineering Technical Conferences * -* and Computers and Information in Engineering Conference (IDETC/CIE2005) * -* September 24-28, 2005 , Long Beach, California, USA * -* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * -* * -*******************************************************************************/ - -/******************************************************************************* -* * -* This is a translation of the Delphi Clipper library and the naming style * -* used has retained a Delphi flavour. * -* * -*******************************************************************************/ - -#include "../include/clipper.hpp" -#include -#include -#include -#include -#include -#include -#include - -namespace ClipperLib { - -static long64 const loRange = 1518500249; //sqrt(2^63 -1)/2 -static long64 const hiRange = 6521908912666391106LL; //sqrt(2^127 -1)/2 -static double const pi = 3.141592653589793238; -enum Direction { dRightToLeft, dLeftToRight }; -enum RangeTest { rtLo, rtHi, rtError }; - -#define HORIZONTAL (-1.0E+40) -#define TOLERANCE (1.0e-20) -#define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE)) -#define NEAR_EQUAL(a, b) NEAR_ZERO((a) - (b)) - -inline long64 Abs(long64 val) -{ - if (val < 0) return -val; else return val; -} -//------------------------------------------------------------------------------ - -//------------------------------------------------------------------------------ -// Int128 class (enables safe math on signed 64bit integers) -// eg Int128 val1((long64)9223372036854775807); //ie 2^63 -1 -// Int128 val2((long64)9223372036854775807); -// Int128 val3 = val1 * val2; -// val3.AsString => "85070591730234615847396907784232501249" (8.5e+37) -//------------------------------------------------------------------------------ - -class Int128 -{ - public: - - Int128(long64 _lo = 0) - { - hi = 0; - if (_lo < 0) { - lo = -_lo; - Negate(*this); - } else - lo = _lo; - } - - Int128(const Int128 &val): hi(val.hi), lo(val.lo){} - - long64 operator = (const long64 &val) - { - hi = 0; - lo = Abs(val); - if (val < 0) Negate(*this); - return val; - } - - bool operator == (const Int128 &val) const - {return (hi == val.hi && lo == val.lo);} - - bool operator != (const Int128 &val) const { return !(*this == val);} - - bool operator > (const Int128 &val) const - { - if (hi > val.hi) return true; - else if (hi < val.hi) return false; - else return ulong64(lo) > ulong64(val.lo); - } - - bool operator < (const Int128 &val) const - { - if (hi < val.hi) return true; - else if (hi > val.hi) return false; - else return ulong64(lo) < ulong64(val.lo); - } - - Int128& operator += (const Int128 &rhs) - { - hi += rhs.hi; - lo += rhs.lo; - if (ulong64(lo) < ulong64(rhs.lo)) hi++; - return *this; - } - - Int128 operator + (const Int128 &rhs) const - { - Int128 result(*this); - result+= rhs; - return result; - } - - Int128& operator -= (const Int128 &rhs) - { - Int128 tmp(rhs); - Negate(tmp); - *this += tmp; - return *this; - } - - Int128 operator - (const Int128 &rhs) const - { - Int128 result(*this); - result-= rhs; - return result; - } - - Int128 operator * (const Int128 &rhs) const { - if ( !(hi == 0 || hi == -1) || !(rhs.hi == 0 || rhs.hi == -1)) - throw "Int128 operator*: overflow error"; - bool negate = (hi < 0) != (rhs.hi < 0); - - Int128 tmp(*this); - if (tmp.hi < 0) Negate(tmp); - ulong64 int1Hi = ulong64(tmp.lo) >> 32; - ulong64 int1Lo = ulong64(tmp.lo & 0xFFFFFFFF); - - tmp = rhs; - if (tmp.hi < 0) Negate(tmp); - ulong64 int2Hi = ulong64(tmp.lo) >> 32; - ulong64 int2Lo = ulong64(tmp.lo & 0xFFFFFFFF); - - //nb: see comments in clipper.pas - ulong64 a = int1Hi * int2Hi; - ulong64 b = int1Lo * int2Lo; - ulong64 c = int1Hi * int2Lo + int1Lo * int2Hi; - - tmp.hi = long64(a + (c >> 32)); - tmp.lo = long64(c << 32); - tmp.lo += long64(b); - if (ulong64(tmp.lo) < b) tmp.hi++; - if (negate) Negate(tmp); - return tmp; - } - - Int128 operator/ (const Int128 &rhs) const - { - if (rhs.lo == 0 && rhs.hi == 0) - throw "Int128 operator/: divide by zero"; - bool negate = (rhs.hi < 0) != (hi < 0); - Int128 result(*this), denom(rhs); - if (result.hi < 0) Negate(result); - if (denom.hi < 0) Negate(denom); - if (denom > result) return Int128(0); //result is only a fraction of 1 - Negate(denom); - - Int128 p(0); - for (int i = 0; i < 128; ++i) - { - p.hi = p.hi << 1; - if (p.lo < 0) p.hi++; - p.lo = long64(p.lo) << 1; - if (result.hi < 0) p.lo++; - result.hi = result.hi << 1; - if (result.lo < 0) result.hi++; - result.lo = long64(result.lo) << 1; - Int128 p2(p); - p += denom; - if (p.hi < 0) p = p2; - else result.lo++; - } - if (negate) Negate(result); - return result; - } - - double AsDouble() const - { - const double shift64 = 18446744073709551616.0; //2^64 - const double bit64 = 9223372036854775808.0; - if (hi < 0) - { - Int128 tmp(*this); - Negate(tmp); - if (tmp.lo < 0) - return (double)tmp.lo - bit64 - tmp.hi * shift64; - else - return -(double)tmp.lo - tmp.hi * shift64; - } - else if (lo < 0) - return -(double)lo + bit64 + hi * shift64; - else - return (double)lo + (double)hi * shift64; - } - - //for bug testing ... - std::string AsString() const - { - std::string result; - unsigned char r = 0; - Int128 tmp(0), val(*this); - if (hi < 0) Negate(val); - result.resize(50); - std::string::size_type i = result.size() -1; - while (val.hi != 0 || val.lo != 0) - { - Div10(val, tmp, r); - result[i--] = char('0' + r); - val = tmp; - } - if (hi < 0) result[i--] = '-'; - result.erase(0,i+1); - if (result.size() == 0) result = "0"; - return result; - } - -private: - long64 hi; - long64 lo; - - static void Negate(Int128 &val) - { - if (val.lo == 0) - { - if( val.hi == 0) return; - val.lo = ~val.lo; - val.hi = ~val.hi +1; - } - else - { - val.lo = ~val.lo +1; - val.hi = ~val.hi; - } - } - - //debugging only ... - void Div10(const Int128 val, Int128& result, unsigned char & remainder) const - { - remainder = 0; - result = 0; - for (int i = 63; i >= 0; --i) - { - if ((val.hi & ((long64)1 << i)) != 0) - remainder = char((remainder * 2) + 1); else - remainder *= char(2); - if (remainder >= 10) - { - result.hi += ((long64)1 << i); - remainder -= char(10); - } - } - for (int i = 63; i >= 0; --i) - { - if ((val.lo & ((long64)1 << i)) != 0) - remainder = char((remainder * 2) + 1); else - remainder *= char(2); - if (remainder >= 10) - { - result.lo += ((long64)1 << i); - remainder -= char(10); - } - } - } -}; - -//------------------------------------------------------------------------------ -//------------------------------------------------------------------------------ - -RangeTest TestRange(const Polygon &pts) -{ - RangeTest result = rtLo; - for (Polygon::size_type i = 0; i < pts.size(); ++i) - { - if (Abs(pts[i].X) > hiRange || Abs(pts[i].Y) > hiRange) - return rtError; - else if (Abs(pts[i].X) > loRange || Abs(pts[i].Y) > loRange) - result = rtHi; - } - return result; -} -//------------------------------------------------------------------------------ - -bool Orientation(const Polygon &poly) -{ - int highI = (int)poly.size() -1; - if (highI < 2) return false; - bool UseFullInt64Range = false; - - int j = 0, jplus, jminus; - for (int i = 0; i <= highI; ++i) - { - if (Abs(poly[i].X) > hiRange || Abs(poly[i].Y) > hiRange) - throw "Coordinate exceeds range bounds."; - if (Abs(poly[i].X) > loRange || Abs(poly[i].Y) > loRange) - UseFullInt64Range = true; - if (poly[i].Y < poly[j].Y) continue; - if ((poly[i].Y > poly[j].Y || poly[i].X < poly[j].X)) j = i; - }; - if (j == highI) jplus = 0; - else jplus = j +1; - if (j == 0) jminus = highI; - else jminus = j -1; - - IntPoint vec1, vec2; - //get cross product of vectors of the edges adjacent to highest point ... - vec1.X = poly[j].X - poly[jminus].X; - vec1.Y = poly[j].Y - poly[jminus].Y; - vec2.X = poly[jplus].X - poly[j].X; - vec2.Y = poly[jplus].Y - poly[j].Y; - - if (UseFullInt64Range) - { - Int128 cross = Int128(vec1.X) * Int128(vec2.Y) - - Int128(vec2.X) * Int128(vec1.Y); - return cross > 0; - } - else - { - return (vec1.X * vec2.Y - vec2.X * vec1.Y) > 0; - } -} -//------------------------------------------------------------------------------ - -bool Orientation(OutRec *outRec, bool UseFullInt64Range) -{ - OutPt *opBottom = outRec->pts, *op = outRec->pts->next; - while (op != outRec->pts) - { - if (op->pt.Y >= opBottom->pt.Y) - { - if (op->pt.Y > opBottom->pt.Y || op->pt.X < opBottom->pt.X) - opBottom = op; - } - op = op->next; - } - - IntPoint vec1, vec2; - vec1.X = op->pt.X - op->prev->pt.X; - vec1.Y = op->pt.Y - op->prev->pt.Y; - vec2.X = op->next->pt.X - op->pt.X; - vec2.Y = op->next->pt.Y - op->pt.Y; - - if (UseFullInt64Range) - { - Int128 cross = Int128(vec1.X) * Int128(vec2.Y) - Int128(vec2.X) * Int128(vec1.Y); - return cross > 0; - } - else - { - return (vec1.X * vec2.Y - vec2.X * vec1.Y) > 0; - } -} -//------------------------------------------------------------------------------ - -inline bool PointsEqual( const IntPoint &pt1, const IntPoint &pt2) -{ - return ( pt1.X == pt2.X && pt1.Y == pt2.Y ); -} -//------------------------------------------------------------------------------ - -double Area(const Polygon &poly) -{ - int highI = (int)poly.size() -1; - if (highI < 2) return 0; - bool UseFullInt64Range; - RangeTest rt = TestRange(poly); - switch (rt) { - case rtLo: - UseFullInt64Range = false; - break; - case rtHi: - UseFullInt64Range = true; - break; - default: - throw "Coordinate exceeds range bounds."; - } - - if (UseFullInt64Range) { - Int128 a(0); - a = (Int128(poly[highI].X) * Int128(poly[0].Y)) - - Int128(poly[0].X) * Int128(poly[highI].Y); - for (int i = 0; i < highI; ++i) - a += Int128(poly[i].X) * Int128(poly[i+1].Y) - - Int128(poly[i+1].X) * Int128(poly[i].Y); - return a.AsDouble() / 2; - } - else - { - double a; - a = (double)poly[highI].X * poly[0].Y - (double)poly[0].X * poly[highI].Y; - for (int i = 0; i < highI; ++i) - a += (double)poly[i].X * poly[i+1].Y - (double)poly[i+1].X * poly[i].Y; - return a/2; - } -} -//------------------------------------------------------------------------------ - -bool PointIsVertex(const IntPoint &pt, OutPt *pp) -{ - OutPt *pp2 = pp; - do - { - if (PointsEqual(pp2->pt, pt)) return true; - pp2 = pp2->next; - } - while (pp2 != pp); - return false; -} -//------------------------------------------------------------------------------ - -bool PointInPolygon(const IntPoint &pt, OutPt *pp, bool UseFullInt64Range) -{ - OutPt *pp2 = pp; - bool result = false; - if (UseFullInt64Range) { - do - { - if ((((pp2->pt.Y <= pt.Y) && (pt.Y < pp2->prev->pt.Y)) || - ((pp2->prev->pt.Y <= pt.Y) && (pt.Y < pp2->pt.Y))) && - Int128(pt.X - pp2->pt.X) < (Int128(pp2->prev->pt.X - pp2->pt.X) * - Int128(pt.Y - pp2->pt.Y)) / Int128(pp2->prev->pt.Y - pp2->pt.Y)) - result = !result; - pp2 = pp2->next; - } - while (pp2 != pp); - } - else - { - do - { - if ((((pp2->pt.Y <= pt.Y) && (pt.Y < pp2->prev->pt.Y)) || - ((pp2->prev->pt.Y <= pt.Y) && (pt.Y < pp2->pt.Y))) && - (pt.X < (pp2->prev->pt.X - pp2->pt.X) * (pt.Y - pp2->pt.Y) / - (pp2->prev->pt.Y - pp2->pt.Y) + pp2->pt.X )) result = !result; - pp2 = pp2->next; - } - while (pp2 != pp); - } - return result; -} -//------------------------------------------------------------------------------ - -bool SlopesEqual(TEdge &e1, TEdge &e2, bool UseFullInt64Range) -{ - if (e1.ybot == e1.ytop) return (e2.ybot == e2.ytop); - else if (e1.xbot == e1.xtop) return (e2.xbot == e2.xtop); - else if (UseFullInt64Range) - return Int128(e1.ytop - e1.ybot) * Int128(e2.xtop - e2.xbot) == - Int128(e1.xtop - e1.xbot) * Int128(e2.ytop - e2.ybot); - else return (e1.ytop - e1.ybot)*(e2.xtop - e2.xbot) == - (e1.xtop - e1.xbot)*(e2.ytop - e2.ybot); -} -//------------------------------------------------------------------------------ - -bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, - const IntPoint pt3, bool UseFullInt64Range) -{ - if (pt1.Y == pt2.Y) return (pt2.Y == pt3.Y); - else if (pt1.X == pt2.X) return (pt2.X == pt3.X); - else if (UseFullInt64Range) - return Int128(pt1.Y-pt2.Y) * Int128(pt2.X-pt3.X) == - Int128(pt1.X-pt2.X) * Int128(pt2.Y-pt3.Y); - else return (pt1.Y-pt2.Y)*(pt2.X-pt3.X) == (pt1.X-pt2.X)*(pt2.Y-pt3.Y); -} -//------------------------------------------------------------------------------ - -bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, - const IntPoint pt3, const IntPoint pt4, bool UseFullInt64Range) -{ - if (pt1.Y == pt2.Y) return (pt3.Y == pt4.Y); - else if (pt1.X == pt2.X) return (pt3.X == pt4.X); - else if (UseFullInt64Range) - return Int128(pt1.Y-pt2.Y) * Int128(pt3.X-pt4.X) == - Int128(pt1.X-pt2.X) * Int128(pt3.Y-pt4.Y); - else return (pt1.Y-pt2.Y)*(pt3.X-pt4.X) == (pt1.X-pt2.X)*(pt3.Y-pt4.Y); -} -//------------------------------------------------------------------------------ - -double GetDx(const IntPoint pt1, const IntPoint pt2) -{ - if (pt1.Y == pt2.Y) return HORIZONTAL; - else return - (double)(pt2.X - pt1.X) / (double)(pt2.Y - pt1.Y); -} -//--------------------------------------------------------------------------- - -void SetDx(TEdge &e) -{ - if (e.ybot == e.ytop) e.dx = HORIZONTAL; - else e.dx = - (double)(e.xtop - e.xbot) / (double)(e.ytop - e.ybot); -} -//--------------------------------------------------------------------------- - -void SwapSides(TEdge &edge1, TEdge &edge2) -{ - EdgeSide side = edge1.side; - edge1.side = edge2.side; - edge2.side = side; -} -//------------------------------------------------------------------------------ - -void SwapPolyIndexes(TEdge &edge1, TEdge &edge2) -{ - int outIdx = edge1.outIdx; - edge1.outIdx = edge2.outIdx; - edge2.outIdx = outIdx; -} -//------------------------------------------------------------------------------ - -inline long64 Round(double val) -{ - if ((val < 0)) return static_cast(val - 0.5); - else return static_cast(val + 0.5); -} -//------------------------------------------------------------------------------ - -long64 TopX(TEdge &edge, const long64 currentY) -{ - if( currentY == edge.ytop ) return edge.xtop; - return edge.xbot + Round(edge.dx *(currentY - edge.ybot)); -} -//------------------------------------------------------------------------------ - -long64 TopX(const IntPoint pt1, const IntPoint pt2, const long64 currentY) -{ - //preconditions: pt1.Y <> pt2.Y and pt1.Y > pt2.Y - if (currentY >= pt1.Y) return pt1.X; - else if (currentY == pt2.Y) return pt2.X; - else if (pt1.X == pt2.X) return pt1.X; - else - { - double q = (double)(pt1.X-pt2.X)/(double)(pt1.Y-pt2.Y); - return Round(pt1.X + (currentY - pt1.Y) *q); - } -} -//------------------------------------------------------------------------------ - -bool IntersectPoint(TEdge &edge1, TEdge &edge2, - IntPoint &ip, bool UseFullInt64Range) -{ - double b1, b2; - if (SlopesEqual(edge1, edge2, UseFullInt64Range)) return false; - else if (NEAR_ZERO(edge1.dx)) - { - ip.X = edge1.xbot; - if (NEAR_EQUAL(edge2.dx, HORIZONTAL)) - { - ip.Y = edge2.ybot; - } else - { - b2 = edge2.ybot - (edge2.xbot/edge2.dx); - ip.Y = Round(ip.X/edge2.dx + b2); - } - } - else if (NEAR_ZERO(edge2.dx)) - { - ip.X = edge2.xbot; - if (NEAR_EQUAL(edge1.dx, HORIZONTAL)) - { - ip.Y = edge1.ybot; - } else - { - b1 = edge1.ybot - (edge1.xbot/edge1.dx); - ip.Y = Round(ip.X/edge1.dx + b1); - } - } else - { - b1 = edge1.xbot - edge1.ybot * edge1.dx; - b2 = edge2.xbot - edge2.ybot * edge2.dx; - b2 = (b2-b1)/(edge1.dx - edge2.dx); - ip.Y = Round(b2); - ip.X = Round(edge1.dx * b2 + b1); - } - - return - //can be *so close* to the top of one edge that the rounded Y equals one ytop ... - (ip.Y == edge1.ytop && ip.Y >= edge2.ytop && edge1.tmpX > edge2.tmpX) || - (ip.Y == edge2.ytop && ip.Y >= edge1.ytop && edge1.tmpX > edge2.tmpX) || - (ip.Y > edge1.ytop && ip.Y > edge2.ytop); -} -//------------------------------------------------------------------------------ - -void ReversePolyPtLinks(OutPt &pp) -{ - OutPt *pp1, *pp2; - pp1 = &pp; - do { - pp2 = pp1->next; - pp1->next = pp1->prev; - pp1->prev = pp2; - pp1 = pp2; - } while( pp1 != &pp ); -} -//------------------------------------------------------------------------------ - -void DisposeOutPts(OutPt*& pp) -{ - if (pp == 0) return; - pp->prev->next = 0; - while( pp ) - { - OutPt *tmpPp = pp; - pp = pp->next; - delete tmpPp ; - } -} -//------------------------------------------------------------------------------ - -void InitEdge(TEdge *e, TEdge *eNext, - TEdge *ePrev, const IntPoint &pt, PolyType polyType) -{ - std::memset( e, 0, sizeof( TEdge )); - - e->next = eNext; - e->prev = ePrev; - e->xcurr = pt.X; - e->ycurr = pt.Y; - if (e->ycurr >= e->next->ycurr) - { - e->xbot = e->xcurr; - e->ybot = e->ycurr; - e->xtop = e->next->xcurr; - e->ytop = e->next->ycurr; - e->windDelta = 1; - } else - { - e->xtop = e->xcurr; - e->ytop = e->ycurr; - e->xbot = e->next->xcurr; - e->ybot = e->next->ycurr; - e->windDelta = -1; - } - SetDx(*e); - e->polyType = polyType; - e->outIdx = -1; -} -//------------------------------------------------------------------------------ - -inline void SwapX(TEdge &e) -{ - //swap horizontal edges' top and bottom x's so they follow the natural - //progression of the bounds - ie so their xbots will align with the - //adjoining lower edge. [Helpful in the ProcessHorizontal() method.] - e.xcurr = e.xtop; - e.xtop = e.xbot; - e.xbot = e.xcurr; -} -//------------------------------------------------------------------------------ - -void SwapPoints(IntPoint &pt1, IntPoint &pt2) -{ - IntPoint tmp = pt1; - pt1 = pt2; - pt2 = tmp; -} -//------------------------------------------------------------------------------ - -bool GetOverlapSegment(IntPoint pt1a, IntPoint pt1b, IntPoint pt2a, - IntPoint pt2b, IntPoint &pt1, IntPoint &pt2) -{ - //precondition: segments are colinear. - if ( pt1a.Y == pt1b.Y || Abs((pt1a.X - pt1b.X)/(pt1a.Y - pt1b.Y)) > 1 ) - { - if (pt1a.X > pt1b.X) SwapPoints(pt1a, pt1b); - if (pt2a.X > pt2b.X) SwapPoints(pt2a, pt2b); - if (pt1a.X > pt2a.X) pt1 = pt1a; else pt1 = pt2a; - if (pt1b.X < pt2b.X) pt2 = pt1b; else pt2 = pt2b; - return pt1.X < pt2.X; - } else - { - if (pt1a.Y < pt1b.Y) SwapPoints(pt1a, pt1b); - if (pt2a.Y < pt2b.Y) SwapPoints(pt2a, pt2b); - if (pt1a.Y < pt2a.Y) pt1 = pt1a; else pt1 = pt2a; - if (pt1b.Y > pt2b.Y) pt2 = pt1b; else pt2 = pt2b; - return pt1.Y > pt2.Y; - } -} -//------------------------------------------------------------------------------ - -OutPt* PolygonBottom(OutPt* pp) -{ - OutPt* p = pp->next; - OutPt* result = pp; - while (p != pp) - { - if (p->pt.Y > result->pt.Y) result = p; - else if (p->pt.Y == result->pt.Y && p->pt.X < result->pt.X) result = p; - p = p->next; - } - return result; -} -//------------------------------------------------------------------------------ - -bool FindSegment(OutPt* &pp, IntPoint &pt1, IntPoint &pt2) -{ - //outPt1 & outPt2 => the overlap segment (if the function returns true) - if (!pp) return false; - OutPt* pp2 = pp; - IntPoint pt1a = pt1, pt2a = pt2; - do - { - if (SlopesEqual(pt1a, pt2a, pp->pt, pp->prev->pt, true) && - SlopesEqual(pt1a, pt2a, pp->pt, true) && - GetOverlapSegment(pt1a, pt2a, pp->pt, pp->prev->pt, pt1, pt2)) - return true; - pp = pp->next; - } - while (pp != pp2); - return false; -} -//------------------------------------------------------------------------------ - -bool Pt3IsBetweenPt1AndPt2(const IntPoint pt1, - const IntPoint pt2, const IntPoint pt3) -{ - if (PointsEqual(pt1, pt3) || PointsEqual(pt2, pt3)) return true; - else if (pt1.X != pt2.X) return (pt1.X < pt3.X) == (pt3.X < pt2.X); - else return (pt1.Y < pt3.Y) == (pt3.Y < pt2.Y); -} -//------------------------------------------------------------------------------ - -OutPt* InsertPolyPtBetween(OutPt* p1, OutPt* p2, const IntPoint pt) -{ - if (p1 == p2) throw "JoinError"; - OutPt* result = new OutPt; - result->pt = pt; - if (p2 == p1->next) - { - p1->next = result; - p2->prev = result; - result->next = p2; - result->prev = p1; - } else - { - p2->next = result; - p1->prev = result; - result->next = p1; - result->prev = p2; - } - return result; -} - -//------------------------------------------------------------------------------ -// ClipperBase class methods ... -//------------------------------------------------------------------------------ - -ClipperBase::ClipperBase() //constructor -{ - m_MinimaList = 0; - m_CurrentLM = 0; - m_UseFullRange = true; -} -//------------------------------------------------------------------------------ - -ClipperBase::~ClipperBase() //destructor -{ - Clear(); -} -//------------------------------------------------------------------------------ - -bool ClipperBase::AddPolygon( const Polygon &pg, PolyType polyType) -{ - int len = (int)pg.size(); - if (len < 3) return false; - Polygon p(len); - p[0] = pg[0]; - int j = 0; - - long64 maxVal; - if (m_UseFullRange) maxVal = hiRange; else maxVal = loRange; - - for (int i = 0; i < len; ++i) - { - if (Abs(pg[i].X) > maxVal || Abs(pg[i].Y) > maxVal) - { - if (m_UseFullRange) - throw "Coordinate exceeds range bounds"; - maxVal = hiRange; - if (Abs(pg[i].X) > maxVal || Abs(pg[i].Y) > maxVal) - throw "Coordinate exceeds range bounds"; - m_UseFullRange = true; - } - - if (i == 0 || PointsEqual(p[j], pg[i])) continue; - else if (j > 0 && SlopesEqual(p[j-1], p[j], pg[i], m_UseFullRange)) - { - if (PointsEqual(p[j-1], pg[i])) j--; - } else j++; - p[j] = pg[i]; - } - if (j < 2) return false; - - len = j+1; - for (;;) - { - //nb: test for point equality before testing slopes ... - if (PointsEqual(p[j], p[0])) j--; - else if (PointsEqual(p[0], p[1]) || - SlopesEqual(p[j], p[0], p[1], m_UseFullRange)) - p[0] = p[j--]; - else if (SlopesEqual(p[j-1], p[j], p[0], m_UseFullRange)) j--; - else if (SlopesEqual(p[0], p[1], p[2], m_UseFullRange)) - { - for (int i = 2; i <= j; ++i) p[i-1] = p[i]; - j--; - } - //exit loop if nothing is changed or there are too few vertices ... - if (j == len-1 || j < 2) break; - len = j +1; - } - if (len < 3) return false; - - //create a new edge array ... - TEdge *edges = new TEdge [len]; - m_edges.push_back(edges); - - //convert vertices to a double-linked-list of edges and initialize ... - edges[0].xcurr = p[0].X; - edges[0].ycurr = p[0].Y; - InitEdge(&edges[len-1], &edges[0], &edges[len-2], p[len-1], polyType); - for (int i = len-2; i > 0; --i) - InitEdge(&edges[i], &edges[i+1], &edges[i-1], p[i], polyType); - InitEdge(&edges[0], &edges[1], &edges[len-1], p[0], polyType); - - //reset xcurr & ycurr and find 'eHighest' (given the Y axis coordinates - //increase downward so the 'highest' edge will have the smallest ytop) ... - TEdge *e = &edges[0]; - TEdge *eHighest = e; - do - { - e->xcurr = e->xbot; - e->ycurr = e->ybot; - if (e->ytop < eHighest->ytop) eHighest = e; - e = e->next; - } - while ( e != &edges[0]); - - //make sure eHighest is positioned so the following loop works safely ... - if (eHighest->windDelta > 0) eHighest = eHighest->next; - if (NEAR_EQUAL(eHighest->dx, HORIZONTAL)) eHighest = eHighest->next; - - //finally insert each local minima ... - e = eHighest; - do { - e = AddBoundsToLML(e); - } - while( e != eHighest ); - return true; -} -//------------------------------------------------------------------------------ - -void ClipperBase::InsertLocalMinima(LocalMinima *newLm) -{ - if( ! m_MinimaList ) - { - m_MinimaList = newLm; - } - else if( newLm->Y >= m_MinimaList->Y ) - { - newLm->next = m_MinimaList; - m_MinimaList = newLm; - } else - { - LocalMinima* tmpLm = m_MinimaList; - while( tmpLm->next && ( newLm->Y < tmpLm->next->Y ) ) - tmpLm = tmpLm->next; - newLm->next = tmpLm->next; - tmpLm->next = newLm; - } -} -//------------------------------------------------------------------------------ - -TEdge* ClipperBase::AddBoundsToLML(TEdge *e) -{ - //Starting at the top of one bound we progress to the bottom where there's - //a local minima. We then go to the top of the next bound. These two bounds - //form the left and right (or right and left) bounds of the local minima. - e->nextInLML = 0; - e = e->next; - for (;;) - { - if (NEAR_EQUAL(e->dx, HORIZONTAL)) - { - //nb: proceed through horizontals when approaching from their right, - // but break on horizontal minima if approaching from their left. - // This ensures 'local minima' are always on the left of horizontals. - if (e->next->ytop < e->ytop && e->next->xbot > e->prev->xbot) break; - if (e->xtop != e->prev->xbot) SwapX(*e); - e->nextInLML = e->prev; - } - else if (e->ycurr == e->prev->ycurr) break; - else e->nextInLML = e->prev; - e = e->next; - } - - //e and e.prev are now at a local minima ... - LocalMinima* newLm = new LocalMinima; - newLm->next = 0; - newLm->Y = e->prev->ybot; - - if ( NEAR_EQUAL(e->dx, HORIZONTAL) ) //horizontal edges never start a left bound - { - if (e->xbot != e->prev->xbot) SwapX(*e); - newLm->leftBound = e->prev; - newLm->rightBound = e; - } else if (e->dx < e->prev->dx) - { - newLm->leftBound = e->prev; - newLm->rightBound = e; - } else - { - newLm->leftBound = e; - newLm->rightBound = e->prev; - } - newLm->leftBound->side = esLeft; - newLm->rightBound->side = esRight; - InsertLocalMinima( newLm ); - - for (;;) - { - if ( e->next->ytop == e->ytop && !NEAR_EQUAL(e->next->dx, HORIZONTAL) ) break; - e->nextInLML = e->next; - e = e->next; - if ( NEAR_EQUAL(e->dx, HORIZONTAL) && e->xbot != e->prev->xtop) SwapX(*e); - } - return e->next; -} -//------------------------------------------------------------------------------ - -bool ClipperBase::AddPolygons(const Polygons &ppg, PolyType polyType) -{ - bool result = true; - for (Polygons::size_type i = 0; i < ppg.size(); ++i) - if (AddPolygon(ppg[i], polyType)) result = false; - return result; -} -//------------------------------------------------------------------------------ - -void ClipperBase::Clear() -{ - DisposeLocalMinimaList(); - for (EdgeList::size_type i = 0; i < m_edges.size(); ++i) delete [] m_edges[i]; - m_edges.clear(); - m_UseFullRange = false; -} -//------------------------------------------------------------------------------ - -void ClipperBase::Reset() -{ - m_CurrentLM = m_MinimaList; - if( !m_CurrentLM ) return; //ie nothing to process - - //reset all edges ... - LocalMinima* lm = m_MinimaList; - while( lm ) - { - TEdge* e = lm->leftBound; - while( e ) - { - e->xcurr = e->xbot; - e->ycurr = e->ybot; - e->side = esLeft; - e->outIdx = -1; - e = e->nextInLML; - } - e = lm->rightBound; - while( e ) - { - e->xcurr = e->xbot; - e->ycurr = e->ybot; - e->side = esRight; - e->outIdx = -1; - e = e->nextInLML; - } - lm = lm->next; - } -} -//------------------------------------------------------------------------------ - -void ClipperBase::DisposeLocalMinimaList() -{ - while( m_MinimaList ) - { - LocalMinima* tmpLm = m_MinimaList->next; - delete m_MinimaList; - m_MinimaList = tmpLm; - } - m_CurrentLM = 0; -} -//------------------------------------------------------------------------------ - -void ClipperBase::PopLocalMinima() -{ - if( ! m_CurrentLM ) return; - m_CurrentLM = m_CurrentLM->next; -} -//------------------------------------------------------------------------------ - -IntRect ClipperBase::GetBounds() -{ - IntRect result; - LocalMinima* lm = m_MinimaList; - if (!lm) - { - result.left = result.top = result.right = result.bottom = 0; - return result; - } - result.left = lm->leftBound->xbot; - result.top = lm->leftBound->ybot; - result.right = lm->leftBound->xbot; - result.bottom = lm->leftBound->ybot; - while (lm) - { - if (lm->leftBound->ybot > result.bottom) - result.bottom = lm->leftBound->ybot; - TEdge* e = lm->leftBound; - for (;;) { - TEdge* bottomE = e; - while (e->nextInLML) - { - if (e->xbot < result.left) result.left = e->xbot; - if (e->xbot > result.right) result.right = e->xbot; - e = e->nextInLML; - } - if (e->xbot < result.left) result.left = e->xbot; - if (e->xbot > result.right) result.right = e->xbot; - if (e->xtop < result.left) result.left = e->xtop; - if (e->xtop > result.right) result.right = e->xtop; - if (e->ytop < result.top) result.top = e->ytop; - - if (bottomE == lm->leftBound) e = lm->rightBound; - else break; - } - lm = lm->next; - } - return result; -} - - -//------------------------------------------------------------------------------ -// TClipper methods ... -//------------------------------------------------------------------------------ - -Clipper::Clipper() : ClipperBase() //constructor -{ - m_Scanbeam = 0; - m_ActiveEdges = 0; - m_SortedEdges = 0; - m_IntersectNodes = 0; - m_ExecuteLocked = false; - m_UseFullRange = false; - m_ReverseOutput = false; -} -//------------------------------------------------------------------------------ - -Clipper::~Clipper() //destructor -{ - Clear(); - DisposeScanbeamList(); -} -//------------------------------------------------------------------------------ - -void Clipper::Clear() -{ - if (m_edges.size() == 0) return; //avoids problems with ClipperBase destructor - DisposeAllPolyPts(); - ClipperBase::Clear(); -} -//------------------------------------------------------------------------------ - -void Clipper::DisposeScanbeamList() -{ - while ( m_Scanbeam ) { - Scanbeam* sb2 = m_Scanbeam->next; - delete m_Scanbeam; - m_Scanbeam = sb2; - } -} -//------------------------------------------------------------------------------ - -void Clipper::Reset() -{ - ClipperBase::Reset(); - m_Scanbeam = 0; - m_ActiveEdges = 0; - m_SortedEdges = 0; - LocalMinima* lm = m_MinimaList; - while (lm) - { - InsertScanbeam(lm->Y); - InsertScanbeam(lm->leftBound->ytop); - lm = lm->next; - } -} -//------------------------------------------------------------------------------ - -bool Clipper::Execute(ClipType clipType, Polygons &solution, - PolyFillType subjFillType, PolyFillType clipFillType) -{ - if( m_ExecuteLocked ) return false; - m_ExecuteLocked = true; - solution.resize(0); - m_SubjFillType = subjFillType; - m_ClipFillType = clipFillType; - m_ClipType = clipType; - bool succeeded = ExecuteInternal(false); - if (succeeded) BuildResult(solution); - m_ExecuteLocked = false; - return succeeded; -} -//------------------------------------------------------------------------------ - -bool Clipper::Execute(ClipType clipType, ExPolygons &solution, - PolyFillType subjFillType, PolyFillType clipFillType) -{ - if( m_ExecuteLocked ) return false; - m_ExecuteLocked = true; - solution.resize(0); - m_SubjFillType = subjFillType; - m_ClipFillType = clipFillType; - m_ClipType = clipType; - bool succeeded = ExecuteInternal(true); - if (succeeded) BuildResultEx(solution); - m_ExecuteLocked = false; - return succeeded; -} -//------------------------------------------------------------------------------ - -bool PolySort(OutRec *or1, OutRec *or2) -{ - if (or1 == or2) return false; - if (!or1->pts || !or2->pts) - { - if (or1->pts != or2->pts) - { - if (or1->pts) return true; else return false; - } - else return false; - } - int i1, i2; - if (or1->isHole) - i1 = or1->FirstLeft->idx; else - i1 = or1->idx; - if (or2->isHole) - i2 = or2->FirstLeft->idx; else - i2 = or2->idx; - int result = i1 - i2; - if (result == 0 && (or1->isHole != or2->isHole)) - { - if (or1->isHole) return false; - else return true; - } - else return result < 0; -} -//------------------------------------------------------------------------------ - -OutRec* FindAppendLinkEnd(OutRec *outRec) -{ - while (outRec->AppendLink) outRec = outRec->AppendLink; - return outRec; -} -//------------------------------------------------------------------------------ - -void Clipper::FixHoleLinkage(OutRec *outRec) -{ - OutRec *tmp; - if (outRec->bottomPt) - tmp = m_PolyOuts[outRec->bottomPt->idx]->FirstLeft; - else - tmp = outRec->FirstLeft; - if (outRec == tmp) throw clipperException("HoleLinkage error"); - - if (tmp) - { - if (tmp->AppendLink) tmp = FindAppendLinkEnd(tmp); - if (tmp == outRec) tmp = 0; - else if (tmp->isHole) - { - FixHoleLinkage(tmp); - tmp = tmp->FirstLeft; - } - } - outRec->FirstLeft = tmp; - if (!tmp) outRec->isHole = false; - outRec->AppendLink = 0; -} -//------------------------------------------------------------------------------ - -bool Clipper::ExecuteInternal(bool fixHoleLinkages) -{ - bool succeeded; - try { - Reset(); - if (!m_CurrentLM ) return true; - long64 botY = PopScanbeam(); - do { - InsertLocalMinimaIntoAEL(botY); - ClearHorzJoins(); - ProcessHorizontals(); - long64 topY = PopScanbeam(); - succeeded = ProcessIntersections(botY, topY); - if (!succeeded) break; - ProcessEdgesAtTopOfScanbeam(topY); - botY = topY; - } while( m_Scanbeam ); - } - catch(...) { - succeeded = false; - } - - if (succeeded) - { - //tidy up output polygons and fix orientations where necessary ... - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) - { - OutRec *outRec = m_PolyOuts[i]; - if (!outRec->pts) continue; - FixupOutPolygon(*outRec); - if (!outRec->pts) continue; - if (outRec->isHole && fixHoleLinkages) FixHoleLinkage(outRec); - if (outRec->isHole == - (m_ReverseOutput ^ Orientation(outRec, m_UseFullRange))) - ReversePolyPtLinks(*outRec->pts); - } - - JoinCommonEdges(fixHoleLinkages); - if (fixHoleLinkages) - std::sort(m_PolyOuts.begin(), m_PolyOuts.end(), PolySort); - } - - ClearJoins(); - ClearHorzJoins(); - return succeeded; -} -//------------------------------------------------------------------------------ - -void Clipper::InsertScanbeam(const long64 Y) -{ - if( !m_Scanbeam ) - { - m_Scanbeam = new Scanbeam; - m_Scanbeam->next = 0; - m_Scanbeam->Y = Y; - } - else if( Y > m_Scanbeam->Y ) - { - Scanbeam* newSb = new Scanbeam; - newSb->Y = Y; - newSb->next = m_Scanbeam; - m_Scanbeam = newSb; - } else - { - Scanbeam* sb2 = m_Scanbeam; - while( sb2->next && ( Y <= sb2->next->Y ) ) sb2 = sb2->next; - if( Y == sb2->Y ) return; //ie ignores duplicates - Scanbeam* newSb = new Scanbeam; - newSb->Y = Y; - newSb->next = sb2->next; - sb2->next = newSb; - } -} -//------------------------------------------------------------------------------ - -long64 Clipper::PopScanbeam() -{ - long64 Y = m_Scanbeam->Y; - Scanbeam* sb2 = m_Scanbeam; - m_Scanbeam = m_Scanbeam->next; - delete sb2; - return Y; -} -//------------------------------------------------------------------------------ - -void Clipper::DisposeAllPolyPts(){ - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) - DisposeOutRec(i); - m_PolyOuts.clear(); -} -//------------------------------------------------------------------------------ - -void Clipper::DisposeOutRec(PolyOutList::size_type index, bool ignorePts) -{ - OutRec *outRec = m_PolyOuts[index]; - if (!ignorePts && outRec->pts) DisposeOutPts(outRec->pts); - delete outRec; - m_PolyOuts[index] = 0; -} -//------------------------------------------------------------------------------ - -void Clipper::SetWindingCount(TEdge &edge) -{ - TEdge *e = edge.prevInAEL; - //find the edge of the same polytype that immediately preceeds 'edge' in AEL - while ( e && e->polyType != edge.polyType ) e = e->prevInAEL; - if ( !e ) - { - edge.windCnt = edge.windDelta; - edge.windCnt2 = 0; - e = m_ActiveEdges; //ie get ready to calc windCnt2 - } else if ( IsEvenOddFillType(edge) ) - { - //EvenOdd filling ... - edge.windCnt = 1; - edge.windCnt2 = e->windCnt2; - e = e->nextInAEL; //ie get ready to calc windCnt2 - } else - { - //nonZero, Positive or Negative filling ... - if ( e->windCnt * e->windDelta < 0 ) - { - if (Abs(e->windCnt) > 1) - { - if (e->windDelta * edge.windDelta < 0) edge.windCnt = e->windCnt; - else edge.windCnt = e->windCnt + edge.windDelta; - } else - edge.windCnt = e->windCnt + e->windDelta + edge.windDelta; - } else - { - if ( Abs(e->windCnt) > 1 && e->windDelta * edge.windDelta < 0) - edge.windCnt = e->windCnt; - else if ( e->windCnt + edge.windDelta == 0 ) - edge.windCnt = e->windCnt; - else edge.windCnt = e->windCnt + edge.windDelta; - } - edge.windCnt2 = e->windCnt2; - e = e->nextInAEL; //ie get ready to calc windCnt2 - } - - //update windCnt2 ... - if ( IsEvenOddAltFillType(edge) ) - { - //EvenOdd filling ... - while ( e != &edge ) - { - edge.windCnt2 = (edge.windCnt2 == 0) ? 1 : 0; - e = e->nextInAEL; - } - } else - { - //nonZero, Positive or Negative filling ... - while ( e != &edge ) - { - edge.windCnt2 += e->windDelta; - e = e->nextInAEL; - } - } -} -//------------------------------------------------------------------------------ - -bool Clipper::IsEvenOddFillType(const TEdge& edge) const -{ - if (edge.polyType == ptSubject) - return m_SubjFillType == pftEvenOdd; else - return m_ClipFillType == pftEvenOdd; -} -//------------------------------------------------------------------------------ - -bool Clipper::IsEvenOddAltFillType(const TEdge& edge) const -{ - if (edge.polyType == ptSubject) - return m_ClipFillType == pftEvenOdd; else - return m_SubjFillType == pftEvenOdd; -} -//------------------------------------------------------------------------------ - -bool Clipper::IsContributing(const TEdge& edge) const -{ - PolyFillType pft, pft2; - if (edge.polyType == ptSubject) - { - pft = m_SubjFillType; - pft2 = m_ClipFillType; - } else - { - pft = m_ClipFillType; - pft2 = m_SubjFillType; - } - - switch(pft) - { - case pftEvenOdd: - case pftNonZero: - if (Abs(edge.windCnt) != 1) return false; - break; - case pftPositive: - if (edge.windCnt != 1) return false; - break; - default: //pftNegative - if (edge.windCnt != -1) return false; - } - - switch(m_ClipType) - { - case ctIntersection: - switch(pft2) - { - case pftEvenOdd: - case pftNonZero: - return (edge.windCnt2 != 0); - case pftPositive: - return (edge.windCnt2 > 0); - default: - return (edge.windCnt2 < 0); - } - case ctUnion: - switch(pft2) - { - case pftEvenOdd: - case pftNonZero: - return (edge.windCnt2 == 0); - case pftPositive: - return (edge.windCnt2 <= 0); - default: - return (edge.windCnt2 >= 0); - } - case ctDifference: - if (edge.polyType == ptSubject) - switch(pft2) - { - case pftEvenOdd: - case pftNonZero: - return (edge.windCnt2 == 0); - case pftPositive: - return (edge.windCnt2 <= 0); - default: - return (edge.windCnt2 >= 0); - } - else - switch(pft2) - { - case pftEvenOdd: - case pftNonZero: - return (edge.windCnt2 != 0); - case pftPositive: - return (edge.windCnt2 > 0); - default: - return (edge.windCnt2 < 0); - } - default: - return true; - } -} -//------------------------------------------------------------------------------ - -void Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt) -{ - if( NEAR_EQUAL(e2->dx, HORIZONTAL) || ( e1->dx > e2->dx ) ) - { - AddOutPt( e1, e2, pt ); - e2->outIdx = e1->outIdx; - e1->side = esLeft; - e2->side = esRight; - } else - { - AddOutPt( e2, e1, pt ); - e1->outIdx = e2->outIdx; - e1->side = esRight; - e2->side = esLeft; - } -} -//------------------------------------------------------------------------------ - -void Clipper::AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt) -{ - AddOutPt( e1, 0, pt ); - if( e1->outIdx == e2->outIdx ) - { - e1->outIdx = -1; - e2->outIdx = -1; - } - else - AppendPolygon( e1, e2 ); -} -//------------------------------------------------------------------------------ - -void Clipper::AddEdgeToSEL(TEdge *edge) -{ - //SEL pointers in PEdge are reused to build a list of horizontal edges. - //However, we don't need to worry about order with horizontal edge processing. - if( !m_SortedEdges ) - { - m_SortedEdges = edge; - edge->prevInSEL = 0; - edge->nextInSEL = 0; - } - else - { - edge->nextInSEL = m_SortedEdges; - edge->prevInSEL = 0; - m_SortedEdges->prevInSEL = edge; - m_SortedEdges = edge; - } -} -//------------------------------------------------------------------------------ - -void Clipper::CopyAELToSEL() -{ - TEdge* e = m_ActiveEdges; - m_SortedEdges = e; - if (!m_ActiveEdges) return; - m_SortedEdges->prevInSEL = 0; - e = e->nextInAEL; - while ( e ) - { - e->prevInSEL = e->prevInAEL; - e->prevInSEL->nextInSEL = e; - e->nextInSEL = 0; - e = e->nextInAEL; - } -} -//------------------------------------------------------------------------------ - -void Clipper::AddJoin(TEdge *e1, TEdge *e2, int e1OutIdx, int e2OutIdx) -{ - JoinRec* jr = new JoinRec; - if (e1OutIdx >= 0) - jr->poly1Idx = e1OutIdx; else - jr->poly1Idx = e1->outIdx; - jr->pt1a = IntPoint(e1->xcurr, e1->ycurr); - jr->pt1b = IntPoint(e1->xtop, e1->ytop); - if (e2OutIdx >= 0) - jr->poly2Idx = e2OutIdx; else - jr->poly2Idx = e2->outIdx; - jr->pt2a = IntPoint(e2->xcurr, e2->ycurr); - jr->pt2b = IntPoint(e2->xtop, e2->ytop); - m_Joins.push_back(jr); -} -//------------------------------------------------------------------------------ - -void Clipper::ClearJoins() -{ - for (JoinList::size_type i = 0; i < m_Joins.size(); i++) - delete m_Joins[i]; - m_Joins.resize(0); -} -//------------------------------------------------------------------------------ - -void Clipper::AddHorzJoin(TEdge *e, int idx) -{ - HorzJoinRec* hj = new HorzJoinRec; - hj->edge = e; - hj->savedIdx = idx; - m_HorizJoins.push_back(hj); -} -//------------------------------------------------------------------------------ - -void Clipper::ClearHorzJoins() -{ - for (HorzJoinList::size_type i = 0; i < m_HorizJoins.size(); i++) - delete m_HorizJoins[i]; - m_HorizJoins.resize(0); -} -//------------------------------------------------------------------------------ - -void Clipper::InsertLocalMinimaIntoAEL( const long64 botY) -{ - while( m_CurrentLM && ( m_CurrentLM->Y == botY ) ) - { - TEdge* lb = m_CurrentLM->leftBound; - TEdge* rb = m_CurrentLM->rightBound; - - InsertEdgeIntoAEL( lb ); - InsertScanbeam( lb->ytop ); - InsertEdgeIntoAEL( rb ); - - if (IsEvenOddFillType(*lb)) - { - lb->windDelta = 1; - rb->windDelta = 1; - } - else - { - rb->windDelta = -lb->windDelta; - } - SetWindingCount( *lb ); - rb->windCnt = lb->windCnt; - rb->windCnt2 = lb->windCnt2; - - if( NEAR_EQUAL(rb->dx, HORIZONTAL) ) - { - //nb: only rightbounds can have a horizontal bottom edge - AddEdgeToSEL( rb ); - InsertScanbeam( rb->nextInLML->ytop ); - } - else - InsertScanbeam( rb->ytop ); - - if( IsContributing(*lb) ) - AddLocalMinPoly( lb, rb, IntPoint(lb->xcurr, m_CurrentLM->Y) ); - - //if output polygons share an edge, they'll need joining later ... - if (lb->outIdx >= 0 && lb->prevInAEL && - lb->prevInAEL->outIdx >= 0 && lb->prevInAEL->xcurr == lb->xbot && - SlopesEqual(*lb, *lb->prevInAEL, m_UseFullRange)) - AddJoin(lb, lb->prevInAEL); - - //if any output polygons share an edge, they'll need joining later ... - if (rb->outIdx >= 0) - { - if (NEAR_EQUAL(rb->dx, HORIZONTAL)) - { - for (HorzJoinList::size_type i = 0; i < m_HorizJoins.size(); ++i) - { - IntPoint pt, pt2; //returned by GetOverlapSegment() but unused here. - HorzJoinRec* hj = m_HorizJoins[i]; - //if horizontals rb and hj.edge overlap, flag for joining later ... - if (GetOverlapSegment(IntPoint(hj->edge->xbot, hj->edge->ybot), - IntPoint(hj->edge->xtop, hj->edge->ytop), - IntPoint(rb->xbot, rb->ybot), - IntPoint(rb->xtop, rb->ytop), pt, pt2)) - AddJoin(hj->edge, rb, hj->savedIdx); - } - } - } - - if( lb->nextInAEL != rb ) - { - if (rb->outIdx >= 0 && rb->prevInAEL->outIdx >= 0 && - SlopesEqual(*rb->prevInAEL, *rb, m_UseFullRange)) - AddJoin(rb, rb->prevInAEL); - - TEdge* e = lb->nextInAEL; - IntPoint pt = IntPoint(lb->xcurr, lb->ycurr); - while( e != rb ) - { - if(!e) throw clipperException("InsertLocalMinimaIntoAEL: missing rightbound!"); - //nb: For calculating winding counts etc, IntersectEdges() assumes - //that param1 will be to the right of param2 ABOVE the intersection ... - IntersectEdges( rb , e , pt , ipNone); //order important here - e = e->nextInAEL; - } - } - PopLocalMinima(); - } -} -//------------------------------------------------------------------------------ - -void Clipper::DeleteFromAEL(TEdge *e) -{ - TEdge* AelPrev = e->prevInAEL; - TEdge* AelNext = e->nextInAEL; - if( !AelPrev && !AelNext && (e != m_ActiveEdges) ) return; //already deleted - if( AelPrev ) AelPrev->nextInAEL = AelNext; - else m_ActiveEdges = AelNext; - if( AelNext ) AelNext->prevInAEL = AelPrev; - e->nextInAEL = 0; - e->prevInAEL = 0; -} -//------------------------------------------------------------------------------ - -void Clipper::DeleteFromSEL(TEdge *e) -{ - TEdge* SelPrev = e->prevInSEL; - TEdge* SelNext = e->nextInSEL; - if( !SelPrev && !SelNext && (e != m_SortedEdges) ) return; //already deleted - if( SelPrev ) SelPrev->nextInSEL = SelNext; - else m_SortedEdges = SelNext; - if( SelNext ) SelNext->prevInSEL = SelPrev; - e->nextInSEL = 0; - e->prevInSEL = 0; -} -//------------------------------------------------------------------------------ - -void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, - const IntPoint &pt, IntersectProtects protects) -{ - //e1 will be to the left of e2 BELOW the intersection. Therefore e1 is before - //e2 in AEL except when e1 is being inserted at the intersection point ... - bool e1stops = !(ipLeft & protects) && !e1->nextInLML && - e1->xtop == pt.X && e1->ytop == pt.Y; - bool e2stops = !(ipRight & protects) && !e2->nextInLML && - e2->xtop == pt.X && e2->ytop == pt.Y; - bool e1Contributing = ( e1->outIdx >= 0 ); - bool e2contributing = ( e2->outIdx >= 0 ); - - //update winding counts... - //assumes that e1 will be to the right of e2 ABOVE the intersection - if ( e1->polyType == e2->polyType ) - { - if ( IsEvenOddFillType( *e1) ) - { - int oldE1WindCnt = e1->windCnt; - e1->windCnt = e2->windCnt; - e2->windCnt = oldE1WindCnt; - } else - { - if (e1->windCnt + e2->windDelta == 0 ) e1->windCnt = -e1->windCnt; - else e1->windCnt += e2->windDelta; - if ( e2->windCnt - e1->windDelta == 0 ) e2->windCnt = -e2->windCnt; - else e2->windCnt -= e1->windDelta; - } - } else - { - if (!IsEvenOddFillType(*e2)) e1->windCnt2 += e2->windDelta; - else e1->windCnt2 = ( e1->windCnt2 == 0 ) ? 1 : 0; - if (!IsEvenOddFillType(*e1)) e2->windCnt2 -= e1->windDelta; - else e2->windCnt2 = ( e2->windCnt2 == 0 ) ? 1 : 0; - } - - PolyFillType e1FillType, e2FillType, e1FillType2, e2FillType2; - if (e1->polyType == ptSubject) - { - e1FillType = m_SubjFillType; - e1FillType2 = m_ClipFillType; - } else - { - e1FillType = m_ClipFillType; - e1FillType2 = m_SubjFillType; - } - if (e2->polyType == ptSubject) - { - e2FillType = m_SubjFillType; - e2FillType2 = m_ClipFillType; - } else - { - e2FillType = m_ClipFillType; - e2FillType2 = m_SubjFillType; - } - - long64 e1Wc, e2Wc; - switch (e1FillType) - { - case pftPositive: e1Wc = e1->windCnt; break; - case pftNegative: e1Wc = -e1->windCnt; break; - default: e1Wc = Abs(e1->windCnt); - } - switch(e2FillType) - { - case pftPositive: e2Wc = e2->windCnt; break; - case pftNegative: e2Wc = -e2->windCnt; break; - default: e2Wc = Abs(e2->windCnt); - } - - if ( e1Contributing && e2contributing ) - { - if ( e1stops || e2stops || - (e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || - (e1->polyType != e2->polyType && m_ClipType != ctXor) ) - AddLocalMaxPoly(e1, e2, pt); - else - DoBothEdges( e1, e2, pt ); - } - else if ( e1Contributing ) - { - if ((e2Wc == 0 || e2Wc == 1) && - (m_ClipType != ctIntersection || - e2->polyType == ptSubject || (e2->windCnt2 != 0))) - DoEdge1(e1, e2, pt); - } - else if ( e2contributing ) - { - if ((e1Wc == 0 || e1Wc == 1) && - (m_ClipType != ctIntersection || - e1->polyType == ptSubject || (e1->windCnt2 != 0))) - DoEdge2(e1, e2, pt); - } - else if ( (e1Wc == 0 || e1Wc == 1) && - (e2Wc == 0 || e2Wc == 1) && !e1stops && !e2stops ) - { - //neither edge is currently contributing ... - - long64 e1Wc2, e2Wc2; - switch (e1FillType2) - { - case pftPositive: e1Wc2 = e1->windCnt2; break; - case pftNegative : e1Wc2 = -e1->windCnt2; break; - default: e1Wc2 = Abs(e1->windCnt2); - } - switch (e2FillType2) - { - case pftPositive: e2Wc2 = e2->windCnt2; break; - case pftNegative: e2Wc2 = -e2->windCnt2; break; - default: e2Wc2 = Abs(e2->windCnt2); - } - - if (e1->polyType != e2->polyType) - AddLocalMinPoly(e1, e2, pt); - else if (e1Wc == 1 && e2Wc == 1) - switch( m_ClipType ) { - case ctIntersection: - if (e1Wc2 > 0 && e2Wc2 > 0) - AddLocalMinPoly(e1, e2, pt); - break; - case ctUnion: - if ( e1Wc2 <= 0 && e2Wc2 <= 0 ) - AddLocalMinPoly(e1, e2, pt); - break; - case ctDifference: - if ((e1->polyType == ptClip && e2->polyType == ptClip && - e1Wc2 > 0 && e2Wc2 > 0) || - (e1->polyType == ptSubject && e2->polyType == ptSubject && - e1Wc2 <= 0 && e2Wc2 <= 0)) - AddLocalMinPoly(e1, e2, pt); - break; - case ctXor: - AddLocalMinPoly(e1, e2, pt); - } - else - SwapSides( *e1, *e2 ); - } - - if( (e1stops != e2stops) && - ( (e1stops && (e1->outIdx >= 0)) || (e2stops && (e2->outIdx >= 0)) ) ) - { - SwapSides( *e1, *e2 ); - SwapPolyIndexes( *e1, *e2 ); - } - - //finally, delete any non-contributing maxima edges ... - if( e1stops ) DeleteFromAEL( e1 ); - if( e2stops ) DeleteFromAEL( e2 ); -} -//------------------------------------------------------------------------------ - -void Clipper::SetHoleState(TEdge *e, OutRec *outRec) -{ - bool isHole = false; - TEdge *e2 = e->prevInAEL; - while (e2) - { - if (e2->outIdx >= 0) - { - isHole = !isHole; - if (! outRec->FirstLeft) - outRec->FirstLeft = m_PolyOuts[e2->outIdx]; - } - e2 = e2->prevInAEL; - } - if (isHole) outRec->isHole = true; -} -//------------------------------------------------------------------------------ - -bool GetNextNonDupOutPt(OutPt* pp, OutPt*& next) -{ - next = pp->next; - while (next != pp && PointsEqual(pp->pt, next->pt)) - next = next->next; - return next != pp; -} -//------------------------------------------------------------------------------ - -bool GetPrevNonDupOutPt(OutPt* pp, OutPt*& prev) -{ - prev = pp->prev; - while (prev != pp && PointsEqual(pp->pt, prev->pt)) - prev = prev->prev; - return prev != pp; -} -//------------------------------------------------------------------------------ - -OutRec* GetLowermostRec(OutRec *outRec1, OutRec *outRec2) -{ - //work out which polygon fragment has the correct hole state ... - OutPt *outPt1 = outRec1->bottomPt; - OutPt *outPt2 = outRec2->bottomPt; - if (outPt1->pt.Y > outPt2->pt.Y) return outRec1; - else if (outPt1->pt.Y < outPt2->pt.Y) return outRec2; - else if (outPt1->pt.X < outPt2->pt.X) return outRec1; - else if (outPt1->pt.X > outPt2->pt.X) return outRec2; - else if (outRec1->bottomE2 == 0) return outRec2; - else if (outRec2->bottomE2 == 0) return outRec1; - else - { - long64 y1 = std::max(outRec1->bottomE1->ybot, outRec1->bottomE2->ybot); - long64 y2 = std::max(outRec2->bottomE1->ybot, outRec2->bottomE2->ybot); - if (y2 == y1 || (y1 > outPt1->pt.Y && y2 > outPt1->pt.Y)) - { - double dx1 = std::max(outRec1->bottomE1->dx, outRec1->bottomE2->dx); - double dx2 = std::max(outRec2->bottomE1->dx, outRec2->bottomE2->dx); - if (dx2 > dx1) return outRec2; else return outRec1; - } - else if (y2 > y1) return outRec2; - else return outRec1; - } -} -//------------------------------------------------------------------------------ - -void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) -{ - //get the start and ends of both output polygons ... - OutRec *outRec1 = m_PolyOuts[e1->outIdx]; - OutRec *outRec2 = m_PolyOuts[e2->outIdx]; - OutRec *holeStateRec = GetLowermostRec(outRec1, outRec2); - - //fixup hole status ... - if (holeStateRec == outRec2) - outRec1->isHole = outRec2->isHole; - else - outRec2->isHole = outRec1->isHole; - - OutPt* p1_lft = outRec1->pts; - OutPt* p1_rt = p1_lft->prev; - OutPt* p2_lft = outRec2->pts; - OutPt* p2_rt = p2_lft->prev; - - EdgeSide side; - //join e2 poly onto e1 poly and delete pointers to e2 ... - if( e1->side == esLeft ) - { - if( e2->side == esLeft ) - { - //z y x a b c - ReversePolyPtLinks(*p2_lft); - p2_lft->next = p1_lft; - p1_lft->prev = p2_lft; - p1_rt->next = p2_rt; - p2_rt->prev = p1_rt; - outRec1->pts = p2_rt; - } else - { - //x y z a b c - p2_rt->next = p1_lft; - p1_lft->prev = p2_rt; - p2_lft->prev = p1_rt; - p1_rt->next = p2_lft; - outRec1->pts = p2_lft; - } - side = esLeft; - } else - { - if( e2->side == esRight ) - { - //a b c z y x - ReversePolyPtLinks( *p2_lft ); - p1_rt->next = p2_rt; - p2_rt->prev = p1_rt; - p2_lft->next = p1_lft; - p1_lft->prev = p2_lft; - } else - { - //a b c x y z - p1_rt->next = p2_lft; - p2_lft->prev = p1_rt; - p1_lft->prev = p2_rt; - p2_rt->next = p1_lft; - } - side = esRight; - } - - if (holeStateRec == outRec2) - { - outRec1->bottomPt = outRec2->bottomPt; - outRec1->bottomPt->idx = outRec1->idx; - outRec1->bottomE1 = outRec2->bottomE1; - outRec1->bottomE2 = outRec2->bottomE2; - - if (outRec2->FirstLeft != outRec1) - outRec1->FirstLeft = outRec2->FirstLeft; - } - outRec2->pts = 0; - outRec2->bottomPt = 0; - outRec2->AppendLink = outRec1; - int OKIdx = e1->outIdx; - int ObsoleteIdx = e2->outIdx; - - e1->outIdx = -1; //nb: safe because we only get here via AddLocalMaxPoly - e2->outIdx = -1; - - TEdge* e = m_ActiveEdges; - while( e ) - { - if( e->outIdx == ObsoleteIdx ) - { - e->outIdx = OKIdx; - e->side = side; - break; - } - e = e->nextInAEL; - } - - for (JoinList::size_type i = 0; i < m_Joins.size(); ++i) - { - if (m_Joins[i]->poly1Idx == ObsoleteIdx) m_Joins[i]->poly1Idx = OKIdx; - if (m_Joins[i]->poly2Idx == ObsoleteIdx) m_Joins[i]->poly2Idx = OKIdx; - } - - for (HorzJoinList::size_type i = 0; i < m_HorizJoins.size(); ++i) - { - if (m_HorizJoins[i]->savedIdx == ObsoleteIdx) - m_HorizJoins[i]->savedIdx = OKIdx; - } - -} -//------------------------------------------------------------------------------ - -OutRec* Clipper::CreateOutRec() -{ - OutRec* result = new OutRec; - result->isHole = false; - result->FirstLeft = 0; - result->AppendLink = 0; - result->pts = 0; - result->bottomPt = 0; - return result; -} -//------------------------------------------------------------------------------ - -void Clipper::AddOutPt(TEdge *e, TEdge *altE, const IntPoint &pt) -{ - bool ToFront = (e->side == esLeft); - if( e->outIdx < 0 ) - { - OutRec *outRec = CreateOutRec(); - m_PolyOuts.push_back(outRec); - outRec->idx = (int)m_PolyOuts.size()-1; - e->outIdx = outRec->idx; - OutPt* op = new OutPt; - outRec->pts = op; - outRec->bottomE1 = e; - outRec->bottomE2 = altE; - outRec->bottomPt = op; - op->pt = pt; - op->idx = outRec->idx; - op->next = op; - op->prev = op; - SetHoleState(e, outRec); - } else - { - OutRec *outRec = m_PolyOuts[e->outIdx]; - OutPt* op = outRec->pts; - if ((ToFront && PointsEqual(pt, op->pt)) || - (!ToFront && PointsEqual(pt, op->prev->pt))) return; - OutPt* op2 = new OutPt; - op2->pt = pt; - op2->idx = outRec->idx; - if (op2->pt.Y == outRec->bottomPt->pt.Y && - op2->pt.X < outRec->bottomPt->pt.X) - { - outRec->bottomPt = op2; - outRec->bottomE1 = e; - outRec->bottomE2 = altE; - } - op2->next = op; - op2->prev = op->prev; - op2->prev->next = op2; - op->prev = op2; - if (ToFront) outRec->pts = op2; - } -} -//------------------------------------------------------------------------------ - -void Clipper::ProcessHorizontals() -{ - TEdge* horzEdge = m_SortedEdges; - while( horzEdge ) - { - DeleteFromSEL( horzEdge ); - ProcessHorizontal( horzEdge ); - horzEdge = m_SortedEdges; - } -} -//------------------------------------------------------------------------------ - -bool Clipper::IsTopHorz(const long64 XPos) -{ - TEdge* e = m_SortedEdges; - while( e ) - { - if( ( XPos >= std::min(e->xcurr, e->xtop) ) && - ( XPos <= std::max(e->xcurr, e->xtop) ) ) return false; - e = e->nextInSEL; - } - return true; -} -//------------------------------------------------------------------------------ - -bool IsMinima(TEdge *e) -{ - return e && (e->prev->nextInLML != e) && (e->next->nextInLML != e); -} -//------------------------------------------------------------------------------ - -bool IsMaxima(TEdge *e, const long64 Y) -{ - return e && e->ytop == Y && !e->nextInLML; -} -//------------------------------------------------------------------------------ - -bool IsIntermediate(TEdge *e, const long64 Y) -{ - return e->ytop == Y && e->nextInLML; -} -//------------------------------------------------------------------------------ - -TEdge *GetMaximaPair(TEdge *e) -{ - if( !IsMaxima(e->next, e->ytop) || e->next->xtop != e->xtop ) - return e->prev; else - return e->next; -} -//------------------------------------------------------------------------------ - -void Clipper::SwapPositionsInAEL(TEdge *edge1, TEdge *edge2) -{ - if( !edge1->nextInAEL && !edge1->prevInAEL ) return; - if( !edge2->nextInAEL && !edge2->prevInAEL ) return; - - if( edge1->nextInAEL == edge2 ) - { - TEdge* next = edge2->nextInAEL; - if( next ) next->prevInAEL = edge1; - TEdge* prev = edge1->prevInAEL; - if( prev ) prev->nextInAEL = edge2; - edge2->prevInAEL = prev; - edge2->nextInAEL = edge1; - edge1->prevInAEL = edge2; - edge1->nextInAEL = next; - } - else if( edge2->nextInAEL == edge1 ) - { - TEdge* next = edge1->nextInAEL; - if( next ) next->prevInAEL = edge2; - TEdge* prev = edge2->prevInAEL; - if( prev ) prev->nextInAEL = edge1; - edge1->prevInAEL = prev; - edge1->nextInAEL = edge2; - edge2->prevInAEL = edge1; - edge2->nextInAEL = next; - } - else - { - TEdge* next = edge1->nextInAEL; - TEdge* prev = edge1->prevInAEL; - edge1->nextInAEL = edge2->nextInAEL; - if( edge1->nextInAEL ) edge1->nextInAEL->prevInAEL = edge1; - edge1->prevInAEL = edge2->prevInAEL; - if( edge1->prevInAEL ) edge1->prevInAEL->nextInAEL = edge1; - edge2->nextInAEL = next; - if( edge2->nextInAEL ) edge2->nextInAEL->prevInAEL = edge2; - edge2->prevInAEL = prev; - if( edge2->prevInAEL ) edge2->prevInAEL->nextInAEL = edge2; - } - - if( !edge1->prevInAEL ) m_ActiveEdges = edge1; - else if( !edge2->prevInAEL ) m_ActiveEdges = edge2; -} -//------------------------------------------------------------------------------ - -void Clipper::SwapPositionsInSEL(TEdge *edge1, TEdge *edge2) -{ - if( !( edge1->nextInSEL ) && !( edge1->prevInSEL ) ) return; - if( !( edge2->nextInSEL ) && !( edge2->prevInSEL ) ) return; - - if( edge1->nextInSEL == edge2 ) - { - TEdge* next = edge2->nextInSEL; - if( next ) next->prevInSEL = edge1; - TEdge* prev = edge1->prevInSEL; - if( prev ) prev->nextInSEL = edge2; - edge2->prevInSEL = prev; - edge2->nextInSEL = edge1; - edge1->prevInSEL = edge2; - edge1->nextInSEL = next; - } - else if( edge2->nextInSEL == edge1 ) - { - TEdge* next = edge1->nextInSEL; - if( next ) next->prevInSEL = edge2; - TEdge* prev = edge2->prevInSEL; - if( prev ) prev->nextInSEL = edge1; - edge1->prevInSEL = prev; - edge1->nextInSEL = edge2; - edge2->prevInSEL = edge1; - edge2->nextInSEL = next; - } - else - { - TEdge* next = edge1->nextInSEL; - TEdge* prev = edge1->prevInSEL; - edge1->nextInSEL = edge2->nextInSEL; - if( edge1->nextInSEL ) edge1->nextInSEL->prevInSEL = edge1; - edge1->prevInSEL = edge2->prevInSEL; - if( edge1->prevInSEL ) edge1->prevInSEL->nextInSEL = edge1; - edge2->nextInSEL = next; - if( edge2->nextInSEL ) edge2->nextInSEL->prevInSEL = edge2; - edge2->prevInSEL = prev; - if( edge2->prevInSEL ) edge2->prevInSEL->nextInSEL = edge2; - } - - if( !edge1->prevInSEL ) m_SortedEdges = edge1; - else if( !edge2->prevInSEL ) m_SortedEdges = edge2; -} -//------------------------------------------------------------------------------ - -TEdge* GetNextInAEL(TEdge *e, Direction dir) -{ - if( dir == dLeftToRight ) return e->nextInAEL; - else return e->prevInAEL; -} -//------------------------------------------------------------------------------ - -void Clipper::ProcessHorizontal(TEdge *horzEdge) -{ - Direction dir; - long64 horzLeft, horzRight; - - if( horzEdge->xcurr < horzEdge->xtop ) - { - horzLeft = horzEdge->xcurr; - horzRight = horzEdge->xtop; - dir = dLeftToRight; - } else - { - horzLeft = horzEdge->xtop; - horzRight = horzEdge->xcurr; - dir = dRightToLeft; - } - - TEdge* eMaxPair; - if( horzEdge->nextInLML ) eMaxPair = 0; - else eMaxPair = GetMaximaPair(horzEdge); - - TEdge* e = GetNextInAEL( horzEdge , dir ); - while( e ) - { - TEdge* eNext = GetNextInAEL( e, dir ); - - if (eMaxPair || - ((dir == dLeftToRight) && (e->xcurr <= horzRight)) || - ((dir == dRightToLeft) && (e->xcurr >= horzLeft))) - { - //ok, so far it looks like we're still in range of the horizontal edge - if ( e->xcurr == horzEdge->xtop && !eMaxPair ) - { - if (SlopesEqual(*e, *horzEdge->nextInLML, m_UseFullRange)) - { - //if output polygons share an edge, they'll need joining later ... - if (horzEdge->outIdx >= 0 && e->outIdx >= 0) - AddJoin(horzEdge->nextInLML, e, horzEdge->outIdx); - break; //we've reached the end of the horizontal line - } - else if (e->dx < horzEdge->nextInLML->dx) - //we really have got to the end of the intermediate horz edge so quit. - //nb: More -ve slopes follow more +ve slopes ABOVE the horizontal. - break; - } - - if( e == eMaxPair ) - { - //horzEdge is evidently a maxima horizontal and we've arrived at its end. - if (dir == dLeftToRight) - IntersectEdges(horzEdge, e, IntPoint(e->xcurr, horzEdge->ycurr), ipNone); - else - IntersectEdges(e, horzEdge, IntPoint(e->xcurr, horzEdge->ycurr), ipNone); - if (eMaxPair->outIdx >= 0) throw clipperException("ProcessHorizontal error"); - return; - } - else if( NEAR_EQUAL(e->dx, HORIZONTAL) && !IsMinima(e) && !(e->xcurr > e->xtop) ) - { - //An overlapping horizontal edge. Overlapping horizontal edges are - //processed as if layered with the current horizontal edge (horizEdge) - //being infinitesimally lower that the next (e). Therfore, we - //intersect with e only if e.xcurr is within the bounds of horzEdge ... - if( dir == dLeftToRight ) - IntersectEdges( horzEdge , e, IntPoint(e->xcurr, horzEdge->ycurr), - (IsTopHorz( e->xcurr ))? ipLeft : ipBoth ); - else - IntersectEdges( e, horzEdge, IntPoint(e->xcurr, horzEdge->ycurr), - (IsTopHorz( e->xcurr ))? ipRight : ipBoth ); - } - else if( dir == dLeftToRight ) - { - IntersectEdges( horzEdge, e, IntPoint(e->xcurr, horzEdge->ycurr), - (IsTopHorz( e->xcurr ))? ipLeft : ipBoth ); - } - else - { - IntersectEdges( e, horzEdge, IntPoint(e->xcurr, horzEdge->ycurr), - (IsTopHorz( e->xcurr ))? ipRight : ipBoth ); - } - SwapPositionsInAEL( horzEdge, e ); - } - else if( (dir == dLeftToRight && e->xcurr > horzRight && m_SortedEdges) || - (dir == dRightToLeft && e->xcurr < horzLeft && m_SortedEdges) ) break; - e = eNext; - } //end while - - if( horzEdge->nextInLML ) - { - if( horzEdge->outIdx >= 0 ) - AddOutPt( horzEdge, 0, IntPoint(horzEdge->xtop, horzEdge->ytop)); - UpdateEdgeIntoAEL( horzEdge ); - } - else - { - if ( horzEdge->outIdx >= 0 ) - IntersectEdges( horzEdge, eMaxPair, - IntPoint(horzEdge->xtop, horzEdge->ycurr), ipBoth); - if (eMaxPair->outIdx >= 0) throw clipperException("ProcessHorizontal error"); - DeleteFromAEL(eMaxPair); - DeleteFromAEL(horzEdge); - } -} -//------------------------------------------------------------------------------ - -void Clipper::UpdateEdgeIntoAEL(TEdge *&e) -{ - if( !e->nextInLML ) throw - clipperException("UpdateEdgeIntoAEL: invalid call"); - TEdge* AelPrev = e->prevInAEL; - TEdge* AelNext = e->nextInAEL; - e->nextInLML->outIdx = e->outIdx; - if( AelPrev ) AelPrev->nextInAEL = e->nextInLML; - else m_ActiveEdges = e->nextInLML; - if( AelNext ) AelNext->prevInAEL = e->nextInLML; - e->nextInLML->side = e->side; - e->nextInLML->windDelta = e->windDelta; - e->nextInLML->windCnt = e->windCnt; - e->nextInLML->windCnt2 = e->windCnt2; - e = e->nextInLML; - e->prevInAEL = AelPrev; - e->nextInAEL = AelNext; - if( !NEAR_EQUAL(e->dx, HORIZONTAL) ) InsertScanbeam( e->ytop ); -} -//------------------------------------------------------------------------------ - -bool Clipper::ProcessIntersections(const long64 botY, const long64 topY) -{ - if( !m_ActiveEdges ) return true; - try { - BuildIntersectList(botY, topY); - if ( !m_IntersectNodes) return true; - if ( FixupIntersections() ) ProcessIntersectList(); - else return false; - } - catch(...) { - m_SortedEdges = 0; - DisposeIntersectNodes(); - throw clipperException("ProcessIntersections error"); - } - return true; -} -//------------------------------------------------------------------------------ - -void Clipper::DisposeIntersectNodes() -{ - while ( m_IntersectNodes ) - { - IntersectNode* iNode = m_IntersectNodes->next; - delete m_IntersectNodes; - m_IntersectNodes = iNode; - } -} -//------------------------------------------------------------------------------ - -void Clipper::BuildIntersectList(const long64 botY, const long64 topY) -{ - if ( !m_ActiveEdges ) return; - - //prepare for sorting ... - TEdge* e = m_ActiveEdges; - e->tmpX = TopX( *e, topY ); - m_SortedEdges = e; - m_SortedEdges->prevInSEL = 0; - e = e->nextInAEL; - while( e ) - { - e->prevInSEL = e->prevInAEL; - e->prevInSEL->nextInSEL = e; - e->nextInSEL = 0; - e->tmpX = TopX( *e, topY ); - e = e->nextInAEL; - } - - //bubblesort ... - bool isModified = true; - while( isModified && m_SortedEdges ) - { - isModified = false; - e = m_SortedEdges; - while( e->nextInSEL ) - { - TEdge *eNext = e->nextInSEL; - IntPoint pt; - if(e->tmpX > eNext->tmpX && - IntersectPoint(*e, *eNext, pt, m_UseFullRange)) - { - if (pt.Y > botY) - { - pt.Y = botY; - pt.X = TopX(*e, pt.Y); - } - AddIntersectNode( e, eNext, pt ); - SwapPositionsInSEL(e, eNext); - isModified = true; - } - else - e = eNext; - } - if( e->prevInSEL ) e->prevInSEL->nextInSEL = 0; - else break; - } - m_SortedEdges = 0; -} -//------------------------------------------------------------------------------ - -bool Process1Before2(IntersectNode &node1, IntersectNode &node2) -{ - bool result; - if (node1.pt.Y == node2.pt.Y) - { - if (node1.edge1 == node2.edge1 || node1.edge2 == node2.edge1) - { - result = node2.pt.X > node1.pt.X; - if (node2.edge1->dx > 0) return !result; else return result; - } - else if (node1.edge1 == node2.edge2 || node1.edge2 == node2.edge2) - { - result = node2.pt.X > node1.pt.X; - if (node2.edge2->dx > 0) return !result; else return result; - } - else return node2.pt.X > node1.pt.X; - } - else return node1.pt.Y > node2.pt.Y; -} -//------------------------------------------------------------------------------ - -void Clipper::AddIntersectNode(TEdge *e1, TEdge *e2, const IntPoint &pt) -{ - IntersectNode* newNode = new IntersectNode; - newNode->edge1 = e1; - newNode->edge2 = e2; - newNode->pt = pt; - newNode->next = 0; - if( !m_IntersectNodes ) m_IntersectNodes = newNode; - else if( Process1Before2(*newNode, *m_IntersectNodes) ) - { - newNode->next = m_IntersectNodes; - m_IntersectNodes = newNode; - } - else - { - IntersectNode* iNode = m_IntersectNodes; - while( iNode->next && Process1Before2(*iNode->next, *newNode) ) - iNode = iNode->next; - newNode->next = iNode->next; - iNode->next = newNode; - } -} -//------------------------------------------------------------------------------ - -void Clipper::ProcessIntersectList() -{ - while( m_IntersectNodes ) - { - IntersectNode* iNode = m_IntersectNodes->next; - { - IntersectEdges( m_IntersectNodes->edge1 , - m_IntersectNodes->edge2 , m_IntersectNodes->pt, ipBoth ); - SwapPositionsInAEL( m_IntersectNodes->edge1 , m_IntersectNodes->edge2 ); - } - delete m_IntersectNodes; - m_IntersectNodes = iNode; - } -} -//------------------------------------------------------------------------------ - -void Clipper::DoMaxima(TEdge *e, long64 topY) -{ - TEdge* eMaxPair = GetMaximaPair(e); - long64 X = e->xtop; - TEdge* eNext = e->nextInAEL; - while( eNext != eMaxPair ) - { - if (!eNext) throw clipperException("DoMaxima error"); - IntersectEdges( e, eNext, IntPoint(X, topY), ipBoth ); - eNext = eNext->nextInAEL; - } - if( e->outIdx < 0 && eMaxPair->outIdx < 0 ) - { - DeleteFromAEL( e ); - DeleteFromAEL( eMaxPair ); - } - else if( e->outIdx >= 0 && eMaxPair->outIdx >= 0 ) - { - IntersectEdges( e, eMaxPair, IntPoint(X, topY), ipNone ); - } - else throw clipperException("DoMaxima error"); -} -//------------------------------------------------------------------------------ - -void Clipper::ProcessEdgesAtTopOfScanbeam(const long64 topY) -{ - TEdge* e = m_ActiveEdges; - while( e ) - { - //1. process maxima, treating them as if they're 'bent' horizontal edges, - // but exclude maxima with horizontal edges. nb: e can't be a horizontal. - if( IsMaxima(e, topY) && !NEAR_EQUAL(GetMaximaPair(e)->dx, HORIZONTAL) ) - { - //'e' might be removed from AEL, as may any following edges so ... - TEdge* ePrior = e->prevInAEL; - DoMaxima(e, topY); - if( !ePrior ) e = m_ActiveEdges; - else e = ePrior->nextInAEL; - } - else - { - //2. promote horizontal edges, otherwise update xcurr and ycurr ... - if( IsIntermediate(e, topY) && NEAR_EQUAL(e->nextInLML->dx, HORIZONTAL) ) - { - if (e->outIdx >= 0) - { - AddOutPt(e, 0, IntPoint(e->xtop, e->ytop)); - - for (HorzJoinList::size_type i = 0; i < m_HorizJoins.size(); ++i) - { - IntPoint pt, pt2; - HorzJoinRec* hj = m_HorizJoins[i]; - if (GetOverlapSegment(IntPoint(hj->edge->xbot, hj->edge->ybot), - IntPoint(hj->edge->xtop, hj->edge->ytop), - IntPoint(e->nextInLML->xbot, e->nextInLML->ybot), - IntPoint(e->nextInLML->xtop, e->nextInLML->ytop), pt, pt2)) - AddJoin(hj->edge, e->nextInLML, hj->savedIdx, e->outIdx); - } - - AddHorzJoin(e->nextInLML, e->outIdx); - } - UpdateEdgeIntoAEL(e); - AddEdgeToSEL(e); - } else - { - //this just simplifies horizontal processing ... - e->xcurr = TopX( *e, topY ); - e->ycurr = topY; - } - e = e->nextInAEL; - } - } - - //3. Process horizontals at the top of the scanbeam ... - ProcessHorizontals(); - - //4. Promote intermediate vertices ... - e = m_ActiveEdges; - while( e ) - { - if( IsIntermediate( e, topY ) ) - { - if( e->outIdx >= 0 ) AddOutPt(e, 0, IntPoint(e->xtop,e->ytop)); - UpdateEdgeIntoAEL(e); - - //if output polygons share an edge, they'll need joining later ... - if (e->outIdx >= 0 && e->prevInAEL && e->prevInAEL->outIdx >= 0 && - e->prevInAEL->xcurr == e->xbot && e->prevInAEL->ycurr == e->ybot && - SlopesEqual(IntPoint(e->xbot,e->ybot), IntPoint(e->xtop, e->ytop), - IntPoint(e->xbot,e->ybot), - IntPoint(e->prevInAEL->xtop, e->prevInAEL->ytop), m_UseFullRange)) - { - AddOutPt(e->prevInAEL, 0, IntPoint(e->xbot, e->ybot)); - AddJoin(e, e->prevInAEL); - } - else if (e->outIdx >= 0 && e->nextInAEL && e->nextInAEL->outIdx >= 0 && - e->nextInAEL->ycurr > e->nextInAEL->ytop && - e->nextInAEL->ycurr < e->nextInAEL->ybot && - e->nextInAEL->xcurr == e->xbot && e->nextInAEL->ycurr == e->ybot && - SlopesEqual(IntPoint(e->xbot,e->ybot), IntPoint(e->xtop, e->ytop), - IntPoint(e->xbot,e->ybot), - IntPoint(e->nextInAEL->xtop, e->nextInAEL->ytop), m_UseFullRange)) - { - AddOutPt(e->nextInAEL, 0, IntPoint(e->xbot, e->ybot)); - AddJoin(e, e->nextInAEL); - } - } - e = e->nextInAEL; - } -} -//------------------------------------------------------------------------------ - -void Clipper::FixupOutPolygon(OutRec &outRec) -{ - //FixupOutPolygon() - removes duplicate points and simplifies consecutive - //parallel edges by removing the middle vertex. - OutPt *lastOK = 0; - outRec.pts = outRec.bottomPt; - OutPt *pp = outRec.bottomPt; - - for (;;) - { - if (pp->prev == pp || pp->prev == pp->next ) - { - DisposeOutPts(pp); - outRec.pts = 0; - outRec.bottomPt = 0; - return; - } - //test for duplicate points and for same slope (cross-product) ... - if ( PointsEqual(pp->pt, pp->next->pt) || - SlopesEqual(pp->prev->pt, pp->pt, pp->next->pt, m_UseFullRange) ) - { - lastOK = 0; - OutPt *tmp = pp; - if (pp == outRec.bottomPt) - { - if (tmp->prev->pt.Y > tmp->next->pt.Y) - outRec.bottomPt = tmp->prev; else - outRec.bottomPt = tmp->next; - outRec.pts = outRec.bottomPt; - outRec.bottomPt->idx = outRec.idx; - } - pp->prev->next = pp->next; - pp->next->prev = pp->prev; - pp = pp->prev; - delete tmp; - } - else if (pp == lastOK) break; - else - { - if (!lastOK) lastOK = pp; - pp = pp->next; - } - } -} -//------------------------------------------------------------------------------ - -void Clipper::BuildResult(Polygons &polys) -{ - int k = 0; - polys.resize(m_PolyOuts.size()); - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) - { - if (m_PolyOuts[i]->pts) - { - Polygon* pg = &polys[k]; - pg->clear(); - OutPt* p = m_PolyOuts[i]->pts; - do - { - pg->push_back(p->pt); - p = p->next; - } while (p != m_PolyOuts[i]->pts); - //make sure each polygon has at least 3 vertices ... - if (pg->size() < 3) pg->clear(); else k++; - } - } - polys.resize(k); -} -//------------------------------------------------------------------------------ - -void Clipper::BuildResultEx(ExPolygons &polys) -{ - PolyOutList::size_type i = 0; - int k = 0; - polys.resize(0); - polys.reserve(m_PolyOuts.size()); - while (i < m_PolyOuts.size() && m_PolyOuts[i]->pts) - { - ExPolygon epg; - OutPt* p = m_PolyOuts[i]->pts; - do { - epg.outer.push_back(p->pt); - p = p->next; - } while (p != m_PolyOuts[i]->pts); - i++; - //make sure polygons have at least 3 vertices ... - if (epg.outer.size() < 3) continue; - while (i < m_PolyOuts.size() - && m_PolyOuts[i]->pts && m_PolyOuts[i]->isHole) - { - Polygon pg; - p = m_PolyOuts[i]->pts; - do { - pg.push_back(p->pt); - p = p->next; - } while (p != m_PolyOuts[i]->pts); - epg.holes.push_back(pg); - i++; - } - polys.push_back(epg); - k++; - } - polys.resize(k); -} -//------------------------------------------------------------------------------ - -void SwapIntersectNodes(IntersectNode &int1, IntersectNode &int2) -{ - TEdge *e1 = int1.edge1; - TEdge *e2 = int1.edge2; - IntPoint p = int1.pt; - - int1.edge1 = int2.edge1; - int1.edge2 = int2.edge2; - int1.pt = int2.pt; - - int2.edge1 = e1; - int2.edge2 = e2; - int2.pt = p; -} -//------------------------------------------------------------------------------ - -bool Clipper::FixupIntersections() -{ - if ( !m_IntersectNodes->next ) return true; - - CopyAELToSEL(); - IntersectNode *int1 = m_IntersectNodes; - IntersectNode *int2 = m_IntersectNodes->next; - while (int2) - { - TEdge *e1 = int1->edge1; - TEdge *e2; - if (e1->prevInSEL == int1->edge2) e2 = e1->prevInSEL; - else if (e1->nextInSEL == int1->edge2) e2 = e1->nextInSEL; - else - { - //The current intersection is out of order, so try and swap it with - //a subsequent intersection ... - while (int2) - { - if (int2->edge1->nextInSEL == int2->edge2 || - int2->edge1->prevInSEL == int2->edge2) break; - else int2 = int2->next; - } - if ( !int2 ) return false; //oops!!! - - //found an intersect node that can be swapped ... - SwapIntersectNodes(*int1, *int2); - e1 = int1->edge1; - e2 = int1->edge2; - } - SwapPositionsInSEL(e1, e2); - int1 = int1->next; - int2 = int1->next; - } - - m_SortedEdges = 0; - - //finally, check the last intersection too ... - return (int1->edge1->prevInSEL == int1->edge2 || - int1->edge1->nextInSEL == int1->edge2); -} -//------------------------------------------------------------------------------ - -bool E2InsertsBeforeE1(TEdge &e1, TEdge &e2) -{ - if (e2.xcurr == e1.xcurr) return e2.dx > e1.dx; - else return e2.xcurr < e1.xcurr; -} -//------------------------------------------------------------------------------ - -void Clipper::InsertEdgeIntoAEL(TEdge *edge) -{ - edge->prevInAEL = 0; - edge->nextInAEL = 0; - if( !m_ActiveEdges ) - { - m_ActiveEdges = edge; - } - else if( E2InsertsBeforeE1(*m_ActiveEdges, *edge) ) - { - edge->nextInAEL = m_ActiveEdges; - m_ActiveEdges->prevInAEL = edge; - m_ActiveEdges = edge; - } else - { - TEdge* e = m_ActiveEdges; - while( e->nextInAEL && !E2InsertsBeforeE1(*e->nextInAEL , *edge) ) - e = e->nextInAEL; - edge->nextInAEL = e->nextInAEL; - if( e->nextInAEL ) e->nextInAEL->prevInAEL = edge; - edge->prevInAEL = e; - e->nextInAEL = edge; - } -} -//---------------------------------------------------------------------- - -void Clipper::DoEdge1(TEdge *edge1, TEdge *edge2, const IntPoint &pt) -{ - AddOutPt(edge1, edge2, pt); - SwapSides(*edge1, *edge2); - SwapPolyIndexes(*edge1, *edge2); -} -//---------------------------------------------------------------------- - -void Clipper::DoEdge2(TEdge *edge1, TEdge *edge2, const IntPoint &pt) -{ - AddOutPt(edge2, edge1, pt); - SwapSides(*edge1, *edge2); - SwapPolyIndexes(*edge1, *edge2); -} -//---------------------------------------------------------------------- - -void Clipper::DoBothEdges(TEdge *edge1, TEdge *edge2, const IntPoint &pt) -{ - AddOutPt(edge1, edge2, pt); - AddOutPt(edge2, edge1, pt); - SwapSides( *edge1 , *edge2 ); - SwapPolyIndexes( *edge1 , *edge2 ); -} -//---------------------------------------------------------------------- - -void Clipper::CheckHoleLinkages1(OutRec *outRec1, OutRec *outRec2) -{ - //when a polygon is split into 2 polygons, make sure any holes the original - //polygon contained link to the correct polygon ... - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) - { - OutRec *orec = m_PolyOuts[i]; - if (orec->isHole && orec->bottomPt && orec->FirstLeft == outRec1 && - !PointInPolygon(orec->bottomPt->pt, outRec1->pts, m_UseFullRange)) - orec->FirstLeft = outRec2; - } -} -//---------------------------------------------------------------------- - -void Clipper::CheckHoleLinkages2(OutRec *outRec1, OutRec *outRec2) -{ - //if a hole is owned by outRec2 then make it owned by outRec1 ... - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) - if (m_PolyOuts[i]->isHole && m_PolyOuts[i]->bottomPt && - m_PolyOuts[i]->FirstLeft == outRec2) - m_PolyOuts[i]->FirstLeft = outRec1; -} -//---------------------------------------------------------------------- - -void Clipper::JoinCommonEdges(bool fixHoleLinkages) -{ - for (JoinList::size_type i = 0; i < m_Joins.size(); i++) - { - JoinRec* j = m_Joins[i]; - OutRec *outRec1 = m_PolyOuts[j->poly1Idx]; - OutPt *pp1a = outRec1->pts; - OutRec *outRec2 = m_PolyOuts[j->poly2Idx]; - OutPt *pp2a = outRec2->pts; - IntPoint pt1 = j->pt2a, pt2 = j->pt2b; - IntPoint pt3 = j->pt1a, pt4 = j->pt1b; - if (!FindSegment(pp1a, pt1, pt2)) continue; - if (j->poly1Idx == j->poly2Idx) - { - //we're searching the same polygon for overlapping segments so - //segment 2 mustn't be the same as segment 1 ... - pp2a = pp1a->next; - if (!FindSegment(pp2a, pt3, pt4) || (pp2a == pp1a)) continue; - } - else if (!FindSegment(pp2a, pt3, pt4)) continue; - - if (!GetOverlapSegment(pt1, pt2, pt3, pt4, pt1, pt2)) continue; - - OutPt *p1, *p2, *p3, *p4; - OutPt *prev = pp1a->prev; - //get p1 & p2 polypts - the overlap start & endpoints on poly1 - if (PointsEqual(pp1a->pt, pt1)) p1 = pp1a; - else if (PointsEqual(prev->pt, pt1)) p1 = prev; - else p1 = InsertPolyPtBetween(pp1a, prev, pt1); - - if (PointsEqual(pp1a->pt, pt2)) p2 = pp1a; - else if (PointsEqual(prev->pt, pt2)) p2 = prev; - else if ((p1 == pp1a) || (p1 == prev)) - p2 = InsertPolyPtBetween(pp1a, prev, pt2); - else if (Pt3IsBetweenPt1AndPt2(pp1a->pt, p1->pt, pt2)) - p2 = InsertPolyPtBetween(pp1a, p1, pt2); else - p2 = InsertPolyPtBetween(p1, prev, pt2); - - //get p3 & p4 polypts - the overlap start & endpoints on poly2 - prev = pp2a->prev; - if (PointsEqual(pp2a->pt, pt1)) p3 = pp2a; - else if (PointsEqual(prev->pt, pt1)) p3 = prev; - else p3 = InsertPolyPtBetween(pp2a, prev, pt1); - - if (PointsEqual(pp2a->pt, pt2)) p4 = pp2a; - else if (PointsEqual(prev->pt, pt2)) p4 = prev; - else if ((p3 == pp2a) || (p3 == prev)) - p4 = InsertPolyPtBetween(pp2a, prev, pt2); - else if (Pt3IsBetweenPt1AndPt2(pp2a->pt, p3->pt, pt2)) - p4 = InsertPolyPtBetween(pp2a, p3, pt2); else - p4 = InsertPolyPtBetween(p3, prev, pt2); - - //p1.pt == p3.pt and p2.pt == p4.pt so join p1 to p3 and p2 to p4 ... - if (p1->next == p2 && p3->prev == p4) - { - p1->next = p3; - p3->prev = p1; - p2->prev = p4; - p4->next = p2; - } - else if (p1->prev == p2 && p3->next == p4) - { - p1->prev = p3; - p3->next = p1; - p2->next = p4; - p4->prev = p2; - } - else - continue; //an orientation is probably wrong - - if (j->poly2Idx == j->poly1Idx) - { - //instead of joining two polygons, we've just created a new one by - //splitting one polygon into two. - outRec1->pts = PolygonBottom(p1); - outRec1->bottomPt = outRec1->pts; - outRec1->bottomPt->idx = outRec1->idx; - outRec2 = CreateOutRec(); - m_PolyOuts.push_back(outRec2); - outRec2->idx = (int)m_PolyOuts.size()-1; - j->poly2Idx = outRec2->idx; - outRec2->pts = PolygonBottom(p2); - outRec2->bottomPt = outRec2->pts; - outRec2->bottomPt->idx = outRec2->idx; - - if (PointInPolygon(outRec2->pts->pt, outRec1->pts, m_UseFullRange)) - { - outRec2->isHole = !outRec1->isHole; - outRec2->FirstLeft = outRec1; - if (outRec2->isHole == Orientation(outRec2, m_UseFullRange)) - ReversePolyPtLinks(*outRec2->pts); - } else if (PointInPolygon(outRec1->pts->pt, outRec2->pts, m_UseFullRange)) - { - outRec2->isHole = outRec1->isHole; - outRec1->isHole = !outRec2->isHole; - outRec2->FirstLeft = outRec1->FirstLeft; - outRec1->FirstLeft = outRec2; - if (outRec1->isHole == Orientation(outRec1, m_UseFullRange)) - ReversePolyPtLinks(*outRec1->pts); - } else - { - outRec2->isHole = outRec1->isHole; - outRec2->FirstLeft = outRec1->FirstLeft; - //make sure any contained holes now link to the correct polygon ... - if (fixHoleLinkages) CheckHoleLinkages1(outRec1, outRec2); - } - - //now fixup any subsequent joins that match this polygon - for (JoinList::size_type k = i+1; k < m_Joins.size(); k++) - { - JoinRec* j2 = m_Joins[k]; - if (j2->poly1Idx == j->poly1Idx && PointIsVertex(j2->pt1a, p2)) - j2->poly1Idx = j->poly2Idx; - if (j2->poly2Idx == j->poly1Idx && PointIsVertex(j2->pt2a, p2)) - j2->poly2Idx = j->poly2Idx; - } - - //now cleanup redundant edges too ... - FixupOutPolygon(*outRec1); - FixupOutPolygon(*outRec2); - } else - { - //joined 2 polygons together ... - - //make sure any holes contained by outRec2 now link to outRec1 ... - if (fixHoleLinkages) CheckHoleLinkages2(outRec1, outRec2); - - //delete the obsolete pointer ... - int OKIdx = outRec1->idx; - int ObsoleteIdx = outRec2->idx; - outRec2->pts = 0; - outRec2->bottomPt = 0; - outRec2->AppendLink = outRec1; - //holes are practically always joined to outers, not vice versa ... - if (outRec1->isHole && !outRec2->isHole) outRec1->isHole = false; - - //now fixup any subsequent Joins that match this polygon - for (JoinList::size_type k = i+1; k < m_Joins.size(); k++) - { - JoinRec* j2 = m_Joins[k]; - if (j2->poly1Idx == ObsoleteIdx) j2->poly1Idx = OKIdx; - if (j2->poly2Idx == ObsoleteIdx) j2->poly2Idx = OKIdx; - } - - //now cleanup redundant edges too ... - if (outRec1->pts) - FixupOutPolygon(*outRec1); - else - FixupOutPolygon(*outRec2); - } - } -} -//------------------------------------------------------------------------------ - -void ReversePoints(Polygon& p) -{ - std::reverse(p.begin(), p.end()); -} -//------------------------------------------------------------------------------ - -void ReversePoints(Polygons& p) -{ - for (Polygons::size_type i = 0; i < p.size(); ++i) - ReversePoints(p[i]); -} - -//------------------------------------------------------------------------------ -// OffsetPolygon functions ... -//------------------------------------------------------------------------------ - -struct DoublePoint -{ - double X; - double Y; - DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {} -}; -//------------------------------------------------------------------------------ - -Polygon BuildArc(const IntPoint &pt, - const double a1, const double a2, const double r) -{ - int steps = std::max(6, int(std::sqrt(std::fabs(r)) * std::fabs(a2 - a1))); - Polygon result(steps); - int n = steps - 1; - double da = (a2 - a1) / n; - double a = a1; - for (int i = 0; i <= n; ++i) - { - result[i].X = pt.X + Round(std::cos(a)*r); - result[i].Y = pt.Y + Round(std::sin(a)*r); - a += da; - } - return result; -} -//------------------------------------------------------------------------------ - -DoublePoint GetUnitNormal( const IntPoint &pt1, const IntPoint &pt2) -{ - if(pt2.X == pt1.X && pt2.Y == pt1.Y) - return DoublePoint(0, 0); - - double dx = (double)(pt2.X - pt1.X); - double dy = (double)(pt2.Y - pt1.Y); - double f = 1 *1.0/ std::sqrt( dx*dx + dy*dy ); - dx *= f; - dy *= f; - return DoublePoint(dy, -dx); -} - -//------------------------------------------------------------------------------ -//------------------------------------------------------------------------------ - -class PolyOffsetBuilder -{ -private: - Polygons m_p; - Polygon* m_curr_poly; - std::vector normals; - double m_delta, m_RMin, m_R; - size_t m_i, m_j, m_k; - static const int buffLength = 128; - JoinType m_jointype; - -public: - -PolyOffsetBuilder(const Polygons& in_polys, Polygons& out_polys, - double delta, JoinType jointype, double MiterLimit) -{ - //nb precondition - out_polys != ptsin_polys - if (NEAR_ZERO(delta)) - { - out_polys = in_polys; - return; - } - - this->m_p = in_polys; - this->m_delta = delta; - this->m_jointype = jointype; - if (MiterLimit <= 1) MiterLimit = 1; - m_RMin = 2/(MiterLimit*MiterLimit); - - double deltaSq = delta*delta; - out_polys.clear(); - out_polys.resize(in_polys.size()); - for (m_i = 0; m_i < in_polys.size(); m_i++) - { - m_curr_poly = &out_polys[m_i]; - size_t len = in_polys[m_i].size(); - if (len > 1 && m_p[m_i][0].X == m_p[m_i][len - 1].X && - m_p[m_i][0].Y == m_p[m_i][len-1].Y) len--; - - //when 'shrinking' polygons - to minimize artefacts - //strip those polygons that have an area < pi * delta^2 ... - double a1 = Area(in_polys[m_i]); - if (delta < 0) { if (a1 > 0 && a1 < deltaSq *pi) len = 0; } - else if (a1 < 0 && -a1 < deltaSq *pi) len = 0; //holes have neg. area - - if (len == 0 || (len < 3 && delta <= 0)) - continue; - else if (len == 1) - { - Polygon arc; - arc = BuildArc(in_polys[m_i][len-1], 0, 2 * pi, delta); - out_polys[m_i] = arc; - continue; - } - - //build normals ... - normals.clear(); - normals.resize(len); - normals[len-1] = GetUnitNormal(in_polys[m_i][len-1], in_polys[m_i][0]); - for (m_j = 0; m_j < len -1; ++m_j) - normals[m_j] = GetUnitNormal(in_polys[m_i][m_j], in_polys[m_i][m_j+1]); - - m_k = len -1; - for (m_j = 0; m_j < len; ++m_j) - { - switch (jointype) - { - case jtMiter: - { - m_R = 1 + (normals[m_j].X*normals[m_k].X + - normals[m_j].Y*normals[m_k].Y); - if (m_R >= m_RMin) DoMiter(); else DoSquare(MiterLimit); - break; - } - case jtSquare: DoSquare(); break; - case jtRound: DoRound(); break; - } - m_k = m_j; - } - } - - //finally, clean up untidy corners using Clipper ... - Clipper clpr; - clpr.AddPolygons(out_polys, ptSubject); - if (delta > 0) - { - if (!clpr.Execute(ctUnion, out_polys, pftPositive, pftPositive)) - out_polys.clear(); - } - else - { - IntRect r = clpr.GetBounds(); - Polygon outer(4); - outer[0] = IntPoint(r.left - 10, r.bottom + 10); - outer[1] = IntPoint(r.right + 10, r.bottom + 10); - outer[2] = IntPoint(r.right + 10, r.top - 10); - outer[3] = IntPoint(r.left - 10, r.top - 10); - - clpr.AddPolygon(outer, ptSubject); - if (clpr.Execute(ctUnion, out_polys, pftNegative, pftNegative)) - { - out_polys.erase(out_polys.begin()); - ReversePoints(out_polys); - - } else - out_polys.clear(); - } -} -//------------------------------------------------------------------------------ - -private: - -void AddPoint(const IntPoint& pt) -{ - Polygon::size_type len = m_curr_poly->size(); - if (len == m_curr_poly->capacity()) - m_curr_poly->reserve(len + buffLength); - m_curr_poly->push_back(pt); -} -//------------------------------------------------------------------------------ - -void DoSquare(double mul = 1.0) -{ - IntPoint pt1 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_k].X * m_delta), - (long64)Round(m_p[m_i][m_j].Y + normals[m_k].Y * m_delta)); - IntPoint pt2 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_j].X * m_delta), - (long64)Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta)); - if ((normals[m_k].X * normals[m_j].Y - normals[m_j].X * normals[m_k].Y) * m_delta >= 0) - { - double a1 = std::atan2(normals[m_k].Y, normals[m_k].X); - double a2 = std::atan2(-normals[m_j].Y, -normals[m_j].X); - a1 = std::fabs(a2 - a1); - if (a1 > pi) a1 = pi * 2 - a1; - double dx = std::tan((pi - a1)/4) * std::fabs(m_delta * mul); - pt1 = IntPoint((long64)(pt1.X -normals[m_k].Y * dx), - (long64)(pt1.Y + normals[m_k].X * dx)); - AddPoint(pt1); - pt2 = IntPoint((long64)(pt2.X + normals[m_j].Y * dx), - (long64)(pt2.Y -normals[m_j].X * dx)); - AddPoint(pt2); - } - else - { - AddPoint(pt1); - AddPoint(m_p[m_i][m_j]); - AddPoint(pt2); - } -} -//------------------------------------------------------------------------------ - -void DoMiter() -{ - if ((normals[m_k].X * normals[m_j].Y - normals[m_j].X * normals[m_k].Y) * m_delta >= 0) - { - double q = m_delta / m_R; - AddPoint(IntPoint((long64)Round(m_p[m_i][m_j].X + - (normals[m_k].X + normals[m_j].X) * q), - (long64)Round(m_p[m_i][m_j].Y + (normals[m_k].Y + normals[m_j].Y) * q))); - } - else - { - IntPoint pt1 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_k].X * - m_delta), (long64)Round(m_p[m_i][m_j].Y + normals[m_k].Y * m_delta)); - IntPoint pt2 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_j].X * - m_delta), (long64)Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta)); - AddPoint(pt1); - AddPoint(m_p[m_i][m_j]); - AddPoint(pt2); - } -} -//------------------------------------------------------------------------------ - -void DoRound() -{ - IntPoint pt1 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_k].X * m_delta), - (long64)Round(m_p[m_i][m_j].Y + normals[m_k].Y * m_delta)); - IntPoint pt2 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_j].X * m_delta), - (long64)Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta)); - AddPoint(pt1); - //round off reflex angles (ie > 180 deg) unless almost flat (ie < ~10deg). - if ((normals[m_k].X*normals[m_j].Y - normals[m_j].X*normals[m_k].Y) * m_delta >= 0) - { - if (normals[m_j].X * normals[m_k].X + normals[m_j].Y * normals[m_k].Y < 0.985) - { - double a1 = std::atan2(normals[m_k].Y, normals[m_k].X); - double a2 = std::atan2(normals[m_j].Y, normals[m_j].X); - if (m_delta > 0 && a2 < a1) a2 += pi *2; - else if (m_delta < 0 && a2 > a1) a2 -= pi *2; - Polygon arc = BuildArc(m_p[m_i][m_j], a1, a2, m_delta); - for (Polygon::size_type m = 0; m < arc.size(); m++) - AddPoint(arc[m]); - } - } - else - AddPoint(m_p[m_i][m_j]); - AddPoint(pt2); -} -//-------------------------------------------------------------------------- - -}; //end PolyOffsetBuilder - -//------------------------------------------------------------------------------ -//------------------------------------------------------------------------------ - -void OffsetPolygons(const Polygons &in_polys, Polygons &out_polys, - double delta, JoinType jointype, double MiterLimit) -{ - if (&out_polys == &in_polys) - { - Polygons poly2(in_polys); - PolyOffsetBuilder(poly2, out_polys, delta, jointype, MiterLimit); - } - else PolyOffsetBuilder(in_polys, out_polys, delta, jointype, MiterLimit); -} -//------------------------------------------------------------------------------ - -std::ostream& operator <<(std::ostream &s, IntPoint& p) -{ - s << p.X << ' ' << p.Y << "\n"; - return s; -} -//------------------------------------------------------------------------------ - -std::ostream& operator <<(std::ostream &s, Polygon &p) -{ - for (Polygon::size_type i = 0; i < p.size(); i++) - s << p[i]; - s << "\n"; - return s; -} -//------------------------------------------------------------------------------ - -std::ostream& operator <<(std::ostream &s, Polygons &p) -{ - for (Polygons::size_type i = 0; i < p.size(); i++) - s << p[i]; - s << "\n"; - return s; -} -//------------------------------------------------------------------------------ - -} //ClipperLib namespace +/******************************************************************************* +* * +* Author : Angus Johnson * +* Version : 4.6.3 * +* Date : 11 November 2011 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2011 * +* * +* License: * +* Use, modification & distribution is subject to Boost Software License Ver 1. * +* http://www.boost.org/LICENSE_1_0.txt * +* * +* Attributions: * +* The code in this library is an extension of Bala Vatti's clipping algorithm: * +* "A generic solution to polygon clipping" * +* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * +* http://portal.acm.org/citation.cfm?id=129906 * +* * +* Computer graphics and geometric modeling: implementation and algorithms * +* By Max K. Agoston * +* Springer; 1 edition (January 4, 2005) * +* http://books.google.com/books?q=vatti+clipping+agoston * +* * +* See also: * +* "Polygon Offsetting by Computing Winding Numbers" * +* Paper no. DETC2005-85513 pp. 565-575 * +* ASME 2005 International Design Engineering Technical Conferences * +* and Computers and Information in Engineering Conference (IDETC/CIE2005) * +* September 24-28, 2005 , Long Beach, California, USA * +* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * +* * +*******************************************************************************/ + +/******************************************************************************* +* * +* This is a translation of the Delphi Clipper library and the naming style * +* used has retained a Delphi flavour. * +* * +*******************************************************************************/ + +#include "../include/clipper.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace ClipperLib { + +static long64 const loRange = 1518500249; //sqrt(2^63 -1)/2 +static long64 const hiRange = 6521908912666391106LL; //sqrt(2^127 -1)/2 +static double const pi = 3.141592653589793238; +enum Direction { dRightToLeft, dLeftToRight }; +enum RangeTest { rtLo, rtHi, rtError }; + +#define HORIZONTAL (-1.0E+40) +#define TOLERANCE (1.0e-20) +#define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE)) +#define NEAR_EQUAL(a, b) NEAR_ZERO((a) - (b)) + +inline long64 Abs(long64 val) +{ + if (val < 0) return -val; else return val; +} +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Int128 class (enables safe math on signed 64bit integers) +// eg Int128 val1((long64)9223372036854775807); //ie 2^63 -1 +// Int128 val2((long64)9223372036854775807); +// Int128 val3 = val1 * val2; +// val3.AsString => "85070591730234615847396907784232501249" (8.5e+37) +//------------------------------------------------------------------------------ + +class Int128 +{ + public: + + Int128(long64 _lo = 0) + { + hi = 0; + if (_lo < 0) { + lo = -_lo; + Negate(*this); + } else + lo = _lo; + } + + Int128(const Int128 &val): hi(val.hi), lo(val.lo){} + + long64 operator = (const long64 &val) + { + hi = 0; + lo = Abs(val); + if (val < 0) Negate(*this); + return val; + } + + bool operator == (const Int128 &val) const + {return (hi == val.hi && lo == val.lo);} + + bool operator != (const Int128 &val) const { return !(*this == val);} + + bool operator > (const Int128 &val) const + { + if (hi > val.hi) return true; + else if (hi < val.hi) return false; + else return ulong64(lo) > ulong64(val.lo); + } + + bool operator < (const Int128 &val) const + { + if (hi < val.hi) return true; + else if (hi > val.hi) return false; + else return ulong64(lo) < ulong64(val.lo); + } + + Int128& operator += (const Int128 &rhs) + { + hi += rhs.hi; + lo += rhs.lo; + if (ulong64(lo) < ulong64(rhs.lo)) hi++; + return *this; + } + + Int128 operator + (const Int128 &rhs) const + { + Int128 result(*this); + result+= rhs; + return result; + } + + Int128& operator -= (const Int128 &rhs) + { + Int128 tmp(rhs); + Negate(tmp); + *this += tmp; + return *this; + } + + Int128 operator - (const Int128 &rhs) const + { + Int128 result(*this); + result-= rhs; + return result; + } + + Int128 operator * (const Int128 &rhs) const { + if ( !(hi == 0 || hi == -1) || !(rhs.hi == 0 || rhs.hi == -1)) + throw "Int128 operator*: overflow error"; + bool negate = (hi < 0) != (rhs.hi < 0); + + Int128 tmp(*this); + if (tmp.hi < 0) Negate(tmp); + ulong64 int1Hi = ulong64(tmp.lo) >> 32; + ulong64 int1Lo = ulong64(tmp.lo & 0xFFFFFFFF); + + tmp = rhs; + if (tmp.hi < 0) Negate(tmp); + ulong64 int2Hi = ulong64(tmp.lo) >> 32; + ulong64 int2Lo = ulong64(tmp.lo & 0xFFFFFFFF); + + //nb: see comments in clipper.pas + ulong64 a = int1Hi * int2Hi; + ulong64 b = int1Lo * int2Lo; + ulong64 c = int1Hi * int2Lo + int1Lo * int2Hi; + + tmp.hi = long64(a + (c >> 32)); + tmp.lo = long64(c << 32); + tmp.lo += long64(b); + if (ulong64(tmp.lo) < b) tmp.hi++; + if (negate) Negate(tmp); + return tmp; + } + + Int128 operator/ (const Int128 &rhs) const + { + if (rhs.lo == 0 && rhs.hi == 0) + throw "Int128 operator/: divide by zero"; + bool negate = (rhs.hi < 0) != (hi < 0); + Int128 result(*this), denom(rhs); + if (result.hi < 0) Negate(result); + if (denom.hi < 0) Negate(denom); + if (denom > result) return Int128(0); //result is only a fraction of 1 + Negate(denom); + + Int128 p(0); + for (int i = 0; i < 128; ++i) + { + p.hi = p.hi << 1; + if (p.lo < 0) p.hi++; + p.lo = long64(p.lo) << 1; + if (result.hi < 0) p.lo++; + result.hi = result.hi << 1; + if (result.lo < 0) result.hi++; + result.lo = long64(result.lo) << 1; + Int128 p2(p); + p += denom; + if (p.hi < 0) p = p2; + else result.lo++; + } + if (negate) Negate(result); + return result; + } + + double AsDouble() const + { + const double shift64 = 18446744073709551616.0; //2^64 + const double bit64 = 9223372036854775808.0; + if (hi < 0) + { + Int128 tmp(*this); + Negate(tmp); + if (tmp.lo < 0) + return (double)tmp.lo - bit64 - tmp.hi * shift64; + else + return -(double)tmp.lo - tmp.hi * shift64; + } + else if (lo < 0) + return -(double)lo + bit64 + hi * shift64; + else + return (double)lo + (double)hi * shift64; + } + + //for bug testing ... + std::string AsString() const + { + std::string result; + unsigned char r = 0; + Int128 tmp(0), val(*this); + if (hi < 0) Negate(val); + result.resize(50); + std::string::size_type i = result.size() -1; + while (val.hi != 0 || val.lo != 0) + { + Div10(val, tmp, r); + result[i--] = char('0' + r); + val = tmp; + } + if (hi < 0) result[i--] = '-'; + result.erase(0,i+1); + if (result.size() == 0) result = "0"; + return result; + } + +private: + long64 hi; + long64 lo; + + static void Negate(Int128 &val) + { + if (val.lo == 0) + { + if( val.hi == 0) return; + val.lo = ~val.lo; + val.hi = ~val.hi +1; + } + else + { + val.lo = ~val.lo +1; + val.hi = ~val.hi; + } + } + + //debugging only ... + void Div10(const Int128 val, Int128& result, unsigned char & remainder) const + { + remainder = 0; + result = 0; + for (int i = 63; i >= 0; --i) + { + if ((val.hi & ((long64)1 << i)) != 0) + remainder = char((remainder * 2) + 1); else + remainder *= char(2); + if (remainder >= 10) + { + result.hi += ((long64)1 << i); + remainder -= char(10); + } + } + for (int i = 63; i >= 0; --i) + { + if ((val.lo & ((long64)1 << i)) != 0) + remainder = char((remainder * 2) + 1); else + remainder *= char(2); + if (remainder >= 10) + { + result.lo += ((long64)1 << i); + remainder -= char(10); + } + } + } +}; + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +RangeTest TestRange(const Polygon &pts) +{ + RangeTest result = rtLo; + for (Polygon::size_type i = 0; i < pts.size(); ++i) + { + if (Abs(pts[i].X) > hiRange || Abs(pts[i].Y) > hiRange) + return rtError; + else if (Abs(pts[i].X) > loRange || Abs(pts[i].Y) > loRange) + result = rtHi; + } + return result; +} +//------------------------------------------------------------------------------ + +bool Orientation(const Polygon &poly) +{ + int highI = (int)poly.size() -1; + if (highI < 2) return false; + bool UseFullInt64Range = false; + + int j = 0, jplus, jminus; + for (int i = 0; i <= highI; ++i) + { + if (Abs(poly[i].X) > hiRange || Abs(poly[i].Y) > hiRange) + throw "Coordinate exceeds range bounds."; + if (Abs(poly[i].X) > loRange || Abs(poly[i].Y) > loRange) + UseFullInt64Range = true; + if (poly[i].Y < poly[j].Y) continue; + if ((poly[i].Y > poly[j].Y || poly[i].X < poly[j].X)) j = i; + }; + if (j == highI) jplus = 0; + else jplus = j +1; + if (j == 0) jminus = highI; + else jminus = j -1; + + IntPoint vec1, vec2; + //get cross product of vectors of the edges adjacent to highest point ... + vec1.X = poly[j].X - poly[jminus].X; + vec1.Y = poly[j].Y - poly[jminus].Y; + vec2.X = poly[jplus].X - poly[j].X; + vec2.Y = poly[jplus].Y - poly[j].Y; + + if (UseFullInt64Range) + { + Int128 cross = Int128(vec1.X) * Int128(vec2.Y) - + Int128(vec2.X) * Int128(vec1.Y); + return cross > 0; + } + else + { + return (vec1.X * vec2.Y - vec2.X * vec1.Y) > 0; + } +} +//------------------------------------------------------------------------------ + +bool Orientation(OutRec *outRec, bool UseFullInt64Range) +{ + OutPt *opBottom = outRec->pts, *op = outRec->pts->next; + while (op != outRec->pts) + { + if (op->pt.Y >= opBottom->pt.Y) + { + if (op->pt.Y > opBottom->pt.Y || op->pt.X < opBottom->pt.X) + opBottom = op; + } + op = op->next; + } + + IntPoint vec1, vec2; + vec1.X = op->pt.X - op->prev->pt.X; + vec1.Y = op->pt.Y - op->prev->pt.Y; + vec2.X = op->next->pt.X - op->pt.X; + vec2.Y = op->next->pt.Y - op->pt.Y; + + if (UseFullInt64Range) + { + Int128 cross = Int128(vec1.X) * Int128(vec2.Y) - Int128(vec2.X) * Int128(vec1.Y); + return cross > 0; + } + else + { + return (vec1.X * vec2.Y - vec2.X * vec1.Y) > 0; + } +} +//------------------------------------------------------------------------------ + +inline bool PointsEqual( const IntPoint &pt1, const IntPoint &pt2) +{ + return ( pt1.X == pt2.X && pt1.Y == pt2.Y ); +} +//------------------------------------------------------------------------------ + +double Area(const Polygon &poly) +{ + int highI = (int)poly.size() -1; + if (highI < 2) return 0; + bool UseFullInt64Range; + RangeTest rt = TestRange(poly); + switch (rt) { + case rtLo: + UseFullInt64Range = false; + break; + case rtHi: + UseFullInt64Range = true; + break; + default: + throw "Coordinate exceeds range bounds."; + } + + if (UseFullInt64Range) { + Int128 a(0); + a = (Int128(poly[highI].X) * Int128(poly[0].Y)) - + Int128(poly[0].X) * Int128(poly[highI].Y); + for (int i = 0; i < highI; ++i) + a += Int128(poly[i].X) * Int128(poly[i+1].Y) - + Int128(poly[i+1].X) * Int128(poly[i].Y); + return a.AsDouble() / 2; + } + else + { + double a; + a = (double)poly[highI].X * poly[0].Y - (double)poly[0].X * poly[highI].Y; + for (int i = 0; i < highI; ++i) + a += (double)poly[i].X * poly[i+1].Y - (double)poly[i+1].X * poly[i].Y; + return a/2; + } +} +//------------------------------------------------------------------------------ + +bool PointIsVertex(const IntPoint &pt, OutPt *pp) +{ + OutPt *pp2 = pp; + do + { + if (PointsEqual(pp2->pt, pt)) return true; + pp2 = pp2->next; + } + while (pp2 != pp); + return false; +} +//------------------------------------------------------------------------------ + +bool PointInPolygon(const IntPoint &pt, OutPt *pp, bool UseFullInt64Range) +{ + OutPt *pp2 = pp; + bool result = false; + if (UseFullInt64Range) { + do + { + if ((((pp2->pt.Y <= pt.Y) && (pt.Y < pp2->prev->pt.Y)) || + ((pp2->prev->pt.Y <= pt.Y) && (pt.Y < pp2->pt.Y))) && + Int128(pt.X - pp2->pt.X) < (Int128(pp2->prev->pt.X - pp2->pt.X) * + Int128(pt.Y - pp2->pt.Y)) / Int128(pp2->prev->pt.Y - pp2->pt.Y)) + result = !result; + pp2 = pp2->next; + } + while (pp2 != pp); + } + else + { + do + { + if ((((pp2->pt.Y <= pt.Y) && (pt.Y < pp2->prev->pt.Y)) || + ((pp2->prev->pt.Y <= pt.Y) && (pt.Y < pp2->pt.Y))) && + (pt.X < (pp2->prev->pt.X - pp2->pt.X) * (pt.Y - pp2->pt.Y) / + (pp2->prev->pt.Y - pp2->pt.Y) + pp2->pt.X )) result = !result; + pp2 = pp2->next; + } + while (pp2 != pp); + } + return result; +} +//------------------------------------------------------------------------------ + +bool SlopesEqual(TEdge &e1, TEdge &e2, bool UseFullInt64Range) +{ + if (e1.ybot == e1.ytop) return (e2.ybot == e2.ytop); + else if (e1.xbot == e1.xtop) return (e2.xbot == e2.xtop); + else if (UseFullInt64Range) + return Int128(e1.ytop - e1.ybot) * Int128(e2.xtop - e2.xbot) == + Int128(e1.xtop - e1.xbot) * Int128(e2.ytop - e2.ybot); + else return (e1.ytop - e1.ybot)*(e2.xtop - e2.xbot) == + (e1.xtop - e1.xbot)*(e2.ytop - e2.ybot); +} +//------------------------------------------------------------------------------ + +bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, + const IntPoint pt3, bool UseFullInt64Range) +{ + if (pt1.Y == pt2.Y) return (pt2.Y == pt3.Y); + else if (pt1.X == pt2.X) return (pt2.X == pt3.X); + else if (UseFullInt64Range) + return Int128(pt1.Y-pt2.Y) * Int128(pt2.X-pt3.X) == + Int128(pt1.X-pt2.X) * Int128(pt2.Y-pt3.Y); + else return (pt1.Y-pt2.Y)*(pt2.X-pt3.X) == (pt1.X-pt2.X)*(pt2.Y-pt3.Y); +} +//------------------------------------------------------------------------------ + +bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, + const IntPoint pt3, const IntPoint pt4, bool UseFullInt64Range) +{ + if (pt1.Y == pt2.Y) return (pt3.Y == pt4.Y); + else if (pt1.X == pt2.X) return (pt3.X == pt4.X); + else if (UseFullInt64Range) + return Int128(pt1.Y-pt2.Y) * Int128(pt3.X-pt4.X) == + Int128(pt1.X-pt2.X) * Int128(pt3.Y-pt4.Y); + else return (pt1.Y-pt2.Y)*(pt3.X-pt4.X) == (pt1.X-pt2.X)*(pt3.Y-pt4.Y); +} +//------------------------------------------------------------------------------ + +double GetDx(const IntPoint pt1, const IntPoint pt2) +{ + if (pt1.Y == pt2.Y) return HORIZONTAL; + else return + (double)(pt2.X - pt1.X) / (double)(pt2.Y - pt1.Y); +} +//--------------------------------------------------------------------------- + +void SetDx(TEdge &e) +{ + if (e.ybot == e.ytop) e.dx = HORIZONTAL; + else e.dx = + (double)(e.xtop - e.xbot) / (double)(e.ytop - e.ybot); +} +//--------------------------------------------------------------------------- + +void SwapSides(TEdge &edge1, TEdge &edge2) +{ + EdgeSide side = edge1.side; + edge1.side = edge2.side; + edge2.side = side; +} +//------------------------------------------------------------------------------ + +void SwapPolyIndexes(TEdge &edge1, TEdge &edge2) +{ + int outIdx = edge1.outIdx; + edge1.outIdx = edge2.outIdx; + edge2.outIdx = outIdx; +} +//------------------------------------------------------------------------------ + +inline long64 Round(double val) +{ + if ((val < 0)) return static_cast(val - 0.5); + else return static_cast(val + 0.5); +} +//------------------------------------------------------------------------------ + +long64 TopX(TEdge &edge, const long64 currentY) +{ + if( currentY == edge.ytop ) return edge.xtop; + return edge.xbot + Round(edge.dx *(currentY - edge.ybot)); +} +//------------------------------------------------------------------------------ + +long64 TopX(const IntPoint pt1, const IntPoint pt2, const long64 currentY) +{ + //preconditions: pt1.Y <> pt2.Y and pt1.Y > pt2.Y + if (currentY >= pt1.Y) return pt1.X; + else if (currentY == pt2.Y) return pt2.X; + else if (pt1.X == pt2.X) return pt1.X; + else + { + double q = (double)(pt1.X-pt2.X)/(double)(pt1.Y-pt2.Y); + return Round(pt1.X + (currentY - pt1.Y) *q); + } +} +//------------------------------------------------------------------------------ + +bool IntersectPoint(TEdge &edge1, TEdge &edge2, + IntPoint &ip, bool UseFullInt64Range) +{ + double b1, b2; + if (SlopesEqual(edge1, edge2, UseFullInt64Range)) return false; + else if (NEAR_ZERO(edge1.dx)) + { + ip.X = edge1.xbot; + if (NEAR_EQUAL(edge2.dx, HORIZONTAL)) + { + ip.Y = edge2.ybot; + } else + { + b2 = edge2.ybot - (edge2.xbot/edge2.dx); + ip.Y = Round(ip.X/edge2.dx + b2); + } + } + else if (NEAR_ZERO(edge2.dx)) + { + ip.X = edge2.xbot; + if (NEAR_EQUAL(edge1.dx, HORIZONTAL)) + { + ip.Y = edge1.ybot; + } else + { + b1 = edge1.ybot - (edge1.xbot/edge1.dx); + ip.Y = Round(ip.X/edge1.dx + b1); + } + } else + { + b1 = edge1.xbot - edge1.ybot * edge1.dx; + b2 = edge2.xbot - edge2.ybot * edge2.dx; + b2 = (b2-b1)/(edge1.dx - edge2.dx); + ip.Y = Round(b2); + ip.X = Round(edge1.dx * b2 + b1); + } + + return + //can be *so close* to the top of one edge that the rounded Y equals one ytop ... + (ip.Y == edge1.ytop && ip.Y >= edge2.ytop && edge1.tmpX > edge2.tmpX) || + (ip.Y == edge2.ytop && ip.Y >= edge1.ytop && edge1.tmpX > edge2.tmpX) || + (ip.Y > edge1.ytop && ip.Y > edge2.ytop); +} +//------------------------------------------------------------------------------ + +void ReversePolyPtLinks(OutPt &pp) +{ + OutPt *pp1, *pp2; + pp1 = &pp; + do { + pp2 = pp1->next; + pp1->next = pp1->prev; + pp1->prev = pp2; + pp1 = pp2; + } while( pp1 != &pp ); +} +//------------------------------------------------------------------------------ + +void DisposeOutPts(OutPt*& pp) +{ + if (pp == 0) return; + pp->prev->next = 0; + while( pp ) + { + OutPt *tmpPp = pp; + pp = pp->next; + delete tmpPp ; + } +} +//------------------------------------------------------------------------------ + +void InitEdge(TEdge *e, TEdge *eNext, + TEdge *ePrev, const IntPoint &pt, PolyType polyType) +{ + std::memset( e, 0, sizeof( TEdge )); + + e->next = eNext; + e->prev = ePrev; + e->xcurr = pt.X; + e->ycurr = pt.Y; + if (e->ycurr >= e->next->ycurr) + { + e->xbot = e->xcurr; + e->ybot = e->ycurr; + e->xtop = e->next->xcurr; + e->ytop = e->next->ycurr; + e->windDelta = 1; + } else + { + e->xtop = e->xcurr; + e->ytop = e->ycurr; + e->xbot = e->next->xcurr; + e->ybot = e->next->ycurr; + e->windDelta = -1; + } + SetDx(*e); + e->polyType = polyType; + e->outIdx = -1; +} +//------------------------------------------------------------------------------ + +inline void SwapX(TEdge &e) +{ + //swap horizontal edges' top and bottom x's so they follow the natural + //progression of the bounds - ie so their xbots will align with the + //adjoining lower edge. [Helpful in the ProcessHorizontal() method.] + e.xcurr = e.xtop; + e.xtop = e.xbot; + e.xbot = e.xcurr; +} +//------------------------------------------------------------------------------ + +void SwapPoints(IntPoint &pt1, IntPoint &pt2) +{ + IntPoint tmp = pt1; + pt1 = pt2; + pt2 = tmp; +} +//------------------------------------------------------------------------------ + +bool GetOverlapSegment(IntPoint pt1a, IntPoint pt1b, IntPoint pt2a, + IntPoint pt2b, IntPoint &pt1, IntPoint &pt2) +{ + //precondition: segments are colinear. + if ( pt1a.Y == pt1b.Y || Abs((pt1a.X - pt1b.X)/(pt1a.Y - pt1b.Y)) > 1 ) + { + if (pt1a.X > pt1b.X) SwapPoints(pt1a, pt1b); + if (pt2a.X > pt2b.X) SwapPoints(pt2a, pt2b); + if (pt1a.X > pt2a.X) pt1 = pt1a; else pt1 = pt2a; + if (pt1b.X < pt2b.X) pt2 = pt1b; else pt2 = pt2b; + return pt1.X < pt2.X; + } else + { + if (pt1a.Y < pt1b.Y) SwapPoints(pt1a, pt1b); + if (pt2a.Y < pt2b.Y) SwapPoints(pt2a, pt2b); + if (pt1a.Y < pt2a.Y) pt1 = pt1a; else pt1 = pt2a; + if (pt1b.Y > pt2b.Y) pt2 = pt1b; else pt2 = pt2b; + return pt1.Y > pt2.Y; + } +} +//------------------------------------------------------------------------------ + +OutPt* PolygonBottom(OutPt* pp) +{ + OutPt* p = pp->next; + OutPt* result = pp; + while (p != pp) + { + if (p->pt.Y > result->pt.Y) result = p; + else if (p->pt.Y == result->pt.Y && p->pt.X < result->pt.X) result = p; + p = p->next; + } + return result; +} +//------------------------------------------------------------------------------ + +bool FindSegment(OutPt* &pp, IntPoint &pt1, IntPoint &pt2) +{ + //outPt1 & outPt2 => the overlap segment (if the function returns true) + if (!pp) return false; + OutPt* pp2 = pp; + IntPoint pt1a = pt1, pt2a = pt2; + do + { + if (SlopesEqual(pt1a, pt2a, pp->pt, pp->prev->pt, true) && + SlopesEqual(pt1a, pt2a, pp->pt, true) && + GetOverlapSegment(pt1a, pt2a, pp->pt, pp->prev->pt, pt1, pt2)) + return true; + pp = pp->next; + } + while (pp != pp2); + return false; +} +//------------------------------------------------------------------------------ + +bool Pt3IsBetweenPt1AndPt2(const IntPoint pt1, + const IntPoint pt2, const IntPoint pt3) +{ + if (PointsEqual(pt1, pt3) || PointsEqual(pt2, pt3)) return true; + else if (pt1.X != pt2.X) return (pt1.X < pt3.X) == (pt3.X < pt2.X); + else return (pt1.Y < pt3.Y) == (pt3.Y < pt2.Y); +} +//------------------------------------------------------------------------------ + +OutPt* InsertPolyPtBetween(OutPt* p1, OutPt* p2, const IntPoint pt) +{ + if (p1 == p2) throw "JoinError"; + OutPt* result = new OutPt; + result->pt = pt; + if (p2 == p1->next) + { + p1->next = result; + p2->prev = result; + result->next = p2; + result->prev = p1; + } else + { + p2->next = result; + p1->prev = result; + result->next = p1; + result->prev = p2; + } + return result; +} + +//------------------------------------------------------------------------------ +// ClipperBase class methods ... +//------------------------------------------------------------------------------ + +ClipperBase::ClipperBase() //constructor +{ + m_MinimaList = 0; + m_CurrentLM = 0; + m_UseFullRange = true; +} +//------------------------------------------------------------------------------ + +ClipperBase::~ClipperBase() //destructor +{ + Clear(); +} +//------------------------------------------------------------------------------ + +bool ClipperBase::AddPolygon( const Polygon &pg, PolyType polyType) +{ + int len = (int)pg.size(); + if (len < 3) return false; + Polygon p(len); + p[0] = pg[0]; + int j = 0; + + long64 maxVal; + if (m_UseFullRange) maxVal = hiRange; else maxVal = loRange; + + for (int i = 0; i < len; ++i) + { + if (Abs(pg[i].X) > maxVal || Abs(pg[i].Y) > maxVal) + { + if (m_UseFullRange) + throw "Coordinate exceeds range bounds"; + maxVal = hiRange; + if (Abs(pg[i].X) > maxVal || Abs(pg[i].Y) > maxVal) + throw "Coordinate exceeds range bounds"; + m_UseFullRange = true; + } + + if (i == 0 || PointsEqual(p[j], pg[i])) continue; + else if (j > 0 && SlopesEqual(p[j-1], p[j], pg[i], m_UseFullRange)) + { + if (PointsEqual(p[j-1], pg[i])) j--; + } else j++; + p[j] = pg[i]; + } + if (j < 2) return false; + + len = j+1; + for (;;) + { + //nb: test for point equality before testing slopes ... + if (PointsEqual(p[j], p[0])) j--; + else if (PointsEqual(p[0], p[1]) || + SlopesEqual(p[j], p[0], p[1], m_UseFullRange)) + p[0] = p[j--]; + else if (SlopesEqual(p[j-1], p[j], p[0], m_UseFullRange)) j--; + else if (SlopesEqual(p[0], p[1], p[2], m_UseFullRange)) + { + for (int i = 2; i <= j; ++i) p[i-1] = p[i]; + j--; + } + //exit loop if nothing is changed or there are too few vertices ... + if (j == len-1 || j < 2) break; + len = j +1; + } + if (len < 3) return false; + + //create a new edge array ... + TEdge *edges = new TEdge [len]; + m_edges.push_back(edges); + + //convert vertices to a double-linked-list of edges and initialize ... + edges[0].xcurr = p[0].X; + edges[0].ycurr = p[0].Y; + InitEdge(&edges[len-1], &edges[0], &edges[len-2], p[len-1], polyType); + for (int i = len-2; i > 0; --i) + InitEdge(&edges[i], &edges[i+1], &edges[i-1], p[i], polyType); + InitEdge(&edges[0], &edges[1], &edges[len-1], p[0], polyType); + + //reset xcurr & ycurr and find 'eHighest' (given the Y axis coordinates + //increase downward so the 'highest' edge will have the smallest ytop) ... + TEdge *e = &edges[0]; + TEdge *eHighest = e; + do + { + e->xcurr = e->xbot; + e->ycurr = e->ybot; + if (e->ytop < eHighest->ytop) eHighest = e; + e = e->next; + } + while ( e != &edges[0]); + + //make sure eHighest is positioned so the following loop works safely ... + if (eHighest->windDelta > 0) eHighest = eHighest->next; + if (NEAR_EQUAL(eHighest->dx, HORIZONTAL)) eHighest = eHighest->next; + + //finally insert each local minima ... + e = eHighest; + do { + e = AddBoundsToLML(e); + } + while( e != eHighest ); + return true; +} +//------------------------------------------------------------------------------ + +void ClipperBase::InsertLocalMinima(LocalMinima *newLm) +{ + if( ! m_MinimaList ) + { + m_MinimaList = newLm; + } + else if( newLm->Y >= m_MinimaList->Y ) + { + newLm->next = m_MinimaList; + m_MinimaList = newLm; + } else + { + LocalMinima* tmpLm = m_MinimaList; + while( tmpLm->next && ( newLm->Y < tmpLm->next->Y ) ) + tmpLm = tmpLm->next; + newLm->next = tmpLm->next; + tmpLm->next = newLm; + } +} +//------------------------------------------------------------------------------ + +TEdge* ClipperBase::AddBoundsToLML(TEdge *e) +{ + //Starting at the top of one bound we progress to the bottom where there's + //a local minima. We then go to the top of the next bound. These two bounds + //form the left and right (or right and left) bounds of the local minima. + e->nextInLML = 0; + e = e->next; + for (;;) + { + if (NEAR_EQUAL(e->dx, HORIZONTAL)) + { + //nb: proceed through horizontals when approaching from their right, + // but break on horizontal minima if approaching from their left. + // This ensures 'local minima' are always on the left of horizontals. + if (e->next->ytop < e->ytop && e->next->xbot > e->prev->xbot) break; + if (e->xtop != e->prev->xbot) SwapX(*e); + e->nextInLML = e->prev; + } + else if (e->ycurr == e->prev->ycurr) break; + else e->nextInLML = e->prev; + e = e->next; + } + + //e and e.prev are now at a local minima ... + LocalMinima* newLm = new LocalMinima; + newLm->next = 0; + newLm->Y = e->prev->ybot; + + if ( NEAR_EQUAL(e->dx, HORIZONTAL) ) //horizontal edges never start a left bound + { + if (e->xbot != e->prev->xbot) SwapX(*e); + newLm->leftBound = e->prev; + newLm->rightBound = e; + } else if (e->dx < e->prev->dx) + { + newLm->leftBound = e->prev; + newLm->rightBound = e; + } else + { + newLm->leftBound = e; + newLm->rightBound = e->prev; + } + newLm->leftBound->side = esLeft; + newLm->rightBound->side = esRight; + InsertLocalMinima( newLm ); + + for (;;) + { + if ( e->next->ytop == e->ytop && !NEAR_EQUAL(e->next->dx, HORIZONTAL) ) break; + e->nextInLML = e->next; + e = e->next; + if ( NEAR_EQUAL(e->dx, HORIZONTAL) && e->xbot != e->prev->xtop) SwapX(*e); + } + return e->next; +} +//------------------------------------------------------------------------------ + +bool ClipperBase::AddPolygons(const Polygons &ppg, PolyType polyType) +{ + bool result = true; + for (Polygons::size_type i = 0; i < ppg.size(); ++i) + if (AddPolygon(ppg[i], polyType)) result = false; + return result; +} +//------------------------------------------------------------------------------ + +void ClipperBase::Clear() +{ + DisposeLocalMinimaList(); + for (EdgeList::size_type i = 0; i < m_edges.size(); ++i) delete [] m_edges[i]; + m_edges.clear(); + m_UseFullRange = false; +} +//------------------------------------------------------------------------------ + +void ClipperBase::Reset() +{ + m_CurrentLM = m_MinimaList; + if( !m_CurrentLM ) return; //ie nothing to process + + //reset all edges ... + LocalMinima* lm = m_MinimaList; + while( lm ) + { + TEdge* e = lm->leftBound; + while( e ) + { + e->xcurr = e->xbot; + e->ycurr = e->ybot; + e->side = esLeft; + e->outIdx = -1; + e = e->nextInLML; + } + e = lm->rightBound; + while( e ) + { + e->xcurr = e->xbot; + e->ycurr = e->ybot; + e->side = esRight; + e->outIdx = -1; + e = e->nextInLML; + } + lm = lm->next; + } +} +//------------------------------------------------------------------------------ + +void ClipperBase::DisposeLocalMinimaList() +{ + while( m_MinimaList ) + { + LocalMinima* tmpLm = m_MinimaList->next; + delete m_MinimaList; + m_MinimaList = tmpLm; + } + m_CurrentLM = 0; +} +//------------------------------------------------------------------------------ + +void ClipperBase::PopLocalMinima() +{ + if( ! m_CurrentLM ) return; + m_CurrentLM = m_CurrentLM->next; +} +//------------------------------------------------------------------------------ + +IntRect ClipperBase::GetBounds() +{ + IntRect result; + LocalMinima* lm = m_MinimaList; + if (!lm) + { + result.left = result.top = result.right = result.bottom = 0; + return result; + } + result.left = lm->leftBound->xbot; + result.top = lm->leftBound->ybot; + result.right = lm->leftBound->xbot; + result.bottom = lm->leftBound->ybot; + while (lm) + { + if (lm->leftBound->ybot > result.bottom) + result.bottom = lm->leftBound->ybot; + TEdge* e = lm->leftBound; + for (;;) { + TEdge* bottomE = e; + while (e->nextInLML) + { + if (e->xbot < result.left) result.left = e->xbot; + if (e->xbot > result.right) result.right = e->xbot; + e = e->nextInLML; + } + if (e->xbot < result.left) result.left = e->xbot; + if (e->xbot > result.right) result.right = e->xbot; + if (e->xtop < result.left) result.left = e->xtop; + if (e->xtop > result.right) result.right = e->xtop; + if (e->ytop < result.top) result.top = e->ytop; + + if (bottomE == lm->leftBound) e = lm->rightBound; + else break; + } + lm = lm->next; + } + return result; +} + + +//------------------------------------------------------------------------------ +// TClipper methods ... +//------------------------------------------------------------------------------ + +Clipper::Clipper() : ClipperBase() //constructor +{ + m_Scanbeam = 0; + m_ActiveEdges = 0; + m_SortedEdges = 0; + m_IntersectNodes = 0; + m_ExecuteLocked = false; + m_UseFullRange = false; + m_ReverseOutput = false; +} +//------------------------------------------------------------------------------ + +Clipper::~Clipper() //destructor +{ + Clear(); + DisposeScanbeamList(); +} +//------------------------------------------------------------------------------ + +void Clipper::Clear() +{ + if (m_edges.size() == 0) return; //avoids problems with ClipperBase destructor + DisposeAllPolyPts(); + ClipperBase::Clear(); +} +//------------------------------------------------------------------------------ + +void Clipper::DisposeScanbeamList() +{ + while ( m_Scanbeam ) { + Scanbeam* sb2 = m_Scanbeam->next; + delete m_Scanbeam; + m_Scanbeam = sb2; + } +} +//------------------------------------------------------------------------------ + +void Clipper::Reset() +{ + ClipperBase::Reset(); + m_Scanbeam = 0; + m_ActiveEdges = 0; + m_SortedEdges = 0; + LocalMinima* lm = m_MinimaList; + while (lm) + { + InsertScanbeam(lm->Y); + InsertScanbeam(lm->leftBound->ytop); + lm = lm->next; + } +} +//------------------------------------------------------------------------------ + +bool Clipper::Execute(ClipType clipType, Polygons &solution, + PolyFillType subjFillType, PolyFillType clipFillType) +{ + if( m_ExecuteLocked ) return false; + m_ExecuteLocked = true; + solution.resize(0); + m_SubjFillType = subjFillType; + m_ClipFillType = clipFillType; + m_ClipType = clipType; + bool succeeded = ExecuteInternal(false); + if (succeeded) BuildResult(solution); + m_ExecuteLocked = false; + return succeeded; +} +//------------------------------------------------------------------------------ + +bool Clipper::Execute(ClipType clipType, ExPolygons &solution, + PolyFillType subjFillType, PolyFillType clipFillType) +{ + if( m_ExecuteLocked ) return false; + m_ExecuteLocked = true; + solution.resize(0); + m_SubjFillType = subjFillType; + m_ClipFillType = clipFillType; + m_ClipType = clipType; + bool succeeded = ExecuteInternal(true); + if (succeeded) BuildResultEx(solution); + m_ExecuteLocked = false; + return succeeded; +} +//------------------------------------------------------------------------------ + +bool PolySort(OutRec *or1, OutRec *or2) +{ + if (or1 == or2) return false; + if (!or1->pts || !or2->pts) + { + if (or1->pts != or2->pts) + { + if (or1->pts) return true; else return false; + } + else return false; + } + int i1, i2; + if (or1->isHole) + i1 = or1->FirstLeft->idx; else + i1 = or1->idx; + if (or2->isHole) + i2 = or2->FirstLeft->idx; else + i2 = or2->idx; + int result = i1 - i2; + if (result == 0 && (or1->isHole != or2->isHole)) + { + if (or1->isHole) return false; + else return true; + } + else return result < 0; +} +//------------------------------------------------------------------------------ + +OutRec* FindAppendLinkEnd(OutRec *outRec) +{ + while (outRec->AppendLink) outRec = outRec->AppendLink; + return outRec; +} +//------------------------------------------------------------------------------ + +void Clipper::FixHoleLinkage(OutRec *outRec) +{ + OutRec *tmp; + if (outRec->bottomPt) + tmp = m_PolyOuts[outRec->bottomPt->idx]->FirstLeft; + else + tmp = outRec->FirstLeft; + if (outRec == tmp) throw clipperException("HoleLinkage error"); + + if (tmp) + { + if (tmp->AppendLink) tmp = FindAppendLinkEnd(tmp); + if (tmp == outRec) tmp = 0; + else if (tmp->isHole) + { + FixHoleLinkage(tmp); + tmp = tmp->FirstLeft; + } + } + outRec->FirstLeft = tmp; + if (!tmp) outRec->isHole = false; + outRec->AppendLink = 0; +} +//------------------------------------------------------------------------------ + +bool Clipper::ExecuteInternal(bool fixHoleLinkages) +{ + bool succeeded; + try { + Reset(); + if (!m_CurrentLM ) return true; + long64 botY = PopScanbeam(); + do { + InsertLocalMinimaIntoAEL(botY); + ClearHorzJoins(); + ProcessHorizontals(); + long64 topY = PopScanbeam(); + succeeded = ProcessIntersections(botY, topY); + if (!succeeded) break; + ProcessEdgesAtTopOfScanbeam(topY); + botY = topY; + } while( m_Scanbeam ); + } + catch(...) { + succeeded = false; + } + + if (succeeded) + { + //tidy up output polygons and fix orientations where necessary ... + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec *outRec = m_PolyOuts[i]; + if (!outRec->pts) continue; + FixupOutPolygon(*outRec); + if (!outRec->pts) continue; + if (outRec->isHole && fixHoleLinkages) FixHoleLinkage(outRec); + if (outRec->isHole == + (m_ReverseOutput ^ Orientation(outRec, m_UseFullRange))) + ReversePolyPtLinks(*outRec->pts); + } + + JoinCommonEdges(fixHoleLinkages); + if (fixHoleLinkages) + std::sort(m_PolyOuts.begin(), m_PolyOuts.end(), PolySort); + } + + ClearJoins(); + ClearHorzJoins(); + return succeeded; +} +//------------------------------------------------------------------------------ + +void Clipper::InsertScanbeam(const long64 Y) +{ + if( !m_Scanbeam ) + { + m_Scanbeam = new Scanbeam; + m_Scanbeam->next = 0; + m_Scanbeam->Y = Y; + } + else if( Y > m_Scanbeam->Y ) + { + Scanbeam* newSb = new Scanbeam; + newSb->Y = Y; + newSb->next = m_Scanbeam; + m_Scanbeam = newSb; + } else + { + Scanbeam* sb2 = m_Scanbeam; + while( sb2->next && ( Y <= sb2->next->Y ) ) sb2 = sb2->next; + if( Y == sb2->Y ) return; //ie ignores duplicates + Scanbeam* newSb = new Scanbeam; + newSb->Y = Y; + newSb->next = sb2->next; + sb2->next = newSb; + } +} +//------------------------------------------------------------------------------ + +long64 Clipper::PopScanbeam() +{ + long64 Y = m_Scanbeam->Y; + Scanbeam* sb2 = m_Scanbeam; + m_Scanbeam = m_Scanbeam->next; + delete sb2; + return Y; +} +//------------------------------------------------------------------------------ + +void Clipper::DisposeAllPolyPts(){ + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + DisposeOutRec(i); + m_PolyOuts.clear(); +} +//------------------------------------------------------------------------------ + +void Clipper::DisposeOutRec(PolyOutList::size_type index, bool ignorePts) +{ + OutRec *outRec = m_PolyOuts[index]; + if (!ignorePts && outRec->pts) DisposeOutPts(outRec->pts); + delete outRec; + m_PolyOuts[index] = 0; +} +//------------------------------------------------------------------------------ + +void Clipper::SetWindingCount(TEdge &edge) +{ + TEdge *e = edge.prevInAEL; + //find the edge of the same polytype that immediately preceeds 'edge' in AEL + while ( e && e->polyType != edge.polyType ) e = e->prevInAEL; + if ( !e ) + { + edge.windCnt = edge.windDelta; + edge.windCnt2 = 0; + e = m_ActiveEdges; //ie get ready to calc windCnt2 + } else if ( IsEvenOddFillType(edge) ) + { + //EvenOdd filling ... + edge.windCnt = 1; + edge.windCnt2 = e->windCnt2; + e = e->nextInAEL; //ie get ready to calc windCnt2 + } else + { + //nonZero, Positive or Negative filling ... + if ( e->windCnt * e->windDelta < 0 ) + { + if (Abs(e->windCnt) > 1) + { + if (e->windDelta * edge.windDelta < 0) edge.windCnt = e->windCnt; + else edge.windCnt = e->windCnt + edge.windDelta; + } else + edge.windCnt = e->windCnt + e->windDelta + edge.windDelta; + } else + { + if ( Abs(e->windCnt) > 1 && e->windDelta * edge.windDelta < 0) + edge.windCnt = e->windCnt; + else if ( e->windCnt + edge.windDelta == 0 ) + edge.windCnt = e->windCnt; + else edge.windCnt = e->windCnt + edge.windDelta; + } + edge.windCnt2 = e->windCnt2; + e = e->nextInAEL; //ie get ready to calc windCnt2 + } + + //update windCnt2 ... + if ( IsEvenOddAltFillType(edge) ) + { + //EvenOdd filling ... + while ( e != &edge ) + { + edge.windCnt2 = (edge.windCnt2 == 0) ? 1 : 0; + e = e->nextInAEL; + } + } else + { + //nonZero, Positive or Negative filling ... + while ( e != &edge ) + { + edge.windCnt2 += e->windDelta; + e = e->nextInAEL; + } + } +} +//------------------------------------------------------------------------------ + +bool Clipper::IsEvenOddFillType(const TEdge& edge) const +{ + if (edge.polyType == ptSubject) + return m_SubjFillType == pftEvenOdd; else + return m_ClipFillType == pftEvenOdd; +} +//------------------------------------------------------------------------------ + +bool Clipper::IsEvenOddAltFillType(const TEdge& edge) const +{ + if (edge.polyType == ptSubject) + return m_ClipFillType == pftEvenOdd; else + return m_SubjFillType == pftEvenOdd; +} +//------------------------------------------------------------------------------ + +bool Clipper::IsContributing(const TEdge& edge) const +{ + PolyFillType pft, pft2; + if (edge.polyType == ptSubject) + { + pft = m_SubjFillType; + pft2 = m_ClipFillType; + } else + { + pft = m_ClipFillType; + pft2 = m_SubjFillType; + } + + switch(pft) + { + case pftEvenOdd: + case pftNonZero: + if (Abs(edge.windCnt) != 1) return false; + break; + case pftPositive: + if (edge.windCnt != 1) return false; + break; + default: //pftNegative + if (edge.windCnt != -1) return false; + } + + switch(m_ClipType) + { + case ctIntersection: + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.windCnt2 != 0); + case pftPositive: + return (edge.windCnt2 > 0); + default: + return (edge.windCnt2 < 0); + } + case ctUnion: + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.windCnt2 == 0); + case pftPositive: + return (edge.windCnt2 <= 0); + default: + return (edge.windCnt2 >= 0); + } + case ctDifference: + if (edge.polyType == ptSubject) + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.windCnt2 == 0); + case pftPositive: + return (edge.windCnt2 <= 0); + default: + return (edge.windCnt2 >= 0); + } + else + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.windCnt2 != 0); + case pftPositive: + return (edge.windCnt2 > 0); + default: + return (edge.windCnt2 < 0); + } + default: + return true; + } +} +//------------------------------------------------------------------------------ + +void Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt) +{ + if( NEAR_EQUAL(e2->dx, HORIZONTAL) || ( e1->dx > e2->dx ) ) + { + AddOutPt( e1, e2, pt ); + e2->outIdx = e1->outIdx; + e1->side = esLeft; + e2->side = esRight; + } else + { + AddOutPt( e2, e1, pt ); + e1->outIdx = e2->outIdx; + e1->side = esRight; + e2->side = esLeft; + } +} +//------------------------------------------------------------------------------ + +void Clipper::AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt) +{ + AddOutPt( e1, 0, pt ); + if( e1->outIdx == e2->outIdx ) + { + e1->outIdx = -1; + e2->outIdx = -1; + } + else + AppendPolygon( e1, e2 ); +} +//------------------------------------------------------------------------------ + +void Clipper::AddEdgeToSEL(TEdge *edge) +{ + //SEL pointers in PEdge are reused to build a list of horizontal edges. + //However, we don't need to worry about order with horizontal edge processing. + if( !m_SortedEdges ) + { + m_SortedEdges = edge; + edge->prevInSEL = 0; + edge->nextInSEL = 0; + } + else + { + edge->nextInSEL = m_SortedEdges; + edge->prevInSEL = 0; + m_SortedEdges->prevInSEL = edge; + m_SortedEdges = edge; + } +} +//------------------------------------------------------------------------------ + +void Clipper::CopyAELToSEL() +{ + TEdge* e = m_ActiveEdges; + m_SortedEdges = e; + if (!m_ActiveEdges) return; + m_SortedEdges->prevInSEL = 0; + e = e->nextInAEL; + while ( e ) + { + e->prevInSEL = e->prevInAEL; + e->prevInSEL->nextInSEL = e; + e->nextInSEL = 0; + e = e->nextInAEL; + } +} +//------------------------------------------------------------------------------ + +void Clipper::AddJoin(TEdge *e1, TEdge *e2, int e1OutIdx, int e2OutIdx) +{ + JoinRec* jr = new JoinRec; + if (e1OutIdx >= 0) + jr->poly1Idx = e1OutIdx; else + jr->poly1Idx = e1->outIdx; + jr->pt1a = IntPoint(e1->xcurr, e1->ycurr); + jr->pt1b = IntPoint(e1->xtop, e1->ytop); + if (e2OutIdx >= 0) + jr->poly2Idx = e2OutIdx; else + jr->poly2Idx = e2->outIdx; + jr->pt2a = IntPoint(e2->xcurr, e2->ycurr); + jr->pt2b = IntPoint(e2->xtop, e2->ytop); + m_Joins.push_back(jr); +} +//------------------------------------------------------------------------------ + +void Clipper::ClearJoins() +{ + for (JoinList::size_type i = 0; i < m_Joins.size(); i++) + delete m_Joins[i]; + m_Joins.resize(0); +} +//------------------------------------------------------------------------------ + +void Clipper::AddHorzJoin(TEdge *e, int idx) +{ + HorzJoinRec* hj = new HorzJoinRec; + hj->edge = e; + hj->savedIdx = idx; + m_HorizJoins.push_back(hj); +} +//------------------------------------------------------------------------------ + +void Clipper::ClearHorzJoins() +{ + for (HorzJoinList::size_type i = 0; i < m_HorizJoins.size(); i++) + delete m_HorizJoins[i]; + m_HorizJoins.resize(0); +} +//------------------------------------------------------------------------------ + +void Clipper::InsertLocalMinimaIntoAEL( const long64 botY) +{ + while( m_CurrentLM && ( m_CurrentLM->Y == botY ) ) + { + TEdge* lb = m_CurrentLM->leftBound; + TEdge* rb = m_CurrentLM->rightBound; + + InsertEdgeIntoAEL( lb ); + InsertScanbeam( lb->ytop ); + InsertEdgeIntoAEL( rb ); + + if (IsEvenOddFillType(*lb)) + { + lb->windDelta = 1; + rb->windDelta = 1; + } + else + { + rb->windDelta = -lb->windDelta; + } + SetWindingCount( *lb ); + rb->windCnt = lb->windCnt; + rb->windCnt2 = lb->windCnt2; + + if( NEAR_EQUAL(rb->dx, HORIZONTAL) ) + { + //nb: only rightbounds can have a horizontal bottom edge + AddEdgeToSEL( rb ); + InsertScanbeam( rb->nextInLML->ytop ); + } + else + InsertScanbeam( rb->ytop ); + + if( IsContributing(*lb) ) + AddLocalMinPoly( lb, rb, IntPoint(lb->xcurr, m_CurrentLM->Y) ); + + //if output polygons share an edge, they'll need joining later ... + if (lb->outIdx >= 0 && lb->prevInAEL && + lb->prevInAEL->outIdx >= 0 && lb->prevInAEL->xcurr == lb->xbot && + SlopesEqual(*lb, *lb->prevInAEL, m_UseFullRange)) + AddJoin(lb, lb->prevInAEL); + + //if any output polygons share an edge, they'll need joining later ... + if (rb->outIdx >= 0) + { + if (NEAR_EQUAL(rb->dx, HORIZONTAL)) + { + for (HorzJoinList::size_type i = 0; i < m_HorizJoins.size(); ++i) + { + IntPoint pt, pt2; //returned by GetOverlapSegment() but unused here. + HorzJoinRec* hj = m_HorizJoins[i]; + //if horizontals rb and hj.edge overlap, flag for joining later ... + if (GetOverlapSegment(IntPoint(hj->edge->xbot, hj->edge->ybot), + IntPoint(hj->edge->xtop, hj->edge->ytop), + IntPoint(rb->xbot, rb->ybot), + IntPoint(rb->xtop, rb->ytop), pt, pt2)) + AddJoin(hj->edge, rb, hj->savedIdx); + } + } + } + + if( lb->nextInAEL != rb ) + { + if (rb->outIdx >= 0 && rb->prevInAEL->outIdx >= 0 && + SlopesEqual(*rb->prevInAEL, *rb, m_UseFullRange)) + AddJoin(rb, rb->prevInAEL); + + TEdge* e = lb->nextInAEL; + IntPoint pt = IntPoint(lb->xcurr, lb->ycurr); + while( e != rb ) + { + if(!e) throw clipperException("InsertLocalMinimaIntoAEL: missing rightbound!"); + //nb: For calculating winding counts etc, IntersectEdges() assumes + //that param1 will be to the right of param2 ABOVE the intersection ... + IntersectEdges( rb , e , pt , ipNone); //order important here + e = e->nextInAEL; + } + } + PopLocalMinima(); + } +} +//------------------------------------------------------------------------------ + +void Clipper::DeleteFromAEL(TEdge *e) +{ + TEdge* AelPrev = e->prevInAEL; + TEdge* AelNext = e->nextInAEL; + if( !AelPrev && !AelNext && (e != m_ActiveEdges) ) return; //already deleted + if( AelPrev ) AelPrev->nextInAEL = AelNext; + else m_ActiveEdges = AelNext; + if( AelNext ) AelNext->prevInAEL = AelPrev; + e->nextInAEL = 0; + e->prevInAEL = 0; +} +//------------------------------------------------------------------------------ + +void Clipper::DeleteFromSEL(TEdge *e) +{ + TEdge* SelPrev = e->prevInSEL; + TEdge* SelNext = e->nextInSEL; + if( !SelPrev && !SelNext && (e != m_SortedEdges) ) return; //already deleted + if( SelPrev ) SelPrev->nextInSEL = SelNext; + else m_SortedEdges = SelNext; + if( SelNext ) SelNext->prevInSEL = SelPrev; + e->nextInSEL = 0; + e->prevInSEL = 0; +} +//------------------------------------------------------------------------------ + +void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, + const IntPoint &pt, IntersectProtects protects) +{ + //e1 will be to the left of e2 BELOW the intersection. Therefore e1 is before + //e2 in AEL except when e1 is being inserted at the intersection point ... + bool e1stops = !(ipLeft & protects) && !e1->nextInLML && + e1->xtop == pt.X && e1->ytop == pt.Y; + bool e2stops = !(ipRight & protects) && !e2->nextInLML && + e2->xtop == pt.X && e2->ytop == pt.Y; + bool e1Contributing = ( e1->outIdx >= 0 ); + bool e2contributing = ( e2->outIdx >= 0 ); + + //update winding counts... + //assumes that e1 will be to the right of e2 ABOVE the intersection + if ( e1->polyType == e2->polyType ) + { + if ( IsEvenOddFillType( *e1) ) + { + int oldE1WindCnt = e1->windCnt; + e1->windCnt = e2->windCnt; + e2->windCnt = oldE1WindCnt; + } else + { + if (e1->windCnt + e2->windDelta == 0 ) e1->windCnt = -e1->windCnt; + else e1->windCnt += e2->windDelta; + if ( e2->windCnt - e1->windDelta == 0 ) e2->windCnt = -e2->windCnt; + else e2->windCnt -= e1->windDelta; + } + } else + { + if (!IsEvenOddFillType(*e2)) e1->windCnt2 += e2->windDelta; + else e1->windCnt2 = ( e1->windCnt2 == 0 ) ? 1 : 0; + if (!IsEvenOddFillType(*e1)) e2->windCnt2 -= e1->windDelta; + else e2->windCnt2 = ( e2->windCnt2 == 0 ) ? 1 : 0; + } + + PolyFillType e1FillType, e2FillType, e1FillType2, e2FillType2; + if (e1->polyType == ptSubject) + { + e1FillType = m_SubjFillType; + e1FillType2 = m_ClipFillType; + } else + { + e1FillType = m_ClipFillType; + e1FillType2 = m_SubjFillType; + } + if (e2->polyType == ptSubject) + { + e2FillType = m_SubjFillType; + e2FillType2 = m_ClipFillType; + } else + { + e2FillType = m_ClipFillType; + e2FillType2 = m_SubjFillType; + } + + long64 e1Wc, e2Wc; + switch (e1FillType) + { + case pftPositive: e1Wc = e1->windCnt; break; + case pftNegative: e1Wc = -e1->windCnt; break; + default: e1Wc = Abs(e1->windCnt); + } + switch(e2FillType) + { + case pftPositive: e2Wc = e2->windCnt; break; + case pftNegative: e2Wc = -e2->windCnt; break; + default: e2Wc = Abs(e2->windCnt); + } + + if ( e1Contributing && e2contributing ) + { + if ( e1stops || e2stops || + (e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || + (e1->polyType != e2->polyType && m_ClipType != ctXor) ) + AddLocalMaxPoly(e1, e2, pt); + else + DoBothEdges( e1, e2, pt ); + } + else if ( e1Contributing ) + { + if ((e2Wc == 0 || e2Wc == 1) && + (m_ClipType != ctIntersection || + e2->polyType == ptSubject || (e2->windCnt2 != 0))) + DoEdge1(e1, e2, pt); + } + else if ( e2contributing ) + { + if ((e1Wc == 0 || e1Wc == 1) && + (m_ClipType != ctIntersection || + e1->polyType == ptSubject || (e1->windCnt2 != 0))) + DoEdge2(e1, e2, pt); + } + else if ( (e1Wc == 0 || e1Wc == 1) && + (e2Wc == 0 || e2Wc == 1) && !e1stops && !e2stops ) + { + //neither edge is currently contributing ... + + long64 e1Wc2, e2Wc2; + switch (e1FillType2) + { + case pftPositive: e1Wc2 = e1->windCnt2; break; + case pftNegative : e1Wc2 = -e1->windCnt2; break; + default: e1Wc2 = Abs(e1->windCnt2); + } + switch (e2FillType2) + { + case pftPositive: e2Wc2 = e2->windCnt2; break; + case pftNegative: e2Wc2 = -e2->windCnt2; break; + default: e2Wc2 = Abs(e2->windCnt2); + } + + if (e1->polyType != e2->polyType) + AddLocalMinPoly(e1, e2, pt); + else if (e1Wc == 1 && e2Wc == 1) + switch( m_ClipType ) { + case ctIntersection: + if (e1Wc2 > 0 && e2Wc2 > 0) + AddLocalMinPoly(e1, e2, pt); + break; + case ctUnion: + if ( e1Wc2 <= 0 && e2Wc2 <= 0 ) + AddLocalMinPoly(e1, e2, pt); + break; + case ctDifference: + if ((e1->polyType == ptClip && e2->polyType == ptClip && + e1Wc2 > 0 && e2Wc2 > 0) || + (e1->polyType == ptSubject && e2->polyType == ptSubject && + e1Wc2 <= 0 && e2Wc2 <= 0)) + AddLocalMinPoly(e1, e2, pt); + break; + case ctXor: + AddLocalMinPoly(e1, e2, pt); + } + else + SwapSides( *e1, *e2 ); + } + + if( (e1stops != e2stops) && + ( (e1stops && (e1->outIdx >= 0)) || (e2stops && (e2->outIdx >= 0)) ) ) + { + SwapSides( *e1, *e2 ); + SwapPolyIndexes( *e1, *e2 ); + } + + //finally, delete any non-contributing maxima edges ... + if( e1stops ) DeleteFromAEL( e1 ); + if( e2stops ) DeleteFromAEL( e2 ); +} +//------------------------------------------------------------------------------ + +void Clipper::SetHoleState(TEdge *e, OutRec *outRec) +{ + bool isHole = false; + TEdge *e2 = e->prevInAEL; + while (e2) + { + if (e2->outIdx >= 0) + { + isHole = !isHole; + if (! outRec->FirstLeft) + outRec->FirstLeft = m_PolyOuts[e2->outIdx]; + } + e2 = e2->prevInAEL; + } + if (isHole) outRec->isHole = true; +} +//------------------------------------------------------------------------------ + +bool GetNextNonDupOutPt(OutPt* pp, OutPt*& next) +{ + next = pp->next; + while (next != pp && PointsEqual(pp->pt, next->pt)) + next = next->next; + return next != pp; +} +//------------------------------------------------------------------------------ + +bool GetPrevNonDupOutPt(OutPt* pp, OutPt*& prev) +{ + prev = pp->prev; + while (prev != pp && PointsEqual(pp->pt, prev->pt)) + prev = prev->prev; + return prev != pp; +} +//------------------------------------------------------------------------------ + +OutRec* GetLowermostRec(OutRec *outRec1, OutRec *outRec2) +{ + //work out which polygon fragment has the correct hole state ... + OutPt *outPt1 = outRec1->bottomPt; + OutPt *outPt2 = outRec2->bottomPt; + if (outPt1->pt.Y > outPt2->pt.Y) return outRec1; + else if (outPt1->pt.Y < outPt2->pt.Y) return outRec2; + else if (outPt1->pt.X < outPt2->pt.X) return outRec1; + else if (outPt1->pt.X > outPt2->pt.X) return outRec2; + else if (outRec1->bottomE2 == 0) return outRec2; + else if (outRec2->bottomE2 == 0) return outRec1; + else + { + long64 y1 = std::max(outRec1->bottomE1->ybot, outRec1->bottomE2->ybot); + long64 y2 = std::max(outRec2->bottomE1->ybot, outRec2->bottomE2->ybot); + if (y2 == y1 || (y1 > outPt1->pt.Y && y2 > outPt1->pt.Y)) + { + double dx1 = std::max(outRec1->bottomE1->dx, outRec1->bottomE2->dx); + double dx2 = std::max(outRec2->bottomE1->dx, outRec2->bottomE2->dx); + if (dx2 > dx1) return outRec2; else return outRec1; + } + else if (y2 > y1) return outRec2; + else return outRec1; + } +} +//------------------------------------------------------------------------------ + +void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) +{ + //get the start and ends of both output polygons ... + OutRec *outRec1 = m_PolyOuts[e1->outIdx]; + OutRec *outRec2 = m_PolyOuts[e2->outIdx]; + OutRec *holeStateRec = GetLowermostRec(outRec1, outRec2); + + //fixup hole status ... + if (holeStateRec == outRec2) + outRec1->isHole = outRec2->isHole; + else + outRec2->isHole = outRec1->isHole; + + OutPt* p1_lft = outRec1->pts; + OutPt* p1_rt = p1_lft->prev; + OutPt* p2_lft = outRec2->pts; + OutPt* p2_rt = p2_lft->prev; + + EdgeSide side; + //join e2 poly onto e1 poly and delete pointers to e2 ... + if( e1->side == esLeft ) + { + if( e2->side == esLeft ) + { + //z y x a b c + ReversePolyPtLinks(*p2_lft); + p2_lft->next = p1_lft; + p1_lft->prev = p2_lft; + p1_rt->next = p2_rt; + p2_rt->prev = p1_rt; + outRec1->pts = p2_rt; + } else + { + //x y z a b c + p2_rt->next = p1_lft; + p1_lft->prev = p2_rt; + p2_lft->prev = p1_rt; + p1_rt->next = p2_lft; + outRec1->pts = p2_lft; + } + side = esLeft; + } else + { + if( e2->side == esRight ) + { + //a b c z y x + ReversePolyPtLinks( *p2_lft ); + p1_rt->next = p2_rt; + p2_rt->prev = p1_rt; + p2_lft->next = p1_lft; + p1_lft->prev = p2_lft; + } else + { + //a b c x y z + p1_rt->next = p2_lft; + p2_lft->prev = p1_rt; + p1_lft->prev = p2_rt; + p2_rt->next = p1_lft; + } + side = esRight; + } + + if (holeStateRec == outRec2) + { + outRec1->bottomPt = outRec2->bottomPt; + outRec1->bottomPt->idx = outRec1->idx; + outRec1->bottomE1 = outRec2->bottomE1; + outRec1->bottomE2 = outRec2->bottomE2; + + if (outRec2->FirstLeft != outRec1) + outRec1->FirstLeft = outRec2->FirstLeft; + } + outRec2->pts = 0; + outRec2->bottomPt = 0; + outRec2->AppendLink = outRec1; + int OKIdx = e1->outIdx; + int ObsoleteIdx = e2->outIdx; + + e1->outIdx = -1; //nb: safe because we only get here via AddLocalMaxPoly + e2->outIdx = -1; + + TEdge* e = m_ActiveEdges; + while( e ) + { + if( e->outIdx == ObsoleteIdx ) + { + e->outIdx = OKIdx; + e->side = side; + break; + } + e = e->nextInAEL; + } + + for (JoinList::size_type i = 0; i < m_Joins.size(); ++i) + { + if (m_Joins[i]->poly1Idx == ObsoleteIdx) m_Joins[i]->poly1Idx = OKIdx; + if (m_Joins[i]->poly2Idx == ObsoleteIdx) m_Joins[i]->poly2Idx = OKIdx; + } + + for (HorzJoinList::size_type i = 0; i < m_HorizJoins.size(); ++i) + { + if (m_HorizJoins[i]->savedIdx == ObsoleteIdx) + m_HorizJoins[i]->savedIdx = OKIdx; + } + +} +//------------------------------------------------------------------------------ + +OutRec* Clipper::CreateOutRec() +{ + OutRec* result = new OutRec; + result->isHole = false; + result->FirstLeft = 0; + result->AppendLink = 0; + result->pts = 0; + result->bottomPt = 0; + return result; +} +//------------------------------------------------------------------------------ + +void Clipper::AddOutPt(TEdge *e, TEdge *altE, const IntPoint &pt) +{ + bool ToFront = (e->side == esLeft); + if( e->outIdx < 0 ) + { + OutRec *outRec = CreateOutRec(); + m_PolyOuts.push_back(outRec); + outRec->idx = (int)m_PolyOuts.size()-1; + e->outIdx = outRec->idx; + OutPt* op = new OutPt; + outRec->pts = op; + outRec->bottomE1 = e; + outRec->bottomE2 = altE; + outRec->bottomPt = op; + op->pt = pt; + op->idx = outRec->idx; + op->next = op; + op->prev = op; + SetHoleState(e, outRec); + } else + { + OutRec *outRec = m_PolyOuts[e->outIdx]; + OutPt* op = outRec->pts; + if ((ToFront && PointsEqual(pt, op->pt)) || + (!ToFront && PointsEqual(pt, op->prev->pt))) return; + OutPt* op2 = new OutPt; + op2->pt = pt; + op2->idx = outRec->idx; + if (op2->pt.Y == outRec->bottomPt->pt.Y && + op2->pt.X < outRec->bottomPt->pt.X) + { + outRec->bottomPt = op2; + outRec->bottomE1 = e; + outRec->bottomE2 = altE; + } + op2->next = op; + op2->prev = op->prev; + op2->prev->next = op2; + op->prev = op2; + if (ToFront) outRec->pts = op2; + } +} +//------------------------------------------------------------------------------ + +void Clipper::ProcessHorizontals() +{ + TEdge* horzEdge = m_SortedEdges; + while( horzEdge ) + { + DeleteFromSEL( horzEdge ); + ProcessHorizontal( horzEdge ); + horzEdge = m_SortedEdges; + } +} +//------------------------------------------------------------------------------ + +bool Clipper::IsTopHorz(const long64 XPos) +{ + TEdge* e = m_SortedEdges; + while( e ) + { + if( ( XPos >= std::min(e->xcurr, e->xtop) ) && + ( XPos <= std::max(e->xcurr, e->xtop) ) ) return false; + e = e->nextInSEL; + } + return true; +} +//------------------------------------------------------------------------------ + +bool IsMinima(TEdge *e) +{ + return e && (e->prev->nextInLML != e) && (e->next->nextInLML != e); +} +//------------------------------------------------------------------------------ + +bool IsMaxima(TEdge *e, const long64 Y) +{ + return e && e->ytop == Y && !e->nextInLML; +} +//------------------------------------------------------------------------------ + +bool IsIntermediate(TEdge *e, const long64 Y) +{ + return e->ytop == Y && e->nextInLML; +} +//------------------------------------------------------------------------------ + +TEdge *GetMaximaPair(TEdge *e) +{ + if( !IsMaxima(e->next, e->ytop) || e->next->xtop != e->xtop ) + return e->prev; else + return e->next; +} +//------------------------------------------------------------------------------ + +void Clipper::SwapPositionsInAEL(TEdge *edge1, TEdge *edge2) +{ + if( !edge1->nextInAEL && !edge1->prevInAEL ) return; + if( !edge2->nextInAEL && !edge2->prevInAEL ) return; + + if( edge1->nextInAEL == edge2 ) + { + TEdge* next = edge2->nextInAEL; + if( next ) next->prevInAEL = edge1; + TEdge* prev = edge1->prevInAEL; + if( prev ) prev->nextInAEL = edge2; + edge2->prevInAEL = prev; + edge2->nextInAEL = edge1; + edge1->prevInAEL = edge2; + edge1->nextInAEL = next; + } + else if( edge2->nextInAEL == edge1 ) + { + TEdge* next = edge1->nextInAEL; + if( next ) next->prevInAEL = edge2; + TEdge* prev = edge2->prevInAEL; + if( prev ) prev->nextInAEL = edge1; + edge1->prevInAEL = prev; + edge1->nextInAEL = edge2; + edge2->prevInAEL = edge1; + edge2->nextInAEL = next; + } + else + { + TEdge* next = edge1->nextInAEL; + TEdge* prev = edge1->prevInAEL; + edge1->nextInAEL = edge2->nextInAEL; + if( edge1->nextInAEL ) edge1->nextInAEL->prevInAEL = edge1; + edge1->prevInAEL = edge2->prevInAEL; + if( edge1->prevInAEL ) edge1->prevInAEL->nextInAEL = edge1; + edge2->nextInAEL = next; + if( edge2->nextInAEL ) edge2->nextInAEL->prevInAEL = edge2; + edge2->prevInAEL = prev; + if( edge2->prevInAEL ) edge2->prevInAEL->nextInAEL = edge2; + } + + if( !edge1->prevInAEL ) m_ActiveEdges = edge1; + else if( !edge2->prevInAEL ) m_ActiveEdges = edge2; +} +//------------------------------------------------------------------------------ + +void Clipper::SwapPositionsInSEL(TEdge *edge1, TEdge *edge2) +{ + if( !( edge1->nextInSEL ) && !( edge1->prevInSEL ) ) return; + if( !( edge2->nextInSEL ) && !( edge2->prevInSEL ) ) return; + + if( edge1->nextInSEL == edge2 ) + { + TEdge* next = edge2->nextInSEL; + if( next ) next->prevInSEL = edge1; + TEdge* prev = edge1->prevInSEL; + if( prev ) prev->nextInSEL = edge2; + edge2->prevInSEL = prev; + edge2->nextInSEL = edge1; + edge1->prevInSEL = edge2; + edge1->nextInSEL = next; + } + else if( edge2->nextInSEL == edge1 ) + { + TEdge* next = edge1->nextInSEL; + if( next ) next->prevInSEL = edge2; + TEdge* prev = edge2->prevInSEL; + if( prev ) prev->nextInSEL = edge1; + edge1->prevInSEL = prev; + edge1->nextInSEL = edge2; + edge2->prevInSEL = edge1; + edge2->nextInSEL = next; + } + else + { + TEdge* next = edge1->nextInSEL; + TEdge* prev = edge1->prevInSEL; + edge1->nextInSEL = edge2->nextInSEL; + if( edge1->nextInSEL ) edge1->nextInSEL->prevInSEL = edge1; + edge1->prevInSEL = edge2->prevInSEL; + if( edge1->prevInSEL ) edge1->prevInSEL->nextInSEL = edge1; + edge2->nextInSEL = next; + if( edge2->nextInSEL ) edge2->nextInSEL->prevInSEL = edge2; + edge2->prevInSEL = prev; + if( edge2->prevInSEL ) edge2->prevInSEL->nextInSEL = edge2; + } + + if( !edge1->prevInSEL ) m_SortedEdges = edge1; + else if( !edge2->prevInSEL ) m_SortedEdges = edge2; +} +//------------------------------------------------------------------------------ + +TEdge* GetNextInAEL(TEdge *e, Direction dir) +{ + if( dir == dLeftToRight ) return e->nextInAEL; + else return e->prevInAEL; +} +//------------------------------------------------------------------------------ + +void Clipper::ProcessHorizontal(TEdge *horzEdge) +{ + Direction dir; + long64 horzLeft, horzRight; + + if( horzEdge->xcurr < horzEdge->xtop ) + { + horzLeft = horzEdge->xcurr; + horzRight = horzEdge->xtop; + dir = dLeftToRight; + } else + { + horzLeft = horzEdge->xtop; + horzRight = horzEdge->xcurr; + dir = dRightToLeft; + } + + TEdge* eMaxPair; + if( horzEdge->nextInLML ) eMaxPair = 0; + else eMaxPair = GetMaximaPair(horzEdge); + + TEdge* e = GetNextInAEL( horzEdge , dir ); + while( e ) + { + TEdge* eNext = GetNextInAEL( e, dir ); + + if (eMaxPair || + ((dir == dLeftToRight) && (e->xcurr <= horzRight)) || + ((dir == dRightToLeft) && (e->xcurr >= horzLeft))) + { + //ok, so far it looks like we're still in range of the horizontal edge + if ( e->xcurr == horzEdge->xtop && !eMaxPair ) + { + if (SlopesEqual(*e, *horzEdge->nextInLML, m_UseFullRange)) + { + //if output polygons share an edge, they'll need joining later ... + if (horzEdge->outIdx >= 0 && e->outIdx >= 0) + AddJoin(horzEdge->nextInLML, e, horzEdge->outIdx); + break; //we've reached the end of the horizontal line + } + else if (e->dx < horzEdge->nextInLML->dx) + //we really have got to the end of the intermediate horz edge so quit. + //nb: More -ve slopes follow more +ve slopes ABOVE the horizontal. + break; + } + + if( e == eMaxPair ) + { + //horzEdge is evidently a maxima horizontal and we've arrived at its end. + if (dir == dLeftToRight) + IntersectEdges(horzEdge, e, IntPoint(e->xcurr, horzEdge->ycurr), ipNone); + else + IntersectEdges(e, horzEdge, IntPoint(e->xcurr, horzEdge->ycurr), ipNone); + if (eMaxPair->outIdx >= 0) throw clipperException("ProcessHorizontal error"); + return; + } + else if( NEAR_EQUAL(e->dx, HORIZONTAL) && !IsMinima(e) && !(e->xcurr > e->xtop) ) + { + //An overlapping horizontal edge. Overlapping horizontal edges are + //processed as if layered with the current horizontal edge (horizEdge) + //being infinitesimally lower that the next (e). Therfore, we + //intersect with e only if e.xcurr is within the bounds of horzEdge ... + if( dir == dLeftToRight ) + IntersectEdges( horzEdge , e, IntPoint(e->xcurr, horzEdge->ycurr), + (IsTopHorz( e->xcurr ))? ipLeft : ipBoth ); + else + IntersectEdges( e, horzEdge, IntPoint(e->xcurr, horzEdge->ycurr), + (IsTopHorz( e->xcurr ))? ipRight : ipBoth ); + } + else if( dir == dLeftToRight ) + { + IntersectEdges( horzEdge, e, IntPoint(e->xcurr, horzEdge->ycurr), + (IsTopHorz( e->xcurr ))? ipLeft : ipBoth ); + } + else + { + IntersectEdges( e, horzEdge, IntPoint(e->xcurr, horzEdge->ycurr), + (IsTopHorz( e->xcurr ))? ipRight : ipBoth ); + } + SwapPositionsInAEL( horzEdge, e ); + } + else if( (dir == dLeftToRight && e->xcurr > horzRight && m_SortedEdges) || + (dir == dRightToLeft && e->xcurr < horzLeft && m_SortedEdges) ) break; + e = eNext; + } //end while + + if( horzEdge->nextInLML ) + { + if( horzEdge->outIdx >= 0 ) + AddOutPt( horzEdge, 0, IntPoint(horzEdge->xtop, horzEdge->ytop)); + UpdateEdgeIntoAEL( horzEdge ); + } + else + { + if ( horzEdge->outIdx >= 0 ) + IntersectEdges( horzEdge, eMaxPair, + IntPoint(horzEdge->xtop, horzEdge->ycurr), ipBoth); + if (eMaxPair->outIdx >= 0) throw clipperException("ProcessHorizontal error"); + DeleteFromAEL(eMaxPair); + DeleteFromAEL(horzEdge); + } +} +//------------------------------------------------------------------------------ + +void Clipper::UpdateEdgeIntoAEL(TEdge *&e) +{ + if( !e->nextInLML ) throw + clipperException("UpdateEdgeIntoAEL: invalid call"); + TEdge* AelPrev = e->prevInAEL; + TEdge* AelNext = e->nextInAEL; + e->nextInLML->outIdx = e->outIdx; + if( AelPrev ) AelPrev->nextInAEL = e->nextInLML; + else m_ActiveEdges = e->nextInLML; + if( AelNext ) AelNext->prevInAEL = e->nextInLML; + e->nextInLML->side = e->side; + e->nextInLML->windDelta = e->windDelta; + e->nextInLML->windCnt = e->windCnt; + e->nextInLML->windCnt2 = e->windCnt2; + e = e->nextInLML; + e->prevInAEL = AelPrev; + e->nextInAEL = AelNext; + if( !NEAR_EQUAL(e->dx, HORIZONTAL) ) InsertScanbeam( e->ytop ); +} +//------------------------------------------------------------------------------ + +bool Clipper::ProcessIntersections(const long64 botY, const long64 topY) +{ + if( !m_ActiveEdges ) return true; + try { + BuildIntersectList(botY, topY); + if ( !m_IntersectNodes) return true; + if ( FixupIntersections() ) ProcessIntersectList(); + else return false; + } + catch(...) { + m_SortedEdges = 0; + DisposeIntersectNodes(); + throw clipperException("ProcessIntersections error"); + } + return true; +} +//------------------------------------------------------------------------------ + +void Clipper::DisposeIntersectNodes() +{ + while ( m_IntersectNodes ) + { + IntersectNode* iNode = m_IntersectNodes->next; + delete m_IntersectNodes; + m_IntersectNodes = iNode; + } +} +//------------------------------------------------------------------------------ + +void Clipper::BuildIntersectList(const long64 botY, const long64 topY) +{ + if ( !m_ActiveEdges ) return; + + //prepare for sorting ... + TEdge* e = m_ActiveEdges; + e->tmpX = TopX( *e, topY ); + m_SortedEdges = e; + m_SortedEdges->prevInSEL = 0; + e = e->nextInAEL; + while( e ) + { + e->prevInSEL = e->prevInAEL; + e->prevInSEL->nextInSEL = e; + e->nextInSEL = 0; + e->tmpX = TopX( *e, topY ); + e = e->nextInAEL; + } + + //bubblesort ... + bool isModified = true; + while( isModified && m_SortedEdges ) + { + isModified = false; + e = m_SortedEdges; + while( e->nextInSEL ) + { + TEdge *eNext = e->nextInSEL; + IntPoint pt; + if(e->tmpX > eNext->tmpX && + IntersectPoint(*e, *eNext, pt, m_UseFullRange)) + { + if (pt.Y > botY) + { + pt.Y = botY; + pt.X = TopX(*e, pt.Y); + } + AddIntersectNode( e, eNext, pt ); + SwapPositionsInSEL(e, eNext); + isModified = true; + } + else + e = eNext; + } + if( e->prevInSEL ) e->prevInSEL->nextInSEL = 0; + else break; + } + m_SortedEdges = 0; +} +//------------------------------------------------------------------------------ + +bool Process1Before2(IntersectNode &node1, IntersectNode &node2) +{ + bool result; + if (node1.pt.Y == node2.pt.Y) + { + if (node1.edge1 == node2.edge1 || node1.edge2 == node2.edge1) + { + result = node2.pt.X > node1.pt.X; + if (node2.edge1->dx > 0) return !result; else return result; + } + else if (node1.edge1 == node2.edge2 || node1.edge2 == node2.edge2) + { + result = node2.pt.X > node1.pt.X; + if (node2.edge2->dx > 0) return !result; else return result; + } + else return node2.pt.X > node1.pt.X; + } + else return node1.pt.Y > node2.pt.Y; +} +//------------------------------------------------------------------------------ + +void Clipper::AddIntersectNode(TEdge *e1, TEdge *e2, const IntPoint &pt) +{ + IntersectNode* newNode = new IntersectNode; + newNode->edge1 = e1; + newNode->edge2 = e2; + newNode->pt = pt; + newNode->next = 0; + if( !m_IntersectNodes ) m_IntersectNodes = newNode; + else if( Process1Before2(*newNode, *m_IntersectNodes) ) + { + newNode->next = m_IntersectNodes; + m_IntersectNodes = newNode; + } + else + { + IntersectNode* iNode = m_IntersectNodes; + while( iNode->next && Process1Before2(*iNode->next, *newNode) ) + iNode = iNode->next; + newNode->next = iNode->next; + iNode->next = newNode; + } +} +//------------------------------------------------------------------------------ + +void Clipper::ProcessIntersectList() +{ + while( m_IntersectNodes ) + { + IntersectNode* iNode = m_IntersectNodes->next; + { + IntersectEdges( m_IntersectNodes->edge1 , + m_IntersectNodes->edge2 , m_IntersectNodes->pt, ipBoth ); + SwapPositionsInAEL( m_IntersectNodes->edge1 , m_IntersectNodes->edge2 ); + } + delete m_IntersectNodes; + m_IntersectNodes = iNode; + } +} +//------------------------------------------------------------------------------ + +void Clipper::DoMaxima(TEdge *e, long64 topY) +{ + TEdge* eMaxPair = GetMaximaPair(e); + long64 X = e->xtop; + TEdge* eNext = e->nextInAEL; + while( eNext != eMaxPair ) + { + if (!eNext) throw clipperException("DoMaxima error"); + IntersectEdges( e, eNext, IntPoint(X, topY), ipBoth ); + eNext = eNext->nextInAEL; + } + if( e->outIdx < 0 && eMaxPair->outIdx < 0 ) + { + DeleteFromAEL( e ); + DeleteFromAEL( eMaxPair ); + } + else if( e->outIdx >= 0 && eMaxPair->outIdx >= 0 ) + { + IntersectEdges( e, eMaxPair, IntPoint(X, topY), ipNone ); + } + else throw clipperException("DoMaxima error"); +} +//------------------------------------------------------------------------------ + +void Clipper::ProcessEdgesAtTopOfScanbeam(const long64 topY) +{ + TEdge* e = m_ActiveEdges; + while( e ) + { + //1. process maxima, treating them as if they're 'bent' horizontal edges, + // but exclude maxima with horizontal edges. nb: e can't be a horizontal. + if( IsMaxima(e, topY) && !NEAR_EQUAL(GetMaximaPair(e)->dx, HORIZONTAL) ) + { + //'e' might be removed from AEL, as may any following edges so ... + TEdge* ePrior = e->prevInAEL; + DoMaxima(e, topY); + if( !ePrior ) e = m_ActiveEdges; + else e = ePrior->nextInAEL; + } + else + { + //2. promote horizontal edges, otherwise update xcurr and ycurr ... + if( IsIntermediate(e, topY) && NEAR_EQUAL(e->nextInLML->dx, HORIZONTAL) ) + { + if (e->outIdx >= 0) + { + AddOutPt(e, 0, IntPoint(e->xtop, e->ytop)); + + for (HorzJoinList::size_type i = 0; i < m_HorizJoins.size(); ++i) + { + IntPoint pt, pt2; + HorzJoinRec* hj = m_HorizJoins[i]; + if (GetOverlapSegment(IntPoint(hj->edge->xbot, hj->edge->ybot), + IntPoint(hj->edge->xtop, hj->edge->ytop), + IntPoint(e->nextInLML->xbot, e->nextInLML->ybot), + IntPoint(e->nextInLML->xtop, e->nextInLML->ytop), pt, pt2)) + AddJoin(hj->edge, e->nextInLML, hj->savedIdx, e->outIdx); + } + + AddHorzJoin(e->nextInLML, e->outIdx); + } + UpdateEdgeIntoAEL(e); + AddEdgeToSEL(e); + } else + { + //this just simplifies horizontal processing ... + e->xcurr = TopX( *e, topY ); + e->ycurr = topY; + } + e = e->nextInAEL; + } + } + + //3. Process horizontals at the top of the scanbeam ... + ProcessHorizontals(); + + //4. Promote intermediate vertices ... + e = m_ActiveEdges; + while( e ) + { + if( IsIntermediate( e, topY ) ) + { + if( e->outIdx >= 0 ) AddOutPt(e, 0, IntPoint(e->xtop,e->ytop)); + UpdateEdgeIntoAEL(e); + + //if output polygons share an edge, they'll need joining later ... + if (e->outIdx >= 0 && e->prevInAEL && e->prevInAEL->outIdx >= 0 && + e->prevInAEL->xcurr == e->xbot && e->prevInAEL->ycurr == e->ybot && + SlopesEqual(IntPoint(e->xbot,e->ybot), IntPoint(e->xtop, e->ytop), + IntPoint(e->xbot,e->ybot), + IntPoint(e->prevInAEL->xtop, e->prevInAEL->ytop), m_UseFullRange)) + { + AddOutPt(e->prevInAEL, 0, IntPoint(e->xbot, e->ybot)); + AddJoin(e, e->prevInAEL); + } + else if (e->outIdx >= 0 && e->nextInAEL && e->nextInAEL->outIdx >= 0 && + e->nextInAEL->ycurr > e->nextInAEL->ytop && + e->nextInAEL->ycurr < e->nextInAEL->ybot && + e->nextInAEL->xcurr == e->xbot && e->nextInAEL->ycurr == e->ybot && + SlopesEqual(IntPoint(e->xbot,e->ybot), IntPoint(e->xtop, e->ytop), + IntPoint(e->xbot,e->ybot), + IntPoint(e->nextInAEL->xtop, e->nextInAEL->ytop), m_UseFullRange)) + { + AddOutPt(e->nextInAEL, 0, IntPoint(e->xbot, e->ybot)); + AddJoin(e, e->nextInAEL); + } + } + e = e->nextInAEL; + } +} +//------------------------------------------------------------------------------ + +void Clipper::FixupOutPolygon(OutRec &outRec) +{ + //FixupOutPolygon() - removes duplicate points and simplifies consecutive + //parallel edges by removing the middle vertex. + OutPt *lastOK = 0; + outRec.pts = outRec.bottomPt; + OutPt *pp = outRec.bottomPt; + + for (;;) + { + if (pp->prev == pp || pp->prev == pp->next ) + { + DisposeOutPts(pp); + outRec.pts = 0; + outRec.bottomPt = 0; + return; + } + //test for duplicate points and for same slope (cross-product) ... + if ( PointsEqual(pp->pt, pp->next->pt) || + SlopesEqual(pp->prev->pt, pp->pt, pp->next->pt, m_UseFullRange) ) + { + lastOK = 0; + OutPt *tmp = pp; + if (pp == outRec.bottomPt) + { + if (tmp->prev->pt.Y > tmp->next->pt.Y) + outRec.bottomPt = tmp->prev; else + outRec.bottomPt = tmp->next; + outRec.pts = outRec.bottomPt; + outRec.bottomPt->idx = outRec.idx; + } + pp->prev->next = pp->next; + pp->next->prev = pp->prev; + pp = pp->prev; + delete tmp; + } + else if (pp == lastOK) break; + else + { + if (!lastOK) lastOK = pp; + pp = pp->next; + } + } +} +//------------------------------------------------------------------------------ + +void Clipper::BuildResult(Polygons &polys) +{ + int k = 0; + polys.resize(m_PolyOuts.size()); + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + if (m_PolyOuts[i]->pts) + { + Polygon* pg = &polys[k]; + pg->clear(); + OutPt* p = m_PolyOuts[i]->pts; + do + { + pg->push_back(p->pt); + p = p->next; + } while (p != m_PolyOuts[i]->pts); + //make sure each polygon has at least 3 vertices ... + if (pg->size() < 3) pg->clear(); else k++; + } + } + polys.resize(k); +} +//------------------------------------------------------------------------------ + +void Clipper::BuildResultEx(ExPolygons &polys) +{ + PolyOutList::size_type i = 0; + int k = 0; + polys.resize(0); + polys.reserve(m_PolyOuts.size()); + while (i < m_PolyOuts.size() && m_PolyOuts[i]->pts) + { + ExPolygon epg; + OutPt* p = m_PolyOuts[i]->pts; + do { + epg.outer.push_back(p->pt); + p = p->next; + } while (p != m_PolyOuts[i]->pts); + i++; + //make sure polygons have at least 3 vertices ... + if (epg.outer.size() < 3) continue; + while (i < m_PolyOuts.size() + && m_PolyOuts[i]->pts && m_PolyOuts[i]->isHole) + { + Polygon pg; + p = m_PolyOuts[i]->pts; + do { + pg.push_back(p->pt); + p = p->next; + } while (p != m_PolyOuts[i]->pts); + epg.holes.push_back(pg); + i++; + } + polys.push_back(epg); + k++; + } + polys.resize(k); +} +//------------------------------------------------------------------------------ + +void SwapIntersectNodes(IntersectNode &int1, IntersectNode &int2) +{ + TEdge *e1 = int1.edge1; + TEdge *e2 = int1.edge2; + IntPoint p = int1.pt; + + int1.edge1 = int2.edge1; + int1.edge2 = int2.edge2; + int1.pt = int2.pt; + + int2.edge1 = e1; + int2.edge2 = e2; + int2.pt = p; +} +//------------------------------------------------------------------------------ + +bool Clipper::FixupIntersections() +{ + if ( !m_IntersectNodes->next ) return true; + + CopyAELToSEL(); + IntersectNode *int1 = m_IntersectNodes; + IntersectNode *int2 = m_IntersectNodes->next; + while (int2) + { + TEdge *e1 = int1->edge1; + TEdge *e2; + if (e1->prevInSEL == int1->edge2) e2 = e1->prevInSEL; + else if (e1->nextInSEL == int1->edge2) e2 = e1->nextInSEL; + else + { + //The current intersection is out of order, so try and swap it with + //a subsequent intersection ... + while (int2) + { + if (int2->edge1->nextInSEL == int2->edge2 || + int2->edge1->prevInSEL == int2->edge2) break; + else int2 = int2->next; + } + if ( !int2 ) return false; //oops!!! + + //found an intersect node that can be swapped ... + SwapIntersectNodes(*int1, *int2); + e1 = int1->edge1; + e2 = int1->edge2; + } + SwapPositionsInSEL(e1, e2); + int1 = int1->next; + int2 = int1->next; + } + + m_SortedEdges = 0; + + //finally, check the last intersection too ... + return (int1->edge1->prevInSEL == int1->edge2 || + int1->edge1->nextInSEL == int1->edge2); +} +//------------------------------------------------------------------------------ + +bool E2InsertsBeforeE1(TEdge &e1, TEdge &e2) +{ + if (e2.xcurr == e1.xcurr) return e2.dx > e1.dx; + else return e2.xcurr < e1.xcurr; +} +//------------------------------------------------------------------------------ + +void Clipper::InsertEdgeIntoAEL(TEdge *edge) +{ + edge->prevInAEL = 0; + edge->nextInAEL = 0; + if( !m_ActiveEdges ) + { + m_ActiveEdges = edge; + } + else if( E2InsertsBeforeE1(*m_ActiveEdges, *edge) ) + { + edge->nextInAEL = m_ActiveEdges; + m_ActiveEdges->prevInAEL = edge; + m_ActiveEdges = edge; + } else + { + TEdge* e = m_ActiveEdges; + while( e->nextInAEL && !E2InsertsBeforeE1(*e->nextInAEL , *edge) ) + e = e->nextInAEL; + edge->nextInAEL = e->nextInAEL; + if( e->nextInAEL ) e->nextInAEL->prevInAEL = edge; + edge->prevInAEL = e; + e->nextInAEL = edge; + } +} +//---------------------------------------------------------------------- + +void Clipper::DoEdge1(TEdge *edge1, TEdge *edge2, const IntPoint &pt) +{ + AddOutPt(edge1, edge2, pt); + SwapSides(*edge1, *edge2); + SwapPolyIndexes(*edge1, *edge2); +} +//---------------------------------------------------------------------- + +void Clipper::DoEdge2(TEdge *edge1, TEdge *edge2, const IntPoint &pt) +{ + AddOutPt(edge2, edge1, pt); + SwapSides(*edge1, *edge2); + SwapPolyIndexes(*edge1, *edge2); +} +//---------------------------------------------------------------------- + +void Clipper::DoBothEdges(TEdge *edge1, TEdge *edge2, const IntPoint &pt) +{ + AddOutPt(edge1, edge2, pt); + AddOutPt(edge2, edge1, pt); + SwapSides( *edge1 , *edge2 ); + SwapPolyIndexes( *edge1 , *edge2 ); +} +//---------------------------------------------------------------------- + +void Clipper::CheckHoleLinkages1(OutRec *outRec1, OutRec *outRec2) +{ + //when a polygon is split into 2 polygons, make sure any holes the original + //polygon contained link to the correct polygon ... + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec *orec = m_PolyOuts[i]; + if (orec->isHole && orec->bottomPt && orec->FirstLeft == outRec1 && + !PointInPolygon(orec->bottomPt->pt, outRec1->pts, m_UseFullRange)) + orec->FirstLeft = outRec2; + } +} +//---------------------------------------------------------------------- + +void Clipper::CheckHoleLinkages2(OutRec *outRec1, OutRec *outRec2) +{ + //if a hole is owned by outRec2 then make it owned by outRec1 ... + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + if (m_PolyOuts[i]->isHole && m_PolyOuts[i]->bottomPt && + m_PolyOuts[i]->FirstLeft == outRec2) + m_PolyOuts[i]->FirstLeft = outRec1; +} +//---------------------------------------------------------------------- + +void Clipper::JoinCommonEdges(bool fixHoleLinkages) +{ + for (JoinList::size_type i = 0; i < m_Joins.size(); i++) + { + JoinRec* j = m_Joins[i]; + OutRec *outRec1 = m_PolyOuts[j->poly1Idx]; + OutPt *pp1a = outRec1->pts; + OutRec *outRec2 = m_PolyOuts[j->poly2Idx]; + OutPt *pp2a = outRec2->pts; + IntPoint pt1 = j->pt2a, pt2 = j->pt2b; + IntPoint pt3 = j->pt1a, pt4 = j->pt1b; + if (!FindSegment(pp1a, pt1, pt2)) continue; + if (j->poly1Idx == j->poly2Idx) + { + //we're searching the same polygon for overlapping segments so + //segment 2 mustn't be the same as segment 1 ... + pp2a = pp1a->next; + if (!FindSegment(pp2a, pt3, pt4) || (pp2a == pp1a)) continue; + } + else if (!FindSegment(pp2a, pt3, pt4)) continue; + + if (!GetOverlapSegment(pt1, pt2, pt3, pt4, pt1, pt2)) continue; + + OutPt *p1, *p2, *p3, *p4; + OutPt *prev = pp1a->prev; + //get p1 & p2 polypts - the overlap start & endpoints on poly1 + if (PointsEqual(pp1a->pt, pt1)) p1 = pp1a; + else if (PointsEqual(prev->pt, pt1)) p1 = prev; + else p1 = InsertPolyPtBetween(pp1a, prev, pt1); + + if (PointsEqual(pp1a->pt, pt2)) p2 = pp1a; + else if (PointsEqual(prev->pt, pt2)) p2 = prev; + else if ((p1 == pp1a) || (p1 == prev)) + p2 = InsertPolyPtBetween(pp1a, prev, pt2); + else if (Pt3IsBetweenPt1AndPt2(pp1a->pt, p1->pt, pt2)) + p2 = InsertPolyPtBetween(pp1a, p1, pt2); else + p2 = InsertPolyPtBetween(p1, prev, pt2); + + //get p3 & p4 polypts - the overlap start & endpoints on poly2 + prev = pp2a->prev; + if (PointsEqual(pp2a->pt, pt1)) p3 = pp2a; + else if (PointsEqual(prev->pt, pt1)) p3 = prev; + else p3 = InsertPolyPtBetween(pp2a, prev, pt1); + + if (PointsEqual(pp2a->pt, pt2)) p4 = pp2a; + else if (PointsEqual(prev->pt, pt2)) p4 = prev; + else if ((p3 == pp2a) || (p3 == prev)) + p4 = InsertPolyPtBetween(pp2a, prev, pt2); + else if (Pt3IsBetweenPt1AndPt2(pp2a->pt, p3->pt, pt2)) + p4 = InsertPolyPtBetween(pp2a, p3, pt2); else + p4 = InsertPolyPtBetween(p3, prev, pt2); + + //p1.pt == p3.pt and p2.pt == p4.pt so join p1 to p3 and p2 to p4 ... + if (p1->next == p2 && p3->prev == p4) + { + p1->next = p3; + p3->prev = p1; + p2->prev = p4; + p4->next = p2; + } + else if (p1->prev == p2 && p3->next == p4) + { + p1->prev = p3; + p3->next = p1; + p2->next = p4; + p4->prev = p2; + } + else + continue; //an orientation is probably wrong + + if (j->poly2Idx == j->poly1Idx) + { + //instead of joining two polygons, we've just created a new one by + //splitting one polygon into two. + outRec1->pts = PolygonBottom(p1); + outRec1->bottomPt = outRec1->pts; + outRec1->bottomPt->idx = outRec1->idx; + outRec2 = CreateOutRec(); + m_PolyOuts.push_back(outRec2); + outRec2->idx = (int)m_PolyOuts.size()-1; + j->poly2Idx = outRec2->idx; + outRec2->pts = PolygonBottom(p2); + outRec2->bottomPt = outRec2->pts; + outRec2->bottomPt->idx = outRec2->idx; + + if (PointInPolygon(outRec2->pts->pt, outRec1->pts, m_UseFullRange)) + { + outRec2->isHole = !outRec1->isHole; + outRec2->FirstLeft = outRec1; + if (outRec2->isHole == Orientation(outRec2, m_UseFullRange)) + ReversePolyPtLinks(*outRec2->pts); + } else if (PointInPolygon(outRec1->pts->pt, outRec2->pts, m_UseFullRange)) + { + outRec2->isHole = outRec1->isHole; + outRec1->isHole = !outRec2->isHole; + outRec2->FirstLeft = outRec1->FirstLeft; + outRec1->FirstLeft = outRec2; + if (outRec1->isHole == Orientation(outRec1, m_UseFullRange)) + ReversePolyPtLinks(*outRec1->pts); + } else + { + outRec2->isHole = outRec1->isHole; + outRec2->FirstLeft = outRec1->FirstLeft; + //make sure any contained holes now link to the correct polygon ... + if (fixHoleLinkages) CheckHoleLinkages1(outRec1, outRec2); + } + + //now fixup any subsequent joins that match this polygon + for (JoinList::size_type k = i+1; k < m_Joins.size(); k++) + { + JoinRec* j2 = m_Joins[k]; + if (j2->poly1Idx == j->poly1Idx && PointIsVertex(j2->pt1a, p2)) + j2->poly1Idx = j->poly2Idx; + if (j2->poly2Idx == j->poly1Idx && PointIsVertex(j2->pt2a, p2)) + j2->poly2Idx = j->poly2Idx; + } + + //now cleanup redundant edges too ... + FixupOutPolygon(*outRec1); + FixupOutPolygon(*outRec2); + } else + { + //joined 2 polygons together ... + + //make sure any holes contained by outRec2 now link to outRec1 ... + if (fixHoleLinkages) CheckHoleLinkages2(outRec1, outRec2); + + //delete the obsolete pointer ... + int OKIdx = outRec1->idx; + int ObsoleteIdx = outRec2->idx; + outRec2->pts = 0; + outRec2->bottomPt = 0; + outRec2->AppendLink = outRec1; + //holes are practically always joined to outers, not vice versa ... + if (outRec1->isHole && !outRec2->isHole) outRec1->isHole = false; + + //now fixup any subsequent Joins that match this polygon + for (JoinList::size_type k = i+1; k < m_Joins.size(); k++) + { + JoinRec* j2 = m_Joins[k]; + if (j2->poly1Idx == ObsoleteIdx) j2->poly1Idx = OKIdx; + if (j2->poly2Idx == ObsoleteIdx) j2->poly2Idx = OKIdx; + } + + //now cleanup redundant edges too ... + if (outRec1->pts) + FixupOutPolygon(*outRec1); + else + FixupOutPolygon(*outRec2); + } + } +} +//------------------------------------------------------------------------------ + +void ReversePoints(Polygon& p) +{ + std::reverse(p.begin(), p.end()); +} +//------------------------------------------------------------------------------ + +void ReversePoints(Polygons& p) +{ + for (Polygons::size_type i = 0; i < p.size(); ++i) + ReversePoints(p[i]); +} + +//------------------------------------------------------------------------------ +// OffsetPolygon functions ... +//------------------------------------------------------------------------------ + +struct DoublePoint +{ + double X; + double Y; + DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {} +}; +//------------------------------------------------------------------------------ + +Polygon BuildArc(const IntPoint &pt, + const double a1, const double a2, const double r) +{ + int steps = std::max(6, int(std::sqrt(std::fabs(r)) * std::fabs(a2 - a1))); + Polygon result(steps); + int n = steps - 1; + double da = (a2 - a1) / n; + double a = a1; + for (int i = 0; i <= n; ++i) + { + result[i].X = pt.X + Round(std::cos(a)*r); + result[i].Y = pt.Y + Round(std::sin(a)*r); + a += da; + } + return result; +} +//------------------------------------------------------------------------------ + +DoublePoint GetUnitNormal( const IntPoint &pt1, const IntPoint &pt2) +{ + if(pt2.X == pt1.X && pt2.Y == pt1.Y) + return DoublePoint(0, 0); + + double dx = (double)(pt2.X - pt1.X); + double dy = (double)(pt2.Y - pt1.Y); + double f = 1 *1.0/ std::sqrt( dx*dx + dy*dy ); + dx *= f; + dy *= f; + return DoublePoint(dy, -dx); +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +class PolyOffsetBuilder +{ +private: + Polygons m_p; + Polygon* m_curr_poly; + std::vector normals; + double m_delta, m_RMin, m_R; + size_t m_i, m_j, m_k; + static const int buffLength = 128; + JoinType m_jointype; + +public: + +PolyOffsetBuilder(const Polygons& in_polys, Polygons& out_polys, + double delta, JoinType jointype, double MiterLimit) +{ + //nb precondition - out_polys != ptsin_polys + if (NEAR_ZERO(delta)) + { + out_polys = in_polys; + return; + } + + this->m_p = in_polys; + this->m_delta = delta; + this->m_jointype = jointype; + if (MiterLimit <= 1) MiterLimit = 1; + m_RMin = 2/(MiterLimit*MiterLimit); + + double deltaSq = delta*delta; + out_polys.clear(); + out_polys.resize(in_polys.size()); + for (m_i = 0; m_i < in_polys.size(); m_i++) + { + m_curr_poly = &out_polys[m_i]; + size_t len = in_polys[m_i].size(); + if (len > 1 && m_p[m_i][0].X == m_p[m_i][len - 1].X && + m_p[m_i][0].Y == m_p[m_i][len-1].Y) len--; + + //when 'shrinking' polygons - to minimize artefacts + //strip those polygons that have an area < pi * delta^2 ... + double a1 = Area(in_polys[m_i]); + if (delta < 0) { if (a1 > 0 && a1 < deltaSq *pi) len = 0; } + else if (a1 < 0 && -a1 < deltaSq *pi) len = 0; //holes have neg. area + + if (len == 0 || (len < 3 && delta <= 0)) + continue; + else if (len == 1) + { + Polygon arc; + arc = BuildArc(in_polys[m_i][len-1], 0, 2 * pi, delta); + out_polys[m_i] = arc; + continue; + } + + //build normals ... + normals.clear(); + normals.resize(len); + normals[len-1] = GetUnitNormal(in_polys[m_i][len-1], in_polys[m_i][0]); + for (m_j = 0; m_j < len -1; ++m_j) + normals[m_j] = GetUnitNormal(in_polys[m_i][m_j], in_polys[m_i][m_j+1]); + + m_k = len -1; + for (m_j = 0; m_j < len; ++m_j) + { + switch (jointype) + { + case jtMiter: + { + m_R = 1 + (normals[m_j].X*normals[m_k].X + + normals[m_j].Y*normals[m_k].Y); + if (m_R >= m_RMin) DoMiter(); else DoSquare(MiterLimit); + break; + } + case jtSquare: DoSquare(); break; + case jtRound: DoRound(); break; + } + m_k = m_j; + } + } + + //finally, clean up untidy corners using Clipper ... + Clipper clpr; + clpr.AddPolygons(out_polys, ptSubject); + if (delta > 0) + { + if (!clpr.Execute(ctUnion, out_polys, pftPositive, pftPositive)) + out_polys.clear(); + } + else + { + IntRect r = clpr.GetBounds(); + Polygon outer(4); + outer[0] = IntPoint(r.left - 10, r.bottom + 10); + outer[1] = IntPoint(r.right + 10, r.bottom + 10); + outer[2] = IntPoint(r.right + 10, r.top - 10); + outer[3] = IntPoint(r.left - 10, r.top - 10); + + clpr.AddPolygon(outer, ptSubject); + if (clpr.Execute(ctUnion, out_polys, pftNegative, pftNegative)) + { + out_polys.erase(out_polys.begin()); + ReversePoints(out_polys); + + } else + out_polys.clear(); + } +} +//------------------------------------------------------------------------------ + +private: + +void AddPoint(const IntPoint& pt) +{ + Polygon::size_type len = m_curr_poly->size(); + if (len == m_curr_poly->capacity()) + m_curr_poly->reserve(len + buffLength); + m_curr_poly->push_back(pt); +} +//------------------------------------------------------------------------------ + +void DoSquare(double mul = 1.0) +{ + IntPoint pt1 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_k].X * m_delta), + (long64)Round(m_p[m_i][m_j].Y + normals[m_k].Y * m_delta)); + IntPoint pt2 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_j].X * m_delta), + (long64)Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta)); + if ((normals[m_k].X * normals[m_j].Y - normals[m_j].X * normals[m_k].Y) * m_delta >= 0) + { + double a1 = std::atan2(normals[m_k].Y, normals[m_k].X); + double a2 = std::atan2(-normals[m_j].Y, -normals[m_j].X); + a1 = std::fabs(a2 - a1); + if (a1 > pi) a1 = pi * 2 - a1; + double dx = std::tan((pi - a1)/4) * std::fabs(m_delta * mul); + pt1 = IntPoint((long64)(pt1.X -normals[m_k].Y * dx), + (long64)(pt1.Y + normals[m_k].X * dx)); + AddPoint(pt1); + pt2 = IntPoint((long64)(pt2.X + normals[m_j].Y * dx), + (long64)(pt2.Y -normals[m_j].X * dx)); + AddPoint(pt2); + } + else + { + AddPoint(pt1); + AddPoint(m_p[m_i][m_j]); + AddPoint(pt2); + } +} +//------------------------------------------------------------------------------ + +void DoMiter() +{ + if ((normals[m_k].X * normals[m_j].Y - normals[m_j].X * normals[m_k].Y) * m_delta >= 0) + { + double q = m_delta / m_R; + AddPoint(IntPoint((long64)Round(m_p[m_i][m_j].X + + (normals[m_k].X + normals[m_j].X) * q), + (long64)Round(m_p[m_i][m_j].Y + (normals[m_k].Y + normals[m_j].Y) * q))); + } + else + { + IntPoint pt1 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_k].X * + m_delta), (long64)Round(m_p[m_i][m_j].Y + normals[m_k].Y * m_delta)); + IntPoint pt2 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_j].X * + m_delta), (long64)Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta)); + AddPoint(pt1); + AddPoint(m_p[m_i][m_j]); + AddPoint(pt2); + } +} +//------------------------------------------------------------------------------ + +void DoRound() +{ + IntPoint pt1 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_k].X * m_delta), + (long64)Round(m_p[m_i][m_j].Y + normals[m_k].Y * m_delta)); + IntPoint pt2 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_j].X * m_delta), + (long64)Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta)); + AddPoint(pt1); + //round off reflex angles (ie > 180 deg) unless almost flat (ie < ~10deg). + if ((normals[m_k].X*normals[m_j].Y - normals[m_j].X*normals[m_k].Y) * m_delta >= 0) + { + if (normals[m_j].X * normals[m_k].X + normals[m_j].Y * normals[m_k].Y < 0.985) + { + double a1 = std::atan2(normals[m_k].Y, normals[m_k].X); + double a2 = std::atan2(normals[m_j].Y, normals[m_j].X); + if (m_delta > 0 && a2 < a1) a2 += pi *2; + else if (m_delta < 0 && a2 > a1) a2 -= pi *2; + Polygon arc = BuildArc(m_p[m_i][m_j], a1, a2, m_delta); + for (Polygon::size_type m = 0; m < arc.size(); m++) + AddPoint(arc[m]); + } + } + else + AddPoint(m_p[m_i][m_j]); + AddPoint(pt2); +} +//-------------------------------------------------------------------------- + +}; //end PolyOffsetBuilder + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +void OffsetPolygons(const Polygons &in_polys, Polygons &out_polys, + double delta, JoinType jointype, double MiterLimit) +{ + if (&out_polys == &in_polys) + { + Polygons poly2(in_polys); + PolyOffsetBuilder(poly2, out_polys, delta, jointype, MiterLimit); + } + else PolyOffsetBuilder(in_polys, out_polys, delta, jointype, MiterLimit); +} +//------------------------------------------------------------------------------ + +std::ostream& operator <<(std::ostream &s, IntPoint& p) +{ + s << p.X << ' ' << p.Y << "\n"; + return s; +} +//------------------------------------------------------------------------------ + +std::ostream& operator <<(std::ostream &s, Polygon &p) +{ + for (Polygon::size_type i = 0; i < p.size(); i++) + s << p[i]; + s << "\n"; + return s; +} +//------------------------------------------------------------------------------ + +std::ostream& operator <<(std::ostream &s, Polygons &p) +{ + for (Polygons::size_type i = 0; i < p.size(); i++) + s << p[i]; + s << "\n"; + return s; +} +//------------------------------------------------------------------------------ + +} //ClipperLib namespace diff --git a/version.rc.in b/version.rc.in index 0b2dfff190..a22c51fa40 100644 --- a/version.rc.in +++ b/version.rc.in @@ -1,53 +1,53 @@ -#include "winres.h" - -#define VER_FILEVERSION @MapServer_VERSION_MAJOR@,@MapServer_VERSION_MINOR@,@MapServer_VERSION_REVISION@,0 -#define VER_FILEVERSION_STR "@MapServer_VERSION_STRING@\0" - -#define VER_PRODUCTVERSION @MapServer_VERSION_MAJOR@,@MapServer_VERSION_MINOR@,@MapServer_VERSION_REVISION@,0 -#define VER_PRODUCTVERSION_STR "@MapServer_VERSION_STRING@\0" - -#ifndef DEBUG -#define VER_DEBUG 0 -#else -#define VER_DEBUG VS_FF_DEBUG -#endif - -VS_VERSION_INFO VERSIONINFO - FILEVERSION VER_FILEVERSION - PRODUCTVERSION VER_PRODUCTVERSION - FILEFLAGSMASK VS_FFI_FILEFLAGSMASK - FILEFLAGS VER_DEBUG - FILEOS VOS__WINDOWS32 - FILETYPE VFT_DLL - FILESUBTYPE VFT2_UNKNOWN -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904E4" - BEGIN - VALUE "CompanyName", "MapServer" - VALUE "FileDescription", "MapServer" - VALUE "FileVersion", VER_FILEVERSION_STR - VALUE "InternalName", "" - VALUE "LegalCopyright", "Copyright (c) 2017 MapServer" - VALUE "LegalTrademarks1", "" - VALUE "LegalTrademarks2", "" - VALUE "OriginalFilename", "" - VALUE "ProductName", "MapServer" - VALUE "ProductVersion", VER_PRODUCTVERSION_STR - END - END - - BLOCK "VarFileInfo" - BEGIN - /* The following line should only be modified for localized versions. */ - /* It consists of any number of WORD,WORD pairs, with each pair */ - /* describing a language,codepage combination supported by the file. */ - /* */ - /* For example, a file might have values "0x409,1252" indicating that it */ - /* supports English language (0x409) in the Windows ANSI codepage (1252). */ - - VALUE "Translation", 0x409, 1252 - - END +#include "winres.h" + +#define VER_FILEVERSION @MapServer_VERSION_MAJOR@,@MapServer_VERSION_MINOR@,@MapServer_VERSION_REVISION@,0 +#define VER_FILEVERSION_STR "@MapServer_VERSION_STRING@\0" + +#define VER_PRODUCTVERSION @MapServer_VERSION_MAJOR@,@MapServer_VERSION_MINOR@,@MapServer_VERSION_REVISION@,0 +#define VER_PRODUCTVERSION_STR "@MapServer_VERSION_STRING@\0" + +#ifndef DEBUG +#define VER_DEBUG 0 +#else +#define VER_DEBUG VS_FF_DEBUG +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VER_FILEVERSION + PRODUCTVERSION VER_PRODUCTVERSION + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS VER_DEBUG + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "CompanyName", "MapServer" + VALUE "FileDescription", "MapServer" + VALUE "FileVersion", VER_FILEVERSION_STR + VALUE "InternalName", "" + VALUE "LegalCopyright", "Copyright (c) 2017 MapServer" + VALUE "LegalTrademarks1", "" + VALUE "LegalTrademarks2", "" + VALUE "OriginalFilename", "" + VALUE "ProductName", "MapServer" + VALUE "ProductVersion", VER_PRODUCTVERSION_STR + END + END + + BLOCK "VarFileInfo" + BEGIN + /* The following line should only be modified for localized versions. */ + /* It consists of any number of WORD,WORD pairs, with each pair */ + /* describing a language,codepage combination supported by the file. */ + /* */ + /* For example, a file might have values "0x409,1252" indicating that it */ + /* supports English language (0x409) in the Windows ANSI codepage (1252). */ + + VALUE "Translation", 0x409, 1252 + + END END \ No newline at end of file From 2a23d53aed35db8c28ccceaa7df9a15914ed07bf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 4 Oct 2020 12:27:37 +0000 Subject: [PATCH 026/160] Remove reference to the missing nmake.opt --- mapscript/csharp/Makefile.vc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapscript/csharp/Makefile.vc b/mapscript/csharp/Makefile.vc index 2297b8889c..eea6ea145b 100755 --- a/mapscript/csharp/Makefile.vc +++ b/mapscript/csharp/Makefile.vc @@ -13,7 +13,7 @@ # Flag indicating to the option files that this is the build of C#/MapScript -!INCLUDE ../../nmake.opt +#!INCLUDE ../../nmake.opt # Be aware when setting different options for libmap.dll and mapscript.dll (Bug 1476) # To change the options for mapscript.dll uncomment the following line From 881161d62d8fdc9f41abf235d5c5f1e6b0dab6c7 Mon Sep 17 00:00:00 2001 From: jmckenna Date: Tue, 13 Oct 2020 10:34:54 -0300 Subject: [PATCH 027/160] remove @tbontfort email notifications --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 780251daef..7ac04f09c4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -81,9 +81,6 @@ after_success: - ./ci/travis/after_success.sh notifications: - email: - recipients: - - thomas.bonfort@gmail.com irc: channels: - "irc.freenode.org#mapserver" From 5235489fa322f187cb7fb7c63c47f52995ab0f79 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 20 Oct 2020 10:19:55 +0200 Subject: [PATCH 028/160] FindOracle.cmake: complementary fix to find OCI 19 --- cmake/FindOracle.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/FindOracle.cmake b/cmake/FindOracle.cmake index 4a8697a2d9..65581302d7 100644 --- a/cmake/FindOracle.cmake +++ b/cmake/FindOracle.cmake @@ -37,7 +37,7 @@ if(DEFINED ENV{ORACLE_HOME}) ${ORACLE_HOME}/OCI/include) # Oracle XE on Windows set(ORACLE_OCI_NAMES clntsh libclntsh oci) - set(ORACLE_NNZ_NAMES nnz10 libnnz10 nnz11 libnnz11 nnz12 libnnz12 nnz18 libnnz18 ociw32 libnnz19) + set(ORACLE_NNZ_NAMES nnz10 libnnz10 nnz11 libnnz11 nnz12 libnnz12 nnz18 libnnz18 ociw32 nnz19 libnnz19) set(ORACLE_OCCI_NAMES libocci occi oraocci10 oraocci11 oraocci12) set(ORACLE_LIB_DIR From c6da2de19919f30956cd480a7c87bc9e42f02bec Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 23 Nov 2020 16:11:06 +0000 Subject: [PATCH 029/160] Contour layer: take into account nodata value from GDAL raster (fixes #6182) --- mapcontour.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/mapcontour.c b/mapcontour.c index d4b99c8993..60b764bf74 100644 --- a/mapcontour.c +++ b/mapcontour.c @@ -446,6 +446,17 @@ static int msContourLayerReadRaster(layerObj *layer, rectObj rect) return MS_FAILURE; } + { + // Copy nodata value from source dataset to memory dataset + int bHasNoData = FALSE; + double dfNoDataValue = GDALGetRasterNoDataValue(hBand, &bHasNoData); + if( bHasNoData ) + { + GDALSetRasterNoDataValue(GDALGetRasterBand(clinfo->hDS, 1), + dfNoDataValue); + } + } + adfGeoTransform[0] = copyRect.minx; adfGeoTransform[1] = dst_cellsize_x; adfGeoTransform[2] = 0; @@ -533,6 +544,8 @@ static int msContourLayerGenerateContour(layerObj *layer) int levelCount = 0; GDALRasterBandH hBand = NULL; CPLErr eErr; + int bHasNoData = FALSE; + double dfNoDataValue; contourLayerInfo *clinfo = (contourLayerInfo *) layer->layerinfo; @@ -613,10 +626,12 @@ static int msContourLayerGenerateContour(layerObj *layer) CSLDestroy(levelsTmp); free(option); } - + + dfNoDataValue = GDALGetRasterNoDataValue(hBand, &bHasNoData); + eErr = GDALContourGenerate( hBand, interval, 0.0, levelCount, levels, - FALSE, 0.0, hLayer, + bHasNoData, dfNoDataValue, hLayer, OGR_FD_GetFieldIndex(OGR_L_GetLayerDefn( hLayer), "ID" ), (elevItem == NULL) ? -1 : From 6bc8253f305f283f4c18b2d307155c8a7b21dc87 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 4 Dec 2020 15:26:13 +0000 Subject: [PATCH 030/160] Update CMake minimum version required in README --- INSTALL.CMAKE | 5 ++--- MIGRATION_GUIDE.txt | 7 ++++--- README.rst | 12 ++++++------ msautotest/README | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/INSTALL.CMAKE b/INSTALL.CMAKE index d68599ce13..2fb3c78983 100644 --- a/INSTALL.CMAKE +++ b/INSTALL.CMAKE @@ -12,9 +12,8 @@ build utilities (make on unixes, visual studio on windows, xcode on osx, etc...) Install CMake ------------- -MapServer requires at least CMake version 2.6.0, although the build process with -such an old version has not been thouroughly tested. 2.8.0 and above are -recommended. +MapServer now requires at least CMake version 3.0, although the CMake process was +first implemented in MapServer 6.4 with CMake 2.6.0. Distro Packaged Version ....................... diff --git a/MIGRATION_GUIDE.txt b/MIGRATION_GUIDE.txt index 8d7371516e..69958aa161 100644 --- a/MIGRATION_GUIDE.txt +++ b/MIGRATION_GUIDE.txt @@ -3,12 +3,13 @@ MapServer Migration Guide ***************************************************************************** The current version of the MapServer Migration Guide is available online -at http://www.mapserver.org/MIGRATION_GUIDE.html. +at https://mapserver.org/MIGRATION_GUIDE.html. For developers: The master copy of the MIGRATION_GUIDE is now located in the root of the -mapserver/docs source tree (https://github.com/mapserver/docs). Developers -are welcome and encouraged to edit/update the guide in the docs tree directly. +/MapServer/MapServer-documentation source tree ( https://github.com/MapServer/MapServer-documentation ). +Developers are welcome and encouraged to edit/update the guide in the documentation +tree directly. diff --git a/README.rst b/README.rst index be676b4d46..6708ef7eb0 100644 --- a/README.rst +++ b/README.rst @@ -13,9 +13,9 @@ respond to a variety of spatial requests like making maps, scalebars, and point, area and feature queries. Virtually all aspects of an application, from web interface to map appearance can be developed without any programming. For the more ambitious user, MapServer -applications can be enhanced using Java, JavaScript or many other web -technologies. For more information and complete documentation please -visit: +applications can be enhanced using Python, PHP, Java, JavaScript or +many other web technologies. For more information and complete +documentation please visit: https://mapserver.org/ @@ -23,7 +23,7 @@ Bug reports and enhancement submissions can be reported in the MapServer issue tracker at the following url. If you do make changes and/or enhancements, please let us know so that they might be incorporated into future releases. - https://github.com/mapserver/mapserver/issues + https://github.com/MapServer/MapServer/issues Join the MapServer user mailing list online at: @@ -91,8 +91,8 @@ License SOFTWARE. -.. |Build Status| image:: https://travis-ci.org/mapserver/mapserver.svg?branch=master - :target: https://travis-ci.org/mapserver/mapserver +.. |Build Status| image:: https://travis-ci.com/MapServer/MapServer.svg?branch=master + :target: https://travis-ci.com/MapServer/MapServer .. |Appveyor Build Status| image:: https://ci.appveyor.com/api/projects/status/vw1n07095a8bg23u?svg=true :target: https://ci.appveyor.com/project/mapserver/mapserver diff --git a/msautotest/README b/msautotest/README index e83faf0958..f14f1c8ffd 100644 --- a/msautotest/README +++ b/msautotest/README @@ -1,6 +1,6 @@ See: - http://www.mapserver.org/development/tests/autotest.html + https://mapserver.org/development/tests/autotest.html -Send new tests via MapServer Issue Tracker at https://github.com/mapserver/mapserver/issues/ +Send new tests via MapServer Issue Tracker at https://github.com/MapServer/MapServer/issues/ (use the label 'Msautotest'). From 2c5f88ffa9af3eb12df0855858412448011113b4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 4 Dec 2020 23:24:59 +0000 Subject: [PATCH 031/160] [msautotest] switch to https for demo.mapserver.org --- msautotest/gdal/wmsclient.map | 2 +- msautotest/gdal/wmsclient_3543.map | 2 +- msautotest/renderers/wmsclient.map | 2 +- msautotest/wxs/wms_client_111.map | 2 +- msautotest/wxs/wms_client_111_axis_fail.map | 2 +- msautotest/wxs/wms_client_130.map | 2 +- msautotest/wxs/wms_client_130_axis_fail.map | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/msautotest/gdal/wmsclient.map b/msautotest/gdal/wmsclient.map index a2e076926e..abb12fa5a1 100644 --- a/msautotest/gdal/wmsclient.map +++ b/msautotest/gdal/wmsclient.map @@ -27,7 +27,7 @@ LAYER STATUS DEFAULT DEBUG 1 #CONNECTION "http://mstest.tiles.osgeo.org/wms/vmap0?" - CONNECTION "http://demo.mapserver.org/cgi-bin/mapserv?map=/osgeo/mapserver/msautotest/world/world.map&" + CONNECTION "https://demo.mapserver.org/cgi-bin/mapserv?map=/osgeo/mapserver/msautotest/world/world.map&" CONNECTIONTYPE WMS METADATA "wms_srs" "EPSG:4326" diff --git a/msautotest/gdal/wmsclient_3543.map b/msautotest/gdal/wmsclient_3543.map index 5b5b44a2c7..59355a2879 100644 --- a/msautotest/gdal/wmsclient_3543.map +++ b/msautotest/gdal/wmsclient_3543.map @@ -22,7 +22,7 @@ END LAYER NAME "X" - CONNECTION "http://demo.mapserver.org/cgi-bin/mapserv?map=/osgeo/mapserver/msautotest/world/world.map&" + CONNECTION "https://demo.mapserver.org/cgi-bin/mapserv?map=/osgeo/mapserver/msautotest/world/world.map&" CONNECTIONTYPE WMS TYPE RASTER STATUS DEFAULT diff --git a/msautotest/renderers/wmsclient.map b/msautotest/renderers/wmsclient.map index 1e8244f053..f8461d80c0 100644 --- a/msautotest/renderers/wmsclient.map +++ b/msautotest/renderers/wmsclient.map @@ -27,7 +27,7 @@ LAYER TYPE RASTER STATUS DEFAULT #CONNECTION "http://mstest.tiles.osgeo.org/wms/vmap0?" - CONNECTION "http://demo.mapserver.org/cgi-bin/mapserv?map=/osgeo/mapserver/msautotest/world/world.map&" + CONNECTION "https://demo.mapserver.org/cgi-bin/mapserv?map=/osgeo/mapserver/msautotest/world/world.map&" CONNECTIONTYPE WMS METADATA "wms_srs" "EPSG:4326" diff --git a/msautotest/wxs/wms_client_111.map b/msautotest/wxs/wms_client_111.map index 4e96d9d66f..3ff4a70522 100644 --- a/msautotest/wxs/wms_client_111.map +++ b/msautotest/wxs/wms_client_111.map @@ -46,7 +46,7 @@ MAP NAME client TYPE RASTER STATUS ON - CONNECTION "http://demo.mapserver.org/cgi-bin/wms?" + CONNECTION "https://demo.mapserver.org/cgi-bin/wms?" CONNECTIONTYPE WMS TEMPLATE "dummy" METADATA diff --git a/msautotest/wxs/wms_client_111_axis_fail.map b/msautotest/wxs/wms_client_111_axis_fail.map index 783f6db4a0..cf28943428 100644 --- a/msautotest/wxs/wms_client_111_axis_fail.map +++ b/msautotest/wxs/wms_client_111_axis_fail.map @@ -44,7 +44,7 @@ MAP NAME client TYPE RASTER STATUS ON - CONNECTION "http://demo.mapserver.org/cgi-bin/wms?" + CONNECTION "https://demo.mapserver.org/cgi-bin/wms?" CONNECTIONTYPE WMS TEMPLATE "dummy" METADATA diff --git a/msautotest/wxs/wms_client_130.map b/msautotest/wxs/wms_client_130.map index 938b861e83..afe9df7fbb 100644 --- a/msautotest/wxs/wms_client_130.map +++ b/msautotest/wxs/wms_client_130.map @@ -46,7 +46,7 @@ MAP NAME client TYPE RASTER STATUS ON - CONNECTION "http://demo.mapserver.org/cgi-bin/wms?" + CONNECTION "https://demo.mapserver.org/cgi-bin/wms?" CONNECTIONTYPE WMS TEMPLATE "dummy" METADATA diff --git a/msautotest/wxs/wms_client_130_axis_fail.map b/msautotest/wxs/wms_client_130_axis_fail.map index 529bec7ef8..8b20f21d2d 100644 --- a/msautotest/wxs/wms_client_130_axis_fail.map +++ b/msautotest/wxs/wms_client_130_axis_fail.map @@ -44,7 +44,7 @@ MAP NAME client TYPE RASTER STATUS ON - CONNECTION "http://demo.mapserver.org/cgi-bin/wms?" + CONNECTION "https://demo.mapserver.org/cgi-bin/wms?" CONNECTIONTYPE WMS TEMPLATE "dummy" METADATA From 335e8a0aef925926c553c3d62aee213ca1897ffa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 7 Dec 2020 17:42:44 +0000 Subject: [PATCH 032/160] remove USE_PROJ check for mode=tile --- maptemplate.c | 3 ++ maptile.h | 2 - msautotest/misc/expected/mode_tile_output.png | Bin 0 -> 3412 bytes msautotest/misc/mode_tile.map | 36 ++++++++++++++++++ 4 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 msautotest/misc/expected/mode_tile_output.png create mode 100644 msautotest/misc/mode_tile.map diff --git a/maptemplate.c b/maptemplate.c index b2cb2a0c34..96d99ef49d 100644 --- a/maptemplate.c +++ b/maptemplate.c @@ -4612,6 +4612,7 @@ mapservObj *msAllocMapServObj() mapserv->ShapeIndex=-1; mapserv->TileIndex=-1; mapserv->TileMode=TILE_GMAP; + mapserv->TileCoords=NULL; mapserv->QueryCoordSource=NONE; mapserv->ZoomSize=0; /* zoom absolute magnitude (i.e. > 0) */ @@ -4651,6 +4652,8 @@ void msFreeMapServObj(mapservObj* mapserv) msFree(mapserv->SelectLayer); msFree(mapserv->QueryFile); + msFree(mapserv->TileCoords); + msFree(mapserv); } } diff --git a/maptile.h b/maptile.h index 84670809b6..07e5d4c650 100644 --- a/maptile.h +++ b/maptile.h @@ -30,9 +30,7 @@ #include "mapserver.h" #include "maptemplate.h" -#ifdef USE_PROJ #define USE_TILE_API 1 -#endif #define SPHEREMERC_PROJ4 "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +units=m +k=1.0 +nadgrids=@null" #define SPHEREMERC_GROUND_SIZE (20037508.34*2) diff --git a/msautotest/misc/expected/mode_tile_output.png b/msautotest/misc/expected/mode_tile_output.png new file mode 100644 index 0000000000000000000000000000000000000000..82c5c1e8874d6e50d41b6dbbc6987fed03893e4b GIT binary patch literal 3412 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 PEdjQcP8QX_`QQC7{U%8e literal 0 HcmV?d00001 diff --git a/msautotest/misc/mode_tile.map b/msautotest/misc/mode_tile.map new file mode 100644 index 0000000000..ee565e4334 --- /dev/null +++ b/msautotest/misc/mode_tile.map @@ -0,0 +1,36 @@ +# +# Test of mode=tile (CGI "tile mode"). A 256x256 tile should be generated +# in the "gmap" tilemode, reprojected to the Google Mercator (EPSG:3857) +# and displayed at the global extent. +# +# REQUIRES: INPUT=OGR OUTPUT=PNG SUPPORTS=PROJ +# +# RUN_PARMS: mode_tile_output.png [MAPSERV] QUERY_STRING="map=[MAPFILE]&MODE=tile&TILEMODE=gmap&TILE=0+0+0&LAYERS=canada-poly" > [RESULT_DEMIME] +# +MAP + NAME "MODE_TILE_TEST" + IMAGETYPE PNG + EXTENT -140.992892 41.976786 -55.630945 71.990315 + SIZE 400 400 + + PROJECTION + "init=epsg:4326" + END #projection + + LAYER + NAME "canada-poly" + TYPE POLYGON + STATUS DEFAULT + CONNECTIONTYPE OGR + CONNECTION "data/canada.dgn" + DATA "elements" + PROJECTION + "init=epsg:4326" + END #projection + CLASS + OUTLINECOLOR 0 0 0 + COLOR 120 120 120 + END #class + END #layer + +END #map From dfdda8a18c69f22806c7d6e46bb2bf59f67ed941 Mon Sep 17 00:00:00 2001 From: Jeff McKenna Date: Mon, 7 Dec 2020 16:09:40 -0400 Subject: [PATCH 033/160] update for 7.6.2 release --- CMakeLists.txt | 2 +- HISTORY.TXT | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d9ea8b592..11d42bf771 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ include(CheckCSourceCompiles) set (MapServer_VERSION_MAJOR 7) set (MapServer_VERSION_MINOR 6) -set (MapServer_VERSION_REVISION 1) +set (MapServer_VERSION_REVISION 2) set (MapServer_VERSION_SUFFIX "") # Set C++ version diff --git a/HISTORY.TXT b/HISTORY.TXT index 76b3d98058..68d800401b 100644 --- a/HISTORY.TXT +++ b/HISTORY.TXT @@ -12,6 +12,11 @@ For a complete change history, please see the Git log comments. For more details about recent point releases, please see the online changelog at: http://mapserver.org/development/changelog/ +7.6.2 release (2020-12-07) +------------------------- + +- No major changes, see detailed changelog for bug fixes + 7.6.1 release (2020-07-31) ------------------------- From d0537969819b361a0eea0a89f3cd4f289758b048 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 14 Dec 2020 11:38:16 +0000 Subject: [PATCH 034/160] docs: fix simple typo, renrdering -> rendering --- renderers/agg/include/agg_rendering_buffer.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/renderers/agg/include/agg_rendering_buffer.h b/renderers/agg/include/agg_rendering_buffer.h index b96077959c..35a4d2f2f0 100644 --- a/renderers/agg/include/agg_rendering_buffer.h +++ b/renderers/agg/include/agg_rendering_buffer.h @@ -128,7 +128,7 @@ namespace mapserver private: //-------------------------------------------------------------------- - T* m_buf; // Pointer to renrdering buffer + T* m_buf; // Pointer to rendering buffer T* m_start; // Pointer to first pixel depending on stride unsigned m_width; // Width in pixels unsigned m_height; // Height in pixels @@ -258,7 +258,7 @@ namespace mapserver private: //-------------------------------------------------------------------- - T* m_buf; // Pointer to renrdering buffer + T* m_buf; // Pointer to rendering buffer pod_array m_rows; // Pointers to each row of the buffer unsigned m_width; // Width in pixels unsigned m_height; // Height in pixels From 7f791355559d5d41b74346a46fd973116cb5eb7f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 15 Jan 2021 21:55:47 +0000 Subject: [PATCH 035/160] update license copyright year --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 6708ef7eb0..8cfe989ba9 100644 --- a/README.rst +++ b/README.rst @@ -69,7 +69,7 @@ License :: - Copyright (c) 2008-2020 Open Source Geospatial Foundation. + Copyright (c) 2008-2021 Open Source Geospatial Foundation. Copyright (c) 1996-2008 Regents of the University of Minnesota. Permission is hereby granted, free of charge, to any person obtaining a copy From 8d2dc67a1632e9912f98242bb2ab7a8f6344ef13 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 16 Jan 2021 20:37:00 +0000 Subject: [PATCH 036/160] more updates for main/master --- MIGRATION_GUIDE.txt | 2 +- README.rst | 2 +- mapscript/python/README.rst | 8 ++++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/MIGRATION_GUIDE.txt b/MIGRATION_GUIDE.txt index 69958aa161..28956387b0 100644 --- a/MIGRATION_GUIDE.txt +++ b/MIGRATION_GUIDE.txt @@ -8,7 +8,7 @@ at https://mapserver.org/MIGRATION_GUIDE.html. For developers: -The master copy of the MIGRATION_GUIDE is now located in the root of the +The main copy of the MIGRATION_GUIDE is now located in the root of the /MapServer/MapServer-documentation source tree ( https://github.com/MapServer/MapServer-documentation ). Developers are welcome and encouraged to edit/update the guide in the documentation tree directly. diff --git a/README.rst b/README.rst index 8cfe989ba9..bac791e4e2 100644 --- a/README.rst +++ b/README.rst @@ -91,7 +91,7 @@ License SOFTWARE. -.. |Build Status| image:: https://travis-ci.com/MapServer/MapServer.svg?branch=master +.. |Build Status| image:: https://travis-ci.com/MapServer/MapServer.svg?branch=main :target: https://travis-ci.com/MapServer/MapServer .. |Appveyor Build Status| image:: https://ci.appveyor.com/api/projects/status/vw1n07095a8bg23u?svg=true diff --git a/mapscript/python/README.rst b/mapscript/python/README.rst index 5ddd4f19c2..92b9f4c574 100644 --- a/mapscript/python/README.rst +++ b/mapscript/python/README.rst @@ -2,7 +2,7 @@ Python MapScript for MapServer README ===================================== :Author: MapServer Team -:Last Updated: 2020-03-02 +:Last Updated: 2021-01-16 Introduction ------------ @@ -35,7 +35,7 @@ Advantages of ready-made wheels on PyPI include: + mapscript can be easily added to a Python `Virtual Environment `_ + Python2 or Python3 versions of mapscript can be installed and work with a single installation of MapServer -Wheels are built based on the `Appveyor build environments `_. +Wheels are built based on the `Appveyor build environments `_. These are as follows at the time of writing: + Python 2.7 x32 @@ -68,6 +68,10 @@ Windows binary packages can be downloaded from `GIS Internals `_ (MapServer for Windows) is a full installer that contains Python & Python + MapScript already configured out-of-the-box, as well as default OGC web services and over 60 working mapfiles. + When using these packages the MapServer path will be similar to ``C:\release-1911-x64-gdal-2-3-mapserver-7-2\bin``. Prior to installing mapscript it is recommended to update pip to the latest version with the following command: From 306164a5d8cb0abf0abaa96d026193a2da7a9ca6 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Fri, 22 Jan 2021 12:31:08 +0100 Subject: [PATCH 037/160] PROJ >= 6 reprojection: fix use of freed memory when input and output projections are the same --- mapproject.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/mapproject.c b/mapproject.c index e432ae1ef7..1ccc505928 100644 --- a/mapproject.c +++ b/mapproject.c @@ -119,11 +119,23 @@ static int msProjectHasLonWrapOrOver(projectionObj *in) { /* Return to be freed with proj_destroy() if *pbFreePJ = TRUE */ static PJ* createNormalizedPJ(projectionObj *in, projectionObj *out, int* pbFreePJ) { + if( in->proj == out->proj ) + { + /* Special case to avoid out_str below to cause in_str to become invalid */ + *pbFreePJ = TRUE; +#if PROJ_VERSION_MAJOR == 6 && PROJ_VERSION_MINOR == 0 + /* 6.0 didn't support proj=noop */ + return proj_create(in->proj_ctx->proj_ctx, "+proj=affine"); +#else + return proj_create(in->proj_ctx->proj_ctx, "+proj=noop"); +#endif + } + const char* const wkt_options[] = { "MULTILINE=NO", NULL }; - const char* in_str = (in && msProjectHasLonWrapOrOver(in)) ? + const char* in_str = msProjectHasLonWrapOrOver(in) ? proj_as_proj_string(in->proj_ctx->proj_ctx, in->proj, PJ_PROJ_4, NULL) : proj_as_wkt(in->proj_ctx->proj_ctx, in->proj, PJ_WKT2_2018, wkt_options); - const char* out_str = (out && msProjectHasLonWrapOrOver(out)) ? + const char* out_str = msProjectHasLonWrapOrOver(out) ? proj_as_proj_string(out->proj_ctx->proj_ctx, out->proj, PJ_PROJ_4, NULL) : proj_as_wkt(out->proj_ctx->proj_ctx, out->proj, PJ_WKT2_2018, wkt_options); PJ* pj_raw; From 3cc59a579482fe587625ecfaeccd47f08f094d0a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 29 Jan 2021 15:49:53 +0000 Subject: [PATCH 038/160] fix writeStyle(...) for "GEOMTRANSFORM CENTROID" --- mapfile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapfile.c b/mapfile.c index da7be007d7..690cc0afe7 100644 --- a/mapfile.c +++ b/mapfile.c @@ -2729,7 +2729,7 @@ void writeStyle(FILE *stream, int indent, styleObj *style) msIO_fprintf(stream, "GEOMTRANSFORM (%s)\n", style->_geomtransform.string); } else if(style->_geomtransform.type != MS_GEOMTRANSFORM_NONE) { - writeKeyword(stream, indent, "GEOMTRANSFORM", style->_geomtransform.type, 7, + writeKeyword(stream, indent, "GEOMTRANSFORM", style->_geomtransform.type, 8, MS_GEOMTRANSFORM_BBOX, "\"bbox\"", MS_GEOMTRANSFORM_END, "\"end\"", MS_GEOMTRANSFORM_LABELPOINT, "\"labelpnt\"", From 39eed028ca4e3bc541035d0930bac7ed184679cd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 10 Mar 2021 12:53:02 +0000 Subject: [PATCH 039/160] Fixed rounding error in WMS client --- mapwmslayer.c | 4 ++-- msautotest/gdal/expected/wmsclient_3543.png | Bin 24643 -> 24821 bytes 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mapwmslayer.c b/mapwmslayer.c index 4e1bcb93c7..0c68f250b9 100755 --- a/mapwmslayer.c +++ b/mapwmslayer.c @@ -734,8 +734,8 @@ msBuildWMSLayerURL(mapObj *map, layerObj *lp, int nRequestType, msRectIntersect( &bbox, &layer_rect ); - bbox_width = ceil((bbox.maxx - bbox.minx) / cellsize); - bbox_height = ceil((bbox.maxy - bbox.miny) / cellsize); + bbox_width = round((bbox.maxx - bbox.minx) / cellsize); + bbox_height = round((bbox.maxy - bbox.miny) / cellsize); /* Force going through the resampler if we're going to receive a clipped BBOX (#4931) */ if(msLayerGetProcessingKey(lp, "RESAMPLE") == NULL) { diff --git a/msautotest/gdal/expected/wmsclient_3543.png b/msautotest/gdal/expected/wmsclient_3543.png index 407f15a590984fb1f78a818177d8d97aeede2385..45a3942da0db3a4f0d6ff8e7ad7cdcdfc44ddabf 100644 GIT binary patch literal 24821 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

K92}lhWq%EWiqr!Y7f?z3($aHbToe5(= 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

H2v`{7@%{>L|;{PgdS|L+eE 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 Date: Sun, 21 Mar 2021 12:26:29 +0000 Subject: [PATCH 040/160] msautotest: add test for UNION layer --- msautotest/misc/data/union/layer1.dbf | Bin 0 -> 370 bytes msautotest/misc/data/union/layer1.shp | Bin 0 -> 3060 bytes msautotest/misc/data/union/layer1.shx | Bin 0 -> 148 bytes msautotest/misc/data/union/layer2.dbf | Bin 0 -> 118 bytes msautotest/misc/data/union/layer2.shp | Bin 0 -> 1620 bytes msautotest/misc/data/union/layer2.shx | Bin 0 -> 132 bytes msautotest/misc/expected/union.png | Bin 0 -> 14966 bytes msautotest/misc/union.map | 57 ++++++++++++++++++++++++++ 8 files changed, 57 insertions(+) create mode 100644 msautotest/misc/data/union/layer1.dbf create mode 100644 msautotest/misc/data/union/layer1.shp create mode 100644 msautotest/misc/data/union/layer1.shx create mode 100644 msautotest/misc/data/union/layer2.dbf create mode 100644 msautotest/misc/data/union/layer2.shp create mode 100644 msautotest/misc/data/union/layer2.shx create mode 100644 msautotest/misc/expected/union.png create mode 100644 msautotest/misc/union.map diff --git a/msautotest/misc/data/union/layer1.dbf b/msautotest/misc/data/union/layer1.dbf new file mode 100644 index 0000000000000000000000000000000000000000..6076a2ababa3497277aa7e865743e0ab4a836172 GIT binary patch literal 370 zcmZ9Gu@1s83`8weVgV!;7N(3$Q5?rkGFG%mEC|8C$agUE=cH)@Z9U}VcV|1zyVF6s%;-ut%D>!6!vp6T1i>(XVG{q@|2+x^^kAh=zw}LZ`7Mr7sCS@S{FGeLgt12zUO%kKYMjI+h^~4-t&In^S;kH zB5e=KeSd0GH>Qi!V6&}uVORZ6W1s4)e%q6;XAXZZ&3xYocm1=e{`YCdlD+E?YZ=zY zd3}vomE+vTLih9r`0k_clIJp~FSsvbx$nS!40dx0Yz}XP|F2*-0uB?j@7vVayS}Y? za&u;X0d_gD73=%KC0D-|oaSS_;kTHsUifdMy$9@uz;+ky!{Bg}`Hj$j6ZsqRv7Q{y zod)}Z&m+I`y%$j*e*kt}uW5JHJ#?=@tjYF#W+nHWo2q+KdEfJ0>buhi$Ws;~kGNnx z@_YasuHal`F7tHg0oy`83ifx9o6gltGWQz%&B$8Bp9N0MSTFOyegGT|{oezpC&*3A zXDR)E1KZ`~LDX6L!09%5psdF^#X;Itz3P+GG@JciY7;-=x4k`p+^e3d>fYMucimrM z_qK*_-7Q@qcVb@|?$7qP1Z@9#5&pAje*}AlX0YpkznKG0?;@Th`xbCyoda`q*N}U> zg!pa@><)tCd2%Q1KY;yW>}QXG!{rxpeqj`B4q?9R*I?U(`75>Z!<^4Uw2#6rN5KBW zEjga~9xRjKc!vH%jDHcF$Yug;X3~E?>JiRiKhJu|9?WZGA4@y+L*Q@EvF{&ZJ=GkP z87umr&$n4*TG80}FnU~TY}#6nUG+(4qfhiXr{CFA3z)~%mvUX?aj8)8AnE z7#wecV=Gwxp#Npo;}@_W2b)V^_cQ%D^KpXy+hMn(;M50>s|JzJO7ex5k#CLuH*lUo zXg`AUHn(vf$7eBjX^QvrPRyN5r~ezAlfPh>_c2HAD%URp+X=910sAr9*I~UoN*)KN zx9Hyn|F8{gf8xE(_k#HrY}df<&q8nAe#T?(`wo8ZEzCmd2B(BMG@sC}bVBQh))}os zPU)1^F|Bi22enRW9o0H3N{99QR#(NOdTW(h6wS(aYo!B^HgrJWv*oTS_psI{tzTN- zwEk&*EV&f@v`SyK{%U=;TEDfvt8=~Q+*Mx`%`CN&JKcG1%roc{=>hwG@^Wz80ghj- z&i?Wa*pGK%?mj2Cf$ec{c?a8ZuzsgF^^Vc!GoyZVmgnsF1}bl!;+x+{)j89zI_+Zf SyodBYC02P)>G#(E*8c~GMn4n) literal 0 HcmV?d00001 diff --git a/msautotest/misc/data/union/layer1.shx b/msautotest/misc/data/union/layer1.shx new file mode 100644 index 0000000000000000000000000000000000000000..98b0d5ff604e3bd7465fc8c7d05d418354e94302 GIT binary patch literal 148 zcmZQzQ0HR64!mA4Gcd41ZUREo2*}(4#P1+9 c<0=LQMgax}rhPyR6k<69Bt8Ib+Xkc^0DvJ5NB{r; literal 0 HcmV?d00001 diff --git a/msautotest/misc/data/union/layer2.dbf b/msautotest/misc/data/union/layer2.dbf new file mode 100644 index 0000000000000000000000000000000000000000..34e534d3f90527ed171caf58b5fca9b6dc9c05a5 GIT binary patch literal 118 zcmZRsWEN#%U|?`$;02OsARH(jMe literal 0 HcmV?d00001 diff --git a/msautotest/misc/data/union/layer2.shp b/msautotest/misc/data/union/layer2.shp new file mode 100644 index 0000000000000000000000000000000000000000..9b8c26737005d56732def5cdc0f8d135d1d517ca GIT binary patch literal 1620 zcmZ{kJ7`ov6o${{B`8>k4+t!o%1B~RNi-5noMe3@##jU-O$Y;)Q7krC2@aTc6$FW3 zicyFlNDSg5h2Wr73b7D^g5new>0B#|G~RpuGrOBHZnO8h=P`Hw^Un}`R?&)99G7exmVUET*BPK zBjmeZ!WD4zPnZLTarnv1fK>tcep=$2SdXT_c3OB+_$%h7kH&hLV~=oIbUG;cQ(&*a zp#@C)CBJeO``aa4J%jzVf?Y|tDq41Ej@Psmt$jE0Qy-*0p~Of3_$={Z>Qj^WIQ4mU zl4;{VXW4d&hjWkRK4`D|AoXAB&*-VzJ`){cZ~X!~DFdC{6R`V?^PwtW>Vik(J)@q= zdGLt)4+E^Te+ky*lgw%ftQPy2?H%d&r4b3`1Alf0>7tCL9R~O93ZUREo2*}(4#P1jw T7=aYyCm`{Hfr05BkoEuo;;Ibl literal 0 HcmV?d00001 diff --git a/msautotest/misc/expected/union.png b/msautotest/misc/expected/union.png new file mode 100644 index 0000000000000000000000000000000000000000..7cb9408719466dcc751c1e49b9d30449a5adfc6a GIT binary patch literal 14966 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 Date: Mon, 8 Mar 2021 15:19:50 +0100 Subject: [PATCH 041/160] FindProj.cmake: allow finding PROJ 8 that has no longer proj_api.h (fixes #6249) --- cmake/FindProj.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/FindProj.cmake b/cmake/FindProj.cmake index e2b64a68bc..63648d1afc 100644 --- a/cmake/FindProj.cmake +++ b/cmake/FindProj.cmake @@ -6,7 +6,7 @@ # PROJ_LIBRARY -FIND_PATH(PROJ_INCLUDE_DIR proj_api.h) +FIND_PATH(PROJ_INCLUDE_DIR NAMES proj.h proj_api.h) FIND_LIBRARY(PROJ_LIBRARY NAMES proj proj_i) From d3997d18039e77399eb8a8c691fe0b07b6ab33be Mon Sep 17 00:00:00 2001 From: Daniel Morissette Date: Wed, 7 Apr 2021 17:07:08 -0400 Subject: [PATCH 042/160] Fetch get-pip.py for Python 2.7 since Python 2 support was dropped in get-pip.py for pip 21.0 release --- scripts/vagrant/packages.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/vagrant/packages.sh b/scripts/vagrant/packages.sh index a4d2f3749f..c0f364c684 100755 --- a/scripts/vagrant/packages.sh +++ b/scripts/vagrant/packages.sh @@ -22,6 +22,6 @@ apt-get install -q -y git build-essential pkg-config cmake3 libgeos-dev rake \ libprotobuf-dev libprotobuf-c0-dev protobuf-c-compiler libharfbuzz-dev gdal-bin \ curl sqlite3 -curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py +curl https://bootstrap.pypa.io/pip/2.7/get-pip.py -o get-pip.py python get-pip.py pip install -U -r /vagrant/msautotest/requirements.txt From e8646838fa2c7e3da41e104221f69ab614866758 Mon Sep 17 00:00:00 2001 From: MapServer-backport-bot <63150209+MapServer-backport-bot@users.noreply.github.com> Date: Wed, 14 Apr 2021 15:51:08 -0300 Subject: [PATCH 043/160] [Backport branch-7-6] Fix resource leak and pointObj initialization errors. (#6296) --- mapdraw.c | 10 ++++++++-- mapfile.c | 7 ++++--- mapgml.c | 26 ++++++++++++++------------ maplabel.c | 2 +- mapobject.c | 10 +++++++--- mapows.c | 18 ++++++++++-------- mappostgis.c | 2 +- mapprimitive.c | 2 +- mapproject.c | 2 +- mapsmoothing.c | 6 +++--- maptemplate.c | 11 ++++++++--- mapwcs.c | 4 +++- mapwcs20.c | 1 + mapwfs.c | 9 +++------ mapwmslayer.c | 18 ++++++++++++------ 15 files changed, 77 insertions(+), 51 deletions(-) diff --git a/mapdraw.c b/mapdraw.c index 0b9873a4e3..c1d52d32bf 100644 --- a/mapdraw.c +++ b/mapdraw.c @@ -542,6 +542,13 @@ imageObj *msDrawMap(mapObj *map, int querymap) if(map->legend.status == MS_EMBED && map->legend.postlabelcache) if(UNLIKELY(MS_FAILURE == msEmbedLegend(map, image))) { msFreeImage( image ); +#if defined(USE_WMS_LYR) || defined(USE_WFS_LYR) + /* Cleanup WMS/WFS Request stuff */ + if (pasOWSReqInfo) { + msHTTPFreeRequestObj(pasOWSReqInfo, numOWSRequests); + msFree(pasOWSReqInfo); + } +#endif return NULL; } @@ -552,7 +559,6 @@ imageObj *msDrawMap(mapObj *map, int querymap) if(map->gt.need_geotransform) msMapRestoreRealExtent(map); - if(MS_SUCCESS != msEmbedScalebar(map, image)) { msFreeImage( image ); #if defined(USE_WMS_LYR) || defined(USE_WFS_LYR) @@ -1947,7 +1953,7 @@ int polygonLayerDrawShape(mapObj *map, imageObj *image, layerObj *layer, { int c = shape->classindex; - pointObj annopnt; + pointObj annopnt = {0,0,0,0}; // initialize int i; if(MS_DRAW_FEATURES(drawmode)) { diff --git a/mapfile.c b/mapfile.c index 690cc0afe7..eb753ad2ff 100644 --- a/mapfile.c +++ b/mapfile.c @@ -1993,7 +1993,7 @@ void msFreeExpression(expressionObj *exp) int loadExpression(expressionObj *exp) { - /* TODO: should we fall msFreeExpression if exp->string != NULL? We do some checking to avoid a leak but is it enough... */ + /* TODO: should we call msFreeExpression if exp->string != NULL? We do some checking to avoid a leak but is it enough... */ msyystring_icase = MS_TRUE; if((exp->type = getSymbol(6, MS_STRING,MS_EXPRESSION,MS_REGEX,MS_ISTRING,MS_IREGEX,MS_LIST)) == -1) return(-1); @@ -2002,6 +2002,7 @@ int loadExpression(expressionObj *exp) msFree(exp->native_string); } exp->string = msStrdup(msyystring_buffer); + exp->native_string = NULL; if(exp->type == MS_ISTRING) { exp->flags = exp->flags | MS_EXP_INSENSITIVE; @@ -6443,7 +6444,7 @@ mapObj *msLoadMapFromString(char *buffer, char *new_mappath) MS_CHECK_ALLOC(map, sizeof(mapObj), NULL); if(initMap(map) == -1) { /* initialize this map */ - msFree(map); + msFreeMap(map); return(NULL); } @@ -6535,7 +6536,7 @@ mapObj *msLoadMap(const char *filename, const char *new_mappath) MS_CHECK_ALLOC(map, sizeof(mapObj), NULL); if(initMap(map) == -1) { /* initialize this map */ - msFree(map); + msFreeMap(map); return(NULL); } diff --git a/mapgml.c b/mapgml.c index 37b1dc72de..4f1e21f8af 100644 --- a/mapgml.c +++ b/mapgml.c @@ -34,7 +34,7 @@ #include "maptime.h" -/* Use only mapgml.c if WMS or WFS is available (with minor exceptions at end)*/ +/* Use only mapgml.c if WMS or WFS is available (with minor exceptions at end) */ #if defined(USE_WMS_SVR) || defined (USE_WFS_SVR) @@ -1485,23 +1485,25 @@ int msGMLWriteQuery(mapObj *map, char *filename, const char *namespaces) if(pszOutputSRS == pszMapSRS && msProjectionsDiffer(&(lp->projection), &(map->projection))) { reprojector = msProjectCreateReprojector(&(lp->projection), &(map->projection)); if( reprojector == NULL ) { - msGMLFreeGroups(groupList); - msGMLFreeConstants(constantList); - msGMLFreeItems(itemList); - msGMLFreeGeometries(geometryList); - return MS_FAILURE; + msGMLFreeGroups(groupList); + msGMLFreeConstants(constantList); + msGMLFreeItems(itemList); + msGMLFreeGeometries(geometryList); + msFree(pszOutputSRS); + return MS_FAILURE; } } for(j=0; jresultcache->numresults; j++) { status = msLayerGetShape(lp, &shape, &(lp->resultcache->results[j])); if(status != MS_SUCCESS) { - msGMLFreeGroups(groupList); - msGMLFreeConstants(constantList); - msGMLFreeItems(itemList); - msGMLFreeGeometries(geometryList); - msProjectDestroyReprojector(reprojector); - return(status); + msGMLFreeGroups(groupList); + msGMLFreeConstants(constantList); + msGMLFreeItems(itemList); + msGMLFreeGeometries(geometryList); + msProjectDestroyReprojector(reprojector); + msFree(pszOutputSRS); + return MS_FAILURE; } /* project the shape into the map projection (if necessary), note that this projects the bounds as well */ diff --git a/maplabel.c b/maplabel.c index cfa2ed0bfe..64dff2ad02 100644 --- a/maplabel.c +++ b/maplabel.c @@ -881,7 +881,7 @@ int msGetTextSymbolSize(mapObj *map, textSymbolObj *ts, rectObj *r) { pointObj get_metrics(pointObj *p, int position, textPathObj *tp, int ox, int oy, double rotation, int buffer, label_bounds *bounds) { - pointObj q; + pointObj q = {0,0,0,0}; // initialize double x1=0, y1=0, x2=0, y2=0; double sin_a,cos_a; double w, h, x, y; diff --git a/mapobject.c b/mapobject.c index 48cc8d73dd..ee303ad692 100644 --- a/mapobject.c +++ b/mapobject.c @@ -47,7 +47,7 @@ void freeLegend(legendObj *legend); mapObj *msNewMapObj() { - mapObj *map; + mapObj *map = NULL; /* create an empty map, no layers etc... */ map = (mapObj *)calloc(sizeof(mapObj),1); @@ -57,11 +57,15 @@ mapObj *msNewMapObj() return NULL; } - if( initMap( map ) == -1 ) + if( initMap( map ) == -1 ) { + msFreeMap(map); return NULL; + } - if( msPostMapParseOutputFormatSetup( map ) == MS_FAILURE ) + if( msPostMapParseOutputFormatSetup( map ) == MS_FAILURE ) { + msFreeMap(map); return NULL; + } return map; } diff --git a/mapows.c b/mapows.c index c9be7598c0..f81d1f9ce7 100644 --- a/mapows.c +++ b/mapows.c @@ -2587,13 +2587,14 @@ char *msOWSGetProjURN(projectionObj *proj, hashTableObj *metadata, const char *n char **tokens; int numtokens, i; size_t bufferSize = 0; - char *oldStyle; + char *oldStyle = NULL; - msOWSGetEPSGProj( proj, metadata, namespaces, - bReturnOnlyFirstOne, &oldStyle ); + msOWSGetEPSGProj( proj, metadata, namespaces, bReturnOnlyFirstOne, &oldStyle ); - if( oldStyle == NULL || strncmp(oldStyle,"EPSG:",5) != 0 ) + if( oldStyle == NULL || strncmp(oldStyle,"EPSG:",5) != 0 ) { + msFree(oldStyle); return NULL; + } result = msStrdup(""); @@ -2647,13 +2648,14 @@ char *msOWSGetProjURI(projectionObj *proj, hashTableObj *metadata, const char *n char *result; char **tokens; int numtokens, i; - char *oldStyle; + char *oldStyle = NULL; - msOWSGetEPSGProj( proj, metadata, namespaces, - bReturnOnlyFirstOne, &oldStyle); + msOWSGetEPSGProj( proj, metadata, namespaces, bReturnOnlyFirstOne, &oldStyle); - if( oldStyle == NULL || !EQUALN(oldStyle,"EPSG:",5) ) + if( oldStyle == NULL || !EQUALN(oldStyle,"EPSG:",5) ) { + msFree(oldStyle); // avoid leak return NULL; + } result = msStrdup(""); diff --git a/mappostgis.c b/mappostgis.c index 0f1bdfcfa5..e960d50ba4 100644 --- a/mappostgis.c +++ b/mappostgis.c @@ -752,7 +752,7 @@ arcSegmentSide(const pointObj *p1, const pointObj *p2, const pointObj *q) int arcCircleCenter(const pointObj *p1, const pointObj *p2, const pointObj *p3, pointObj *center, double *radius) { - pointObj c; + pointObj c = {0,0,0,0}; // initialize double dx21, dy21, dx31, dy31, h21, h31, d, r; /* Circle is closed, so p2 must be opposite p1 & p3. */ diff --git a/mapprimitive.c b/mapprimitive.c index eabfdbc903..623eb20dcd 100644 --- a/mapprimitive.c +++ b/mapprimitive.c @@ -1133,7 +1133,7 @@ void msTransformPixelToShape(shapeObj *shape, rectObj extent, double cellsize) */ static pointObj generateLineIntersection(pointObj a, pointObj b, pointObj c, pointObj d) { - pointObj p; + pointObj p = {0,0,0,0}; // initialize double r; double denominator, numerator; diff --git a/mapproject.c b/mapproject.c index 1ccc505928..b967af1f94 100644 --- a/mapproject.c +++ b/mapproject.c @@ -1211,7 +1211,7 @@ static int msProjectShapeShouldDoLineCutting(reprojectionObj* reprojector) return MS_FALSE; } - pointObj p; + pointObj p = {0,0,0,0}; // initialize double invgt0 = out->gt.need_geotransform ? out->gt.invgeotransform[0] : 0.0; double invgt1 = out->gt.need_geotransform ? out->gt.invgeotransform[1] : 1.0; double invgt3 = out->gt.need_geotransform ? out->gt.invgeotransform[3] : 0.0; diff --git a/mapsmoothing.c b/mapsmoothing.c index 12e32c24fe..b6c68dd085 100644 --- a/mapsmoothing.c +++ b/mapsmoothing.c @@ -142,7 +142,7 @@ static int processShapePathDistance(shapeObj *shape, int force) while ((res = nextLineWindow(&lw)) != MS_DONE) { double ratio = 0; - pointObj point; + pointObj point = {0,0,0,0}; // initialize if (lw.lineIsRing && lw.pos==lw.line->numpoints-1) { point = newShape->line[i].point[0]; @@ -261,8 +261,8 @@ shapeObj* msSmoothShapeSIA(shapeObj *shape, int ss, int si, char *preprocessing) while ((res = nextLineWindow(&lw)) != MS_DONE) { double sum_x=0, sum_y=0, sum = 0; - pointObj point; - int k = 0; + pointObj point = {0,0,0,0}; // initialize + int k = 0; if (res == MS_FALSE) { /* invalid window */ msAddPointToLine(&newShape->line[j], diff --git a/maptemplate.c b/maptemplate.c index 96d99ef49d..3ca137bc43 100644 --- a/maptemplate.c +++ b/maptemplate.c @@ -1728,8 +1728,8 @@ static int processShplabelTag(layerObj *layer, char **line, shapeObj *origshape) } if(labelposvalid == MS_TRUE) { - pointObj p1; - pointObj p2; + pointObj p1 = {0,0,0,0}; // initialize + pointObj p2 = {0,0,0,0}; int label_offset_x, label_offset_y; labelObj *label=NULL; label_bounds lbounds; @@ -3636,12 +3636,15 @@ char *processOneToManyJoin(mapservObj* mapserv, joinObj *join) while(fgets(line, MS_BUFFER_LENGTH, stream) != NULL) outbuf = msStringConcatenate(outbuf, line); fclose(stream); + stream = NULL; } /* clear any data associated with the join */ msFreeCharArray(join->values, join->numitems); join->values = NULL; + if(stream) fclose(stream); + return(outbuf); } @@ -4219,8 +4222,10 @@ int msReturnPage(mapservObj *mapserv, char *html, int mode, char **papszBuffer) if(strchr(line, '[') != NULL) { tmpline = processLine(mapserv, line, stream, mode); - if(!tmpline) + if(!tmpline) { + fclose(stream); return MS_FAILURE; + } if(papszBuffer) { if(nBufferSize <= (int)(nCurrentSize + strlen(tmpline) + 1)) { diff --git a/mapwcs.c b/mapwcs.c index a876562c10..8957a5595c 100644 --- a/mapwcs.c +++ b/mapwcs.c @@ -1514,8 +1514,10 @@ static int msWCSGetCoverage_ImageCRSSetup( /* -------------------------------------------------------------------- */ char *layer_proj = msGetProjectionString( &(layer->projection) ); - if (msLoadProjectionString(&(map->projection), layer_proj) != 0) + if (msLoadProjectionString(&(map->projection), layer_proj) != 0) { + msFree(layer_proj); return msWCSException( map, NULL, NULL, params->version ); + } free( layer_proj ); layer_proj = NULL; diff --git a/mapwcs20.c b/mapwcs20.c index 37078ac2e8..2732a744f6 100644 --- a/mapwcs20.c +++ b/mapwcs20.c @@ -3602,6 +3602,7 @@ int msWCSGetCapabilities20(mapObj *map, cgiRequestObj *req, status = msWCSGetCapabilities20_CoverageSummary( map, params, psDoc, psNode, layer ); if(status != MS_SUCCESS) { + msFree(validated_language); xmlFreeDoc(psDoc); xmlCleanupParser(); return msWCSException(map, "Internal", "mapserv", params->version); diff --git a/mapwfs.c b/mapwfs.c index 86881def58..71e9127ce5 100644 --- a/mapwfs.c +++ b/mapwfs.c @@ -2202,15 +2202,12 @@ static int msWFSRunBasicGetFeature(mapObj* map, status = msLoadProjectionString(&(map->projection), pszMapSRS); if (status != 0) { - msSetError(MS_WFSERR, "msLoadProjectionString() failed: %s", - "msWFSGetFeature()", pszMapSRS); + msSetError(MS_WFSERR, "msLoadProjectionString() failed: %s", "msWFSGetFeature()", pszMapSRS); msFree(pszMapSRS); - return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE, - paramsObj->pszVersion); + return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE, paramsObj->pszVersion); } - msFree(pszMapSRS); - } + msFree(pszMapSRS); /*make sure that the layer projection is loaded. It could come from a ows/wfs_srs metadata*/ diff --git a/mapwmslayer.c b/mapwmslayer.c index 0c68f250b9..c39f807578 100755 --- a/mapwmslayer.c +++ b/mapwmslayer.c @@ -430,7 +430,7 @@ msBuildWMSLayerURL(mapObj *map, layerObj *lp, int nRequestType, int bFlipAxisOrder = MS_FALSE; const char *pszTmp; int bIsEssential = MS_FALSE; - + if (lp->connectiontype != MS_WMS) { msSetError(MS_WMSCONNERR, "Call supported only for CONNECTIONTYPE WMS", "msBuildWMSLayerURL()"); @@ -619,24 +619,30 @@ msBuildWMSLayerURL(mapObj *map, layerObj *lp, int nRequestType, char* pszEPSGCodeFromLayer = NULL; msOWSGetEPSGProj(&(lp->projection), NULL, "MO", MS_TRUE, &pszEPSGCodeFromLayer); if (pszEPSGCodeFromLayer == NULL || strcasecmp(pszEPSG, pszEPSGCodeFromLayer) != 0) { - char *ows_srs; - msOWSGetEPSGProj(NULL,&(lp->metadata), "MO", MS_FALSE, &ows_srs); + char *ows_srs = NULL; + msOWSGetEPSGProj(NULL, &(lp->metadata), "MO", MS_FALSE, &ows_srs); /* no need to set lp->proj if it is already set and there is only one item in the _srs metadata for this layer - we will assume the projection block matches the _srs metadata (the search for ' ' in ows_srs is a test to see if there are multiple EPSG: codes) */ if( lp->projection.numargs == 0 || ows_srs == NULL || (strchr(ows_srs,' ') != NULL) ) { - msFree(ows_srs); if (strncasecmp(pszEPSG, "EPSG:", 5) == 0) { char szProj[20]; snprintf(szProj, sizeof(szProj), "init=epsg:%s", pszEPSG+5); - if (msLoadProjectionString(&(lp->projection), szProj) != 0) + if (msLoadProjectionString(&(lp->projection), szProj) != 0) { + msFree(pszEPSGCodeFromLayer); + msFree(ows_srs); return MS_FAILURE; + } } else { - if (msLoadProjectionString(&(lp->projection), pszEPSG) != 0) + if (msLoadProjectionString(&(lp->projection), pszEPSG) != 0) { + msFree(pszEPSGCodeFromLayer); + msFree(ows_srs); return MS_FAILURE; + } } } + msFree(ows_srs); } msFree(pszEPSGCodeFromLayer); } From 6d06cfa6caa0236d9dc26dd184def454c8559b88 Mon Sep 17 00:00:00 2001 From: MapServer-backport-bot <63150209+MapServer-backport-bot@users.noreply.github.com> Date: Thu, 15 Apr 2021 19:38:46 -0300 Subject: [PATCH 044/160] [Backport branch-7-6] Improved initial check on generating reference maps, avoid crash with label styles (symbol errors), removed dead code. (#6298) * Fixes for a couple of things I ran into when doing an upgrade on app * Update mapraster.c Resolve merge conflict. Co-authored-by: github-actions[bot] Co-authored-by: Steve Lime --- mapdraw.c | 3 ++- mapfile.c | 11 ----------- mapraster.c | 10 ++++++++-- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/mapdraw.c b/mapdraw.c index c1d52d32bf..1d07d74be9 100644 --- a/mapdraw.c +++ b/mapdraw.c @@ -2837,7 +2837,7 @@ int computeMarkerBounds(mapObj *map, pointObj *annopoint, textSymbolObj *ts, lab double aox,aoy; symbolObj *symbol = map->symbolset.symbol[style->symbol]; if(msGetMarkerSize(map, style, &sx, &sy, ts->scalefactor) != MS_SUCCESS) - return MS_FALSE; + return -1; /* real error, different from MS_FALSE, return -1 so we can trap it */ if(style->angle) { pointObj *point = poly->poly->point; point[0].x = sx / 2.0; @@ -3125,6 +3125,7 @@ int msDrawLabelCache(mapObj *map, imageObj *image) break; /* the marker collided, break from multi-label loop */ } } + if(have_label_marker == -1) return MS_FAILURE; /* error occured (symbol not found, etc...) */ if(textSymbolPtr->annotext) { /* diff --git a/mapfile.c b/mapfile.c index eb753ad2ff..9138b1ec2e 100644 --- a/mapfile.c +++ b/mapfile.c @@ -6637,17 +6637,6 @@ int msUpdateMapFromURL(mapObj *map, char *variable, char *string) switch(msyylex()) { case(MAP): switch(msyylex()) { - case(CONFIG): { - char *key=NULL, *value=NULL; - if((getString(&key) != MS_FAILURE) && (getString(&value) != MS_FAILURE)) { - msSetConfigOption( map, key, value ); - free( key ); - key=NULL; - free( value ); - value=NULL; - } - } - break; case(EXTENT): msyystate = MS_TOKENIZE_URL_STRING; msyystring = string; diff --git a/mapraster.c b/mapraster.c index 211c9c2935..b531d6bbee 100644 --- a/mapraster.c +++ b/mapraster.c @@ -984,16 +984,22 @@ imageObj *msDrawReferenceMap(mapObj *map) char szPath[MS_MAXPATHLEN]; int status = MS_SUCCESS; - imageObj *image = NULL; + imageObj *image = NULL; styleObj style; + /* check to see if we have enough information to actually proceed */ + if(!map->reference.image || map->reference.height == 0 || map->reference.width == 0) { + msSetError(MS_MISCERR, "Reference map configuration error.", "msDrawReferenceMap()"); + return NULL; + } rendererVTableObj *renderer = MS_MAP_RENDERER(map); rasterBufferObj *refImage = (rasterBufferObj*)calloc(1,sizeof(rasterBufferObj)); MS_CHECK_ALLOC(refImage, sizeof(rasterBufferObj), NULL); if(MS_SUCCESS != renderer->loadImageFromFile(msBuildPath(szPath, map->mappath, map->reference.image),refImage)) { - msSetError(MS_MISCERR,"error loading reference image %s","msDrawREferenceMap()",szPath); + msSetError(MS_MISCERR,"Error loading reference image %s.","msDrawReferenceMap()",szPath); + free(refImage); return NULL; } From 3c1a5c6ca2b0180de5a777fdcee86d79f146c728 Mon Sep 17 00:00:00 2001 From: MapServer-backport-bot <63150209+MapServer-backport-bot@users.noreply.github.com> Date: Fri, 16 Apr 2021 11:28:20 -0300 Subject: [PATCH 045/160] Require url-based symbol values to be pre-defined. (#6301) Co-authored-by: github-actions[bot] --- mapfile.c | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/mapfile.c b/mapfile.c index 9138b1ec2e..3d46e3d7d0 100644 --- a/mapfile.c +++ b/mapfile.c @@ -3375,12 +3375,15 @@ int loadClass(classObj *class, layerObj *layer) static int classResolveSymbolNames(classObj *class) { int i,j; + int try_addimage_if_notfound = MS_TRUE; + + if(msyysource == MS_URL_TOKENS) try_addimage_if_notfound = MS_FALSE; /* step through styles and labels to resolve symbol names */ /* class styles */ for(i=0; inumstyles; i++) { if(class->styles[i]->symbolname) { - if((class->styles[i]->symbol = msGetSymbolIndex(&(class->layer->map->symbolset), class->styles[i]->symbolname, MS_TRUE)) == -1) { + if((class->styles[i]->symbol = msGetSymbolIndex(&(class->layer->map->symbolset), class->styles[i]->symbolname, try_addimage_if_notfound)) == -1) { msSetError(MS_MISCERR, "Undefined symbol \"%s\" in class, style %d of layer %s.", "classResolveSymbolNames()", class->styles[i]->symbolname, i, class->layer->name); return MS_FAILURE; } @@ -3391,7 +3394,7 @@ static int classResolveSymbolNames(classObj *class) for(i=0; inumlabels; i++) { for(j=0; jlabels[i]->numstyles; j++) { if(class->labels[i]->styles[j]->symbolname) { - if((class->labels[i]->styles[j]->symbol = msGetSymbolIndex(&(class->layer->map->symbolset), class->labels[i]->styles[j]->symbolname, MS_TRUE)) == -1) { + if((class->labels[i]->styles[j]->symbol = msGetSymbolIndex(&(class->layer->map->symbolset), class->labels[i]->styles[j]->symbolname, try_addimage_if_notfound)) == -1) { msSetError(MS_MISCERR, "Undefined symbol \"%s\" in class, label style %d of layer %s.", "classResolveSymbolNames()", class->labels[i]->styles[j]->symbolname, j, class->layer->name); return MS_FAILURE; } @@ -6738,22 +6741,9 @@ int msUpdateMapFromURL(mapObj *map, char *variable, char *string) if(msUpdateLayerFromString((GET_LAYER(map, i)), string, MS_TRUE) != MS_SUCCESS) return MS_FAILURE; } - /* make sure any symbol names for this layer have been resolved (bug #2700) */ - for(j=0; jnumclasses; j++) { - for(k=0; kclass[j]->numstyles; k++) { - if(GET_LAYER(map, i)->class[j]->styles[k]->symbolname && GET_LAYER(map, i)->class[j]->styles[k]->symbol == 0) { - if((GET_LAYER(map, i)->class[j]->styles[k]->symbol = msGetSymbolIndex(&(map->symbolset), GET_LAYER(map, i)->class[j]->styles[k]->symbolname, MS_TRUE)) == -1) { - msSetError(MS_MISCERR, "Undefined symbol \"%s\" in class %d, style %d of layer %s.", "msUpdateMapFromURL()", GET_LAYER(map, i)->class[j]->styles[k]->symbolname, j, k, GET_LAYER(map, i)->name); - return MS_FAILURE; - } - } - if(!MS_IS_VALID_ARRAY_INDEX(GET_LAYER(map, i)->class[j]->styles[k]->symbol, map->symbolset.numsymbols)) { - msSetError(MS_MISCERR, "Invalid symbol index in class %d, style %d of layer %s.", "msUpdateMapFromURL()", j, k, GET_LAYER(map, i)->name); - return MS_FAILURE; - } - } - } - + // make sure symbols are resolved + if (resolveSymbolNames(map) == MS_FAILURE) return MS_FAILURE; + break; case(LEGEND): if(msyylex() == LABEL) { From b128dace3ec3e61bf063f7285d1279e9f9fd9e28 Mon Sep 17 00:00:00 2001 From: Steve Lime Date: Fri, 23 Apr 2021 03:21:01 -0500 Subject: [PATCH 046/160] Use CPLSetConfigOption/CPLGetConfigOption for some CGI/FastCGI-related env vars. (#6304) Push a few high-value env vars into CPL config and then reference that instead of the env (mostly for IIS/FastCGI). --- maphttp.c | 4 ++-- mapserv.c | 11 +++++++++++ mapserv.h | 1 + mapservutil.c | 13 +++++++++---- 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/maphttp.c b/maphttp.c index e539e22ffb..29ce444cdf 100644 --- a/maphttp.c +++ b/maphttp.c @@ -39,7 +39,7 @@ #include "mapthread.h" #include "mapows.h" - +#include "cpl_conv.h" #include #ifndef _WIN32 @@ -471,7 +471,7 @@ int msHTTPExecuteRequests(httpRequestObj *pasReqInfo, int numRequests, * If set then the value is the full path to the ca-bundle.crt file * e.g. CURL_CA_BUNDLE=/usr/local/share/curl/curl-ca-bundle.crt */ - pszCurlCABundle = getenv("CURL_CA_BUNDLE"); + pszCurlCABundle = CPLGetConfigOption("CURL_CA_BUNDLE", NULL); if (debug) { msDebug("HTTP: Starting to prepare HTTP requests.\n"); diff --git a/mapserv.c b/mapserv.c index 00603a6d55..11f9bfbddd 100644 --- a/mapserv.c +++ b/mapserv.c @@ -43,6 +43,8 @@ #include "mapio.h" #include "maptime.h" +#include "cpl_conv.h" + #ifndef WIN32 #include #endif @@ -162,6 +164,15 @@ int main(int argc, char *argv[]) if(msGetGlobalDebugLevel() >= MS_DEBUGLEVEL_TUNING) msGettimeofday(&execstarttime, NULL); + /* push high-value ENV vars into the CPL global config - primarily for IIS/FastCGI */ + const char* const apszEnvVars[] = { + "CURL_CA_BUNDLE", "MS_MAPFILE", "MS_MAP_NO_PATH", "MS_MAP_PATTERN", + NULL /* guard */ }; + for( int i = 0; apszEnvVars[i] != NULL; ++i ) { + const char* value = getenv(apszEnvVars[i]); + if(value) CPLSetConfigOption(apszEnvVars[i], value); + } + /* -------------------------------------------------------------------- */ /* Process arguments. In normal use as a cgi-bin there are no */ /* commandline switches, but we provide a few for test/debug */ diff --git a/mapserv.h b/mapserv.h index 845a544dac..27042a04e3 100644 --- a/mapserv.h +++ b/mapserv.h @@ -41,6 +41,7 @@ #include "maptile.h" #include "cgiutil.h" + /* ** Defines */ diff --git a/mapservutil.c b/mapservutil.c index 0ef4feca71..7f32479bd4 100644 --- a/mapservutil.c +++ b/mapservutil.c @@ -33,6 +33,8 @@ #include "maptime.h" #include "mapows.h" +#include "cpl_conv.h" + /* ** Enumerated types, keep the query modes in sequence and at the end of the enumeration (mode enumeration is in maptemplate.h). */ @@ -197,12 +199,15 @@ mapObj *msCGILoadMap(mapservObj *mapserv) int i, j; mapObj *map = NULL; + const char *ms_mapfile = CPLGetConfigOption("MS_MAPFILE", NULL); + const char *ms_map_no_path = CPLGetConfigOption("MS_MAP_NO_PATH", NULL); + const char *ms_map_pattern = CPLGetConfigOption("MS_MAP_PATTERN", NULL); + for(i=0; irequest->NumParams; i++) /* find the mapfile parameter first */ if(strcasecmp(mapserv->request->ParamNames[i], "map") == 0) break; if(i == mapserv->request->NumParams) { - char *ms_mapfile = getenv("MS_MAPFILE"); - if(ms_mapfile) { + if(ms_mapfile != NULL) { map = msLoadMap(ms_mapfile,NULL); } else { msSetError(MS_WEBERR, "CGI variable \"map\" is not set.", "msCGILoadMap()"); /* no default, outta here */ @@ -213,12 +218,12 @@ mapObj *msCGILoadMap(mapservObj *mapserv) map = msLoadMap(getenv(mapserv->request->ParamValues[i]), NULL); else { /* by here we know the request isn't for something in an environment variable */ - if(getenv("MS_MAP_NO_PATH")) { + if(ms_map_no_path != NULL) { msSetError(MS_WEBERR, "Mapfile not found in environment variables and this server is not configured for full paths.", "msCGILoadMap()"); return NULL; } - if(getenv("MS_MAP_PATTERN") && msEvalRegex(getenv("MS_MAP_PATTERN"), mapserv->request->ParamValues[i]) != MS_TRUE) { + if(ms_map_pattern != NULL && msEvalRegex(ms_map_pattern, mapserv->request->ParamValues[i]) != MS_TRUE) { msSetError(MS_WEBERR, "Parameter 'map' value fails to validate.", "msCGILoadMap()"); return NULL; } From f89e386ba5e1e23160d044e1f38f094b979ad303 Mon Sep 17 00:00:00 2001 From: MapServer-backport-bot <63150209+MapServer-backport-bot@users.noreply.github.com> Date: Sat, 24 Apr 2021 05:49:26 -0300 Subject: [PATCH 047/160] [Backport branch-7-6] Fix most of remaining Coverity scan warnings with high priority (#6307) --- mapcontext.c | 4 ++-- mapcpl.c | 2 ++ mapfile.c | 6 +----- mapogcsld.c | 4 ++-- mapogcsos.c | 6 ++++++ mapogr.cpp | 3 +++ mapstring.c | 16 ++++++++++------ mapwfs.c | 8 +++++--- 8 files changed, 31 insertions(+), 18 deletions(-) diff --git a/mapcontext.c b/mapcontext.c index 2eaa4d275d..aadc922d2a 100644 --- a/mapcontext.c +++ b/mapcontext.c @@ -811,7 +811,6 @@ int msLoadMapContextGeneral(mapObj *map, CPLXMLNode *psGeneral, int msLoadMapContextLayer(mapObj *map, CPLXMLNode *psLayer, int nVersion, char *filename, int unique_layer_names) { - char *pszProj=NULL; char *pszValue; const char *pszHash; char *pszName=NULL; @@ -970,7 +969,7 @@ int msLoadMapContextLayer(mapObj *map, CPLXMLNode *psLayer, int nVersion, pszHash = msLookupHashTable(&(layer->metadata), "wms_srs"); if(((pszHash == NULL) || (strcasecmp(pszHash, "") == 0)) && map->projection.numargs != 0) { - pszProj = map->projection.args[map->projection.numargs-1]; + char* pszProj = map->projection.args[map->projection.numargs-1]; if(pszProj != NULL) { if(strncasecmp(pszProj, "AUTO:", 5) == 0) { @@ -987,6 +986,7 @@ int msLoadMapContextLayer(mapObj *map, CPLXMLNode *psLayer, int nVersion, pszProj); } } + msFree(pszProj); } } diff --git a/mapcpl.c b/mapcpl.c index 1e09a244c5..9a2854dc35 100644 --- a/mapcpl.c +++ b/mapcpl.c @@ -195,6 +195,8 @@ void *msGetSymbol( const char * pszLibrary, const char * pszSymbolName ) return NULL; } + /* We accept leakage of pLibrary */ + /* coverity[leaked_storage] */ return( pSymbol ); } diff --git a/mapfile.c b/mapfile.c index 3d46e3d7d0..b3502e5ba3 100644 --- a/mapfile.c +++ b/mapfile.c @@ -1933,12 +1933,8 @@ char* msWriteLabelToString(labelObj *label) void msInitExpression(expressionObj *exp) { + memset(exp, 0, sizeof(*exp)); exp->type = MS_STRING; - exp->string = NULL; - exp->native_string = NULL; - exp->compiled = MS_FALSE; - exp->flags = 0; - exp->tokens = exp->curtoken = NULL; } void msFreeExpressionTokens(expressionObj *exp) diff --git a/mapogcsld.c b/mapogcsld.c index 9eefa6924f..71a54c2bcd 100644 --- a/mapogcsld.c +++ b/mapogcsld.c @@ -4716,8 +4716,8 @@ char *msSLDGetAttributeNameOrValue(char *pszExpression, } else pszAttributeName[iValue++] = pszExpression[i]; } - pszAttributeName[iValue] = '\0'; } + pszAttributeName[iValue] = '\0'; } msFreeCharArray(aszValues, nTokens); } else if (bOneCharCompare == 0) { @@ -4744,8 +4744,8 @@ char *msSLDGetAttributeNameOrValue(char *pszExpression, } else pszAttributeName[iValue++] = pszExpression[i]; } - pszAttributeName[iValue] = '\0'; } + pszAttributeName[iValue] = '\0'; } /* -------------------------------------------------------------------- */ diff --git a/mapogcsos.c b/mapogcsos.c index 95d32ad805..fd38e4e9f8 100644 --- a/mapogcsos.c +++ b/mapogcsos.c @@ -2931,16 +2931,22 @@ int msSOSParseRequest(mapObj *map, cgiRequestObj *request, sosParamsObj *sospara if (psXPathTmp) sosparams->pszRequest = msStrdup("GetCapabilities"); + xmlXPathFreeObject(psXPathTmp); + psXPathTmp = msLibXml2GetXPath(doc, context, (xmlChar *)"/sos:DescribeSensor"); if (psXPathTmp) sosparams->pszRequest = msStrdup("DescribeSensor"); + xmlXPathFreeObject(psXPathTmp); + psXPathTmp = msLibXml2GetXPath(doc, context, (xmlChar *)"/sos:GetObservation"); if (psXPathTmp) sosparams->pszRequest = msStrdup("GetObservation"); + xmlXPathFreeObject(psXPathTmp); + psXPathTmp = msLibXml2GetXPath(doc, context, (xmlChar *)"/sos:DescribeObservationType"); if (psXPathTmp) diff --git a/mapogr.cpp b/mapogr.cpp index c157ebe4a3..5f1d4dee7b 100644 --- a/mapogr.cpp +++ b/mapogr.cpp @@ -3080,7 +3080,10 @@ int msOGRFileReadTile( layerObj *layer, msOGRFileInfo *psInfo, #ifndef IGNORE_MISSING_DATA if( psTileInfo == NULL && targetTile == -1 ) + { + msFree(pszSRS); goto NextFile; + } #endif if( psTileInfo == NULL ) diff --git a/mapstring.c b/mapstring.c index 2401520817..148cd2bb5d 100644 --- a/mapstring.c +++ b/mapstring.c @@ -2184,7 +2184,7 @@ int msLayerEncodeShapeAttributes( layerObj *layer, shapeObj *shape) { iconv_t cd = NULL; const char *inp; char *outp, *out = NULL; - size_t len, bufsize, bufleft, iconv_status; + size_t len, bufsize, bufleft; int i; if( !layer->encoding || !*layer->encoding || !strcasecmp(layer->encoding, "UTF-8")) @@ -2198,6 +2198,7 @@ int msLayerEncodeShapeAttributes( layerObj *layer, shapeObj *shape) { } for(i=0;i numvalues; i++) { + int failedIconv = FALSE; if(!shape->values[i] || (len = strlen(shape->values[i]))==0) { continue; /* Nothing to do */ } @@ -2210,15 +2211,18 @@ int msLayerEncodeShapeAttributes( layerObj *layer, shapeObj *shape) { outp = out; bufleft = bufsize; - iconv_status = -1; while (len > 0) { - iconv_status = iconv(cd, (char**)&inp, &len, &outp, &bufleft); - if(iconv_status == -1) { - msFree(out); - continue; /* silently ignore failed conversions */ + const size_t iconv_status = iconv(cd, (char**)&inp, &len, &outp, &bufleft); + if(iconv_status == (size_t)(-1)) { + failedIconv = TRUE; + break; } } + if( failedIconv ) { + msFree(out); + continue; /* silently ignore failed conversions */ + } out[bufsize - bufleft] = '\0'; msFree(shape->values[i]); shape->values[i] = out; diff --git a/mapwfs.c b/mapwfs.c index 71e9127ce5..92bbccf7ea 100644 --- a/mapwfs.c +++ b/mapwfs.c @@ -5179,10 +5179,12 @@ int msWFSParseRequest(mapObj *map, cgiRequestObj *request, owsRequestObj *ows_re } /* these are unsupported requests. Just set the */ /* request value and return; */ - else if (msWFSGetIndexUnsupportedOperation(psOperation->pszValue) >= 0) { + else { int idx = msWFSGetIndexUnsupportedOperation(psOperation->pszValue); - wfsparams->pszRequest = msStrdup(wfsUnsupportedOperations[idx]); - break; + if( idx >= 0 ) { + wfsparams->pszRequest = msStrdup(wfsUnsupportedOperations[idx]); + break; + } } } } From 927ac97cb9ece305306b5ab2b5600d3afe8c1732 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Fri, 30 Apr 2021 22:30:45 +0200 Subject: [PATCH 048/160] Address flaw in CGI mapfile loading that makes it possible to bypass security controls (#6313) (#6314) (#6315) Co-authored-by: Even Rouault Co-authored-by: Steve Lime --- mapfile.c | 30 +++++++++++++++++++++++++++ mapserv.c | 3 ++- mapserver.h | 2 ++ mapservutil.c | 56 +++++++++++++++++++++++++++++++++++++-------------- 4 files changed, 75 insertions(+), 16 deletions(-) diff --git a/mapfile.c b/mapfile.c index b3502e5ba3..d10229758c 100644 --- a/mapfile.c +++ b/mapfile.c @@ -97,6 +97,16 @@ int msValidateParameter(const char *value, const char *pattern1, const char *pat return(MS_FAILURE); } +int msIsValidRegex(const char* e) { + ms_regex_t re; + if(ms_regcomp(&re, e, MS_REG_EXTENDED|MS_REG_NOSUB) != 0) { + msSetError(MS_REGEXERR, "Failed to compile expression (%s).", "msEvalRegex()", e); + return(MS_FALSE); + } + ms_regfree(&re); + return MS_TRUE; +} + int msEvalRegex(const char *e, const char *s) { ms_regex_t re; @@ -117,6 +127,26 @@ int msEvalRegex(const char *e, const char *s) return(MS_TRUE); } +int msCaseEvalRegex(const char *e, const char *s) +{ + ms_regex_t re; + + if(!e || !s) return(MS_FALSE); + + if(ms_regcomp(&re, e, MS_REG_EXTENDED|MS_REG_ICASE|MS_REG_NOSUB) != 0) { + msSetError(MS_REGEXERR, "Failed to compile expression (%s).", "msEvalRegex()", e); + return(MS_FALSE); + } + + if(ms_regexec(&re, s, 0, NULL, 0) != 0) { /* no match */ + ms_regfree(&re); + return(MS_FALSE); + } + ms_regfree(&re); + + return(MS_TRUE); +} + #ifdef USE_MSFREE void msFree(void *p) { diff --git a/mapserv.c b/mapserv.c index 11f9bfbddd..288af3af47 100644 --- a/mapserv.c +++ b/mapserv.c @@ -166,7 +166,8 @@ int main(int argc, char *argv[]) /* push high-value ENV vars into the CPL global config - primarily for IIS/FastCGI */ const char* const apszEnvVars[] = { - "CURL_CA_BUNDLE", "MS_MAPFILE", "MS_MAP_NO_PATH", "MS_MAP_PATTERN", + "CURL_CA_BUNDLE", "MS_MAPFILE", "MS_MAP_NO_PATH", "MS_MAP_PATTERN", "MS_MAP_ENV_PATTERN", + "MS_MAP_BAD_PATTERN", "MS_MAP_ENV_BAD_PATTERN", NULL /* guard */ }; for( int i = 0; apszEnvVars[i] != NULL; ++i ) { const char* value = getenv(apszEnvVars[i]); diff --git a/mapserver.h b/mapserver.h index bef6245028..143b9c54b0 100644 --- a/mapserver.h +++ b/mapserver.h @@ -2159,7 +2159,9 @@ void msPopulateTextSymbolForLabelAndString(textSymbolObj *ts, labelObj *l, char MS_DLL_EXPORT char *msWriteReferenceMapToString(referenceMapObj *ref); MS_DLL_EXPORT char *msWriteLegendToString(legendObj *legend); MS_DLL_EXPORT char *msWriteClusterToString(clusterObj *cluster); + MS_DLL_EXPORT int msIsValidRegex(const char* e); MS_DLL_EXPORT int msEvalRegex(const char *e, const char *s); + MS_DLL_EXPORT int msCaseEvalRegex(const char *e, const char *s); #ifdef USE_MSFREE MS_DLL_EXPORT void msFree(void *p); #else diff --git a/mapservutil.c b/mapservutil.c index 7f32479bd4..1d69104d10 100644 --- a/mapservutil.c +++ b/mapservutil.c @@ -199,41 +199,67 @@ mapObj *msCGILoadMap(mapservObj *mapserv) int i, j; mapObj *map = NULL; + const char *ms_map_bad_pattern_default = "[/\\]{2}|[/\\]?\\.+[/\\]|,"; + const char *ms_map_env_bad_pattern_default = "^(AUTH_.*|CERT_.*|CONTENT_(LENGTH|TYPE)|DOCUMENT_(ROOT|URI)|GATEWAY_INTERFACE|HTTP.*|QUERY_STRING|PATH_(INFO|TRANSLATED)|REMOTE_.*|REQUEST_(METHOD|URI)|SCRIPT_(FILENAME|NAME)|SERVER_.*)"; + + int ms_mapfile_tainted = MS_TRUE; const char *ms_mapfile = CPLGetConfigOption("MS_MAPFILE", NULL); + const char *ms_map_no_path = CPLGetConfigOption("MS_MAP_NO_PATH", NULL); const char *ms_map_pattern = CPLGetConfigOption("MS_MAP_PATTERN", NULL); + const char *ms_map_env_pattern = CPLGetConfigOption("MS_MAP_ENV_PATTERN", NULL); + + const char *ms_map_bad_pattern = CPLGetConfigOption("MS_MAP_BAD_PATTERN", NULL); + if(ms_map_bad_pattern == NULL) ms_map_bad_pattern = ms_map_bad_pattern_default; + + const char *ms_map_env_bad_pattern = CPLGetConfigOption("MS_MAP_ENV_BAD_PATTERN", NULL); + if(ms_map_env_bad_pattern == NULL) ms_map_env_bad_pattern = ms_map_env_bad_pattern_default; for(i=0; irequest->NumParams; i++) /* find the mapfile parameter first */ if(strcasecmp(mapserv->request->ParamNames[i], "map") == 0) break; if(i == mapserv->request->NumParams) { - if(ms_mapfile != NULL) { - map = msLoadMap(ms_mapfile,NULL); - } else { + if(ms_mapfile == NULL) { msSetError(MS_WEBERR, "CGI variable \"map\" is not set.", "msCGILoadMap()"); /* no default, outta here */ return NULL; } + ms_mapfile_tainted = MS_FALSE; } else { - if(getenv(mapserv->request->ParamValues[i])) /* an environment variable references the actual file to use */ - map = msLoadMap(getenv(mapserv->request->ParamValues[i]), NULL); - else { - /* by here we know the request isn't for something in an environment variable */ - if(ms_map_no_path != NULL) { - msSetError(MS_WEBERR, "Mapfile not found in environment variables and this server is not configured for full paths.", "msCGILoadMap()"); + if(getenv(mapserv->request->ParamValues[i])) { /* an environment variable references the actual file to use */ + /* validate env variable name */ + if(msIsValidRegex(ms_map_env_bad_pattern) == MS_FALSE || msCaseEvalRegex(ms_map_env_bad_pattern, mapserv->request->ParamValues[i]) == MS_TRUE) { + msSetError(MS_WEBERR, "CGI variable \"map\" fails to validate.", "msCGILoadMap()"); return NULL; } - - if(ms_map_pattern != NULL && msEvalRegex(ms_map_pattern, mapserv->request->ParamValues[i]) != MS_TRUE) { - msSetError(MS_WEBERR, "Parameter 'map' value fails to validate.", "msCGILoadMap()"); + if(ms_map_env_pattern != NULL && msEvalRegex(ms_map_env_pattern, mapserv->request->ParamValues[i]) != MS_TRUE) { + msSetError(MS_WEBERR, "CGI variable \"map\" fails to validate.", "msCGILoadMap()"); return NULL; } + ms_mapfile = getenv(mapserv->request->ParamValues[i]); + } else { + /* by now we know the request isn't for something in an environment variable */ + if(ms_map_no_path != NULL) { + msSetError(MS_WEBERR, "CGI variable \"map\" not found in environment and this server is not configured for full paths.", "msCGILoadMap()"); + return NULL; + } + ms_mapfile = mapserv->request->ParamValues[i]; + } + } - /* ok to try to load now */ - map = msLoadMap(mapserv->request->ParamValues[i], NULL); + /* validate ms_mapfile if tainted */ + if(ms_mapfile_tainted == MS_TRUE) { + if(msIsValidRegex(ms_map_bad_pattern) == MS_FALSE || msEvalRegex(ms_map_bad_pattern, ms_mapfile) == MS_TRUE) { + msSetError(MS_WEBERR, "CGI variable \"map\" fails to validate.", "msCGILoadMap()"); + return NULL; + } + if(ms_map_pattern != NULL && msEvalRegex(ms_map_pattern, ms_mapfile) != MS_TRUE) { + msSetError(MS_WEBERR, "CGI variable \"map\" fails to validate.", "msCGILoadMap()"); + return NULL; } } - + /* ok to try to load now */ + map = msLoadMap(ms_mapfile, NULL); if(!map) return NULL; if(!msLookupHashTable(&(map->web.validation), "immutable")) { From 7c0572445549e176a76502e381cf936f62f1266f Mon Sep 17 00:00:00 2001 From: Jeff McKenna Date: Fri, 30 Apr 2021 18:26:25 -0300 Subject: [PATCH 049/160] update for 7.6.3 release --- CMakeLists.txt | 2 +- HISTORY.TXT | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 11d42bf771..265902ac32 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ include(CheckCSourceCompiles) set (MapServer_VERSION_MAJOR 7) set (MapServer_VERSION_MINOR 6) -set (MapServer_VERSION_REVISION 2) +set (MapServer_VERSION_REVISION 3) set (MapServer_VERSION_SUFFIX "") # Set C++ version diff --git a/HISTORY.TXT b/HISTORY.TXT index 68d800401b..b801625b2c 100644 --- a/HISTORY.TXT +++ b/HISTORY.TXT @@ -12,6 +12,17 @@ For a complete change history, please see the Git log comments. For more details about recent point releases, please see the online changelog at: http://mapserver.org/development/changelog/ +7.6.3 release (2021-04-30) +------------------------- + +- fix security flaw for processing the MAP parameter (#6313) + +- fix code defects through Coverity Scan warnings (#6307) + +- add support for PROJ 8 (#6249) + +see detailed changelog for other fixes + 7.6.2 release (2020-12-07) ------------------------- From 9755395b72d1b05a87d5393f3cc937bdc3f1b587 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 4 May 2021 17:02:33 +0200 Subject: [PATCH 050/160] mapshape: remove useless null terminator It's pointless to null-terminate the buffer if strcpy() overwrites it. --- mapshape.c | 1 - 1 file changed, 1 deletion(-) diff --git a/mapshape.c b/mapshape.c index a70d858232..192f7b3651 100644 --- a/mapshape.c +++ b/mapshape.c @@ -1730,7 +1730,6 @@ int msShapefileOpen(shapefileObj *shpfile, const char *mode, const char *filenam bufferSize = strlen(filename)+5; dbfFilename = (char *)msSmallMalloc(bufferSize); - dbfFilename[0] = '\0'; strcpy(dbfFilename, filename); /* clean off any extention the filename might have */ From b5c0e29ea2b8b0253e409cdba741f09246675e74 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 4 May 2021 17:01:00 +0200 Subject: [PATCH 051/160] mapshape: validate numshapes If the value is implausible, refuse to parse the file, because that value will be used later in memory allocations. I used the same limit as in msSHPOpen(). DoS vulnerability found with libFuzzer. --- mapshape.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mapshape.c b/mapshape.c index 192f7b3651..f6450796b7 100644 --- a/mapshape.c +++ b/mapshape.c @@ -1726,6 +1726,14 @@ int msShapefileOpen(shapefileObj *shpfile, const char *mode, const char *filenam /* load some information about this shapefile */ msSHPGetInfo( shpfile->hSHP, &shpfile->numshapes, &shpfile->type); + + if( shpfile->numshapes < 0 || shpfile->numshapes > 256000000 ) { + msSetError(MS_SHPERR, "Corrupted .shp file : numshapes = %d.", + "msShapefileOpen()", shpfile->numshapes); + msSHPClose(shpfile->hSHP); + return -1; + } + msSHPReadBounds( shpfile->hSHP, -1, &(shpfile->bounds)); bufferSize = strlen(filename)+5; From 5e4c5046f294a67865a9d21afab2f212849b94d0 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 4 May 2021 17:00:15 +0200 Subject: [PATCH 052/160] mapshape, mapxbase: fix several memory leaks in error code paths Leak bug found with libFuzzer. --- mapshape.c | 1 + mapxbase.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/mapshape.c b/mapshape.c index f6450796b7..0a607d052d 100644 --- a/mapshape.c +++ b/mapshape.c @@ -1756,6 +1756,7 @@ int msShapefileOpen(shapefileObj *shpfile, const char *mode, const char *filenam if( log_failures ) msSetError(MS_IOERR, "(%s)", "msShapefileOpen()", dbfFilename); free(dbfFilename); + msSHPClose(shpfile->hSHP); return(-1); } free(dbfFilename); diff --git a/mapxbase.c b/mapxbase.c index c283259693..25a9457436 100644 --- a/mapxbase.c +++ b/mapxbase.c @@ -192,6 +192,7 @@ DBFHandle msDBFOpen( const char * pszFilename, const char * pszAccess ) pabyBuf = (uchar *) msSmallMalloc(500); if( VSIFReadL( pabyBuf, 32, 1, psDBF->fp ) != 1 ) { + VSIFCloseL( psDBF->fp ); msFree(psDBF); msFree(pabyBuf); return( NULL ); @@ -217,6 +218,7 @@ DBFHandle msDBFOpen( const char * pszFilename, const char * pszAccess ) if( VSIFReadL( pabyBuf, nHeadLen - 32, 1, psDBF->fp ) != 1 ) { msFree(psDBF->pszCurrentRecord); + VSIFCloseL( psDBF->fp ); msFree(psDBF); msFree(pabyBuf); return( NULL ); From ee5d5de5140ee9b42b817f93f74741c64eebca30 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 4 May 2021 17:09:01 +0200 Subject: [PATCH 053/160] mapxbase: validate nHeadLen Make sure the "nFields" formula doesn't underflow, leading to a multi-gigabyte memory allocation and probably a heap buffer overflow. Vulnerability found with libFuzzer. --- mapxbase.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mapxbase.c b/mapxbase.c index 25a9457436..dd813671cd 100644 --- a/mapxbase.c +++ b/mapxbase.c @@ -204,6 +204,13 @@ DBFHandle msDBFOpen( const char * pszFilename, const char * pszAccess ) psDBF->nHeaderLength = nHeadLen = pabyBuf[8] + pabyBuf[9]*256; psDBF->nRecordLength = nRecLen = pabyBuf[10] + pabyBuf[11]*256; + if (nHeadLen <= 32) { + VSIFCloseL( psDBF->fp ); + msFree(psDBF); + msFree(pabyBuf); + return( NULL ); + } + psDBF->nFields = nFields = (nHeadLen - 32) / 32; psDBF->pszCurrentRecord = (char *) msSmallMalloc(nRecLen); From 782ee8bacb7f322fd603383fc54040521f995b02 Mon Sep 17 00:00:00 2001 From: Tamas Szekeres Date: Mon, 3 May 2021 11:21:09 +0200 Subject: [PATCH 054/160] Fix querymap failure for polygon layers with labels only (no styles) (#6262) --- mapdraw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mapdraw.c b/mapdraw.c index 1d07d74be9..5fc1bd4bf3 100644 --- a/mapdraw.c +++ b/mapdraw.c @@ -1362,7 +1362,7 @@ int msDrawQueryLayer(mapObj *map, layerObj *layer, imageObj *image) } for(i=0; inumclasses; i++) { - if(layer->type == MS_LAYER_POLYGON) { /* alter BOTTOM style since that's almost always the fill */ + if(layer->type == MS_LAYER_POLYGON && layer->class[i]->numstyles > 0) { /* alter BOTTOM style since that's almost always the fill */ if (layer->class[i]->styles == NULL) { msSetError(MS_MISCERR, "Don't know how to draw class %s of layer %s without a style definition.", "msDrawQueryLayer()", layer->class[i]->name, layer->name); msFree(colorbuffer); @@ -1521,7 +1521,7 @@ int msDrawQueryLayer(mapObj *map, layerObj *layer, imageObj *image) /* if MS_HILITE, restore color and mindistance values */ if(map->querymap.style == MS_HILITE) { for(i=0; inumclasses; i++) { - if(layer->type == MS_LAYER_POLYGON) { + if(layer->type == MS_LAYER_POLYGON && layer->class[i]->numstyles > 0) { if(MS_VALID_COLOR(layer->class[i]->styles[0]->color)) layer->class[i]->styles[0]->color = colorbuffer[i]; else if(MS_VALID_COLOR(layer->class[i]->styles[0]->outlinecolor)) From 91b01e2a08c74b2865589cec06cb92f65002498b Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 12 May 2021 17:26:48 +0200 Subject: [PATCH 055/160] Add test case showing the need of a fix for #6325 (lack of next link) --- msautotest/wxs/data/test_6325.gpkg | Bin 0 -> 98304 bytes .../wxs/expected/wfs_ogr_gpkg_issue_6325.xml | 35 ++++++++++++++++++ msautotest/wxs/wfs_ogr_gpkg.map | 21 +++++++++++ 3 files changed, 56 insertions(+) create mode 100644 msautotest/wxs/data/test_6325.gpkg create mode 100644 msautotest/wxs/expected/wfs_ogr_gpkg_issue_6325.xml diff --git a/msautotest/wxs/data/test_6325.gpkg b/msautotest/wxs/data/test_6325.gpkg new file mode 100644 index 0000000000000000000000000000000000000000..74b3aa66160678ba6b520ea4f813b9fdf67fefc6 GIT binary patch literal 98304 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 Mj2n%BqcIcz5B0_n + + + + 0.00000 0.00000 + 10.00000 10.00000 + + + + + + + 0.00000 0.00000 + 10.00000 10.00000 + + + + + 0.00000 0.00000 10.00000 10.00000 + + + 2 + LINESTRING(0 0,10 10) + + + + diff --git a/msautotest/wxs/wfs_ogr_gpkg.map b/msautotest/wxs/wfs_ogr_gpkg.map index 93461b6633..d93f1b29c9 100644 --- a/msautotest/wxs/wfs_ogr_gpkg.map +++ b/msautotest/wxs/wfs_ogr_gpkg.map @@ -8,6 +8,9 @@ # Filter using startIndex # RUN_PARMS: wfs_ogr_gpkg_filter_startindex.xml [MAPSERV] QUERY_STRING="map=[MAPFILE]&SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME=popplace&propertyname=(name)&maxfeatures=10&startindex=0" > [RESULT] # RUN_PARMS: wfs_ogr_gpkg_filter_startindex2.xml [MAPSERV] QUERY_STRING="map=[MAPFILE]&SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME=popplace&propertyname=(name)&maxfeatures=10&startindex=10" > [RESULT] +# +# RUN_PARMS: wfs_ogr_gpkg_issue_6325.xml [MAPSERV] QUERY_STRING="map=[MAPFILE]&SERVICE=WFS&VERSION=2.0.0&REQUEST=GetFeature&TYPENAMES=test_6325&BBOX=0.75,0.75,9,9&COUNT=1" > [RESULT_DEVERSION] +# MAP @@ -82,4 +85,22 @@ LAYER END END # Layer + +LAYER + NAME "test_6325" + CONNECTIONTYPE OGR + CONNECTION "./data/test_6325.gpkg" + METADATA + "ows_title" "test_6325" + "wfs_featureid" "id" + "gml_include_items" "all" + "gml_types" "auto" + END + TYPE LINE + STATUS ON + PROJECTION + "init=epsg:4326" + END +END # Layer + END # Map File From ce8dc4df3792700332299540f7703b79e30ba353 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 12 May 2021 17:31:18 +0200 Subject: [PATCH 056/160] WFS: fix paging with GPKG/Spatialite datasources and non-point geometries (fixes #6325) --- mapogr.cpp | 24 +++++++++++++++---- .../wxs/expected/wfs_ogr_gpkg_issue_6325.xml | 3 ++- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/mapogr.cpp b/mapogr.cpp index 5f1d4dee7b..09b1d47730 100644 --- a/mapogr.cpp +++ b/mapogr.cpp @@ -2368,16 +2368,32 @@ static int msOGRFileWhichShapes(layerObj *layer, rectObj rect, msOGRFileInfo *ps msFree(points); filter = msStringConcatenate(filter, "))"); } - else if( psInfo->dialect && EQUAL(psInfo->dialect, "Spatialite") && - psInfo->pszMainTableName != NULL && !psInfo->bHasSpatialIndex ) + else if( psInfo->dialect && + (EQUAL(psInfo->dialect, "Spatialite") || + EQUAL(psInfo->dialect, "GPKG")) && + psInfo->pszMainTableName != NULL ) { + const bool isGPKG = EQUAL(psInfo->dialect, "GPKG"); if (filter) filter = msStringConcatenate(filter, " AND"); const char *col = OGR_L_GetGeometryColumn(psInfo->hLayer); // which geom field?? - filter = msStringConcatenate(filter, " MbrIntersects(\""); + filter = msStringConcatenate(filter, " Intersects("); + if( isGPKG ) + { + // Casting GeoPackage geometries to spatialie ones is done + // automatically normally, since GDAL enables the + // "amphibious" mode, but without it + // explicilty specified, spatialite 4.3.0a does an + // out-of-bounds access. + filter = msStringConcatenate(filter, "GeomFromGPB("); + } + filter = msStringConcatenate(filter, "\""); char* escaped = msLayerEscapePropertyName(layer, col); filter = msStringConcatenate(filter, escaped); msFree(escaped); - filter = msStringConcatenate(filter, "\", BuildMbr("); + filter = msStringConcatenate(filter, "\""); + if( isGPKG ) + filter = msStringConcatenate(filter, ")"); + filter = msStringConcatenate(filter, ", BuildMbr("); char *points = (char *)msSmallMalloc(30*2*5); snprintf(points, 30*4, "%lf,%lf,%lf,%lf", rect.minx, rect.miny, rect.maxx, rect.maxy); filter = msStringConcatenate(filter, points); diff --git a/msautotest/wxs/expected/wfs_ogr_gpkg_issue_6325.xml b/msautotest/wxs/expected/wfs_ogr_gpkg_issue_6325.xml index 7dfa519cdb..e8ede1b19c 100644 --- a/msautotest/wxs/expected/wfs_ogr_gpkg_issue_6325.xml +++ b/msautotest/wxs/expected/wfs_ogr_gpkg_issue_6325.xml @@ -7,7 +7,8 @@ Content-Type: text/xml; subtype="gml/3.2.1"; charset=UTF-8 xmlns:wfs="http://www.opengis.net/wfs/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://mapserver.gis.umn.edu/mapserver http://localhost/path/to/wfs_simple?SERVICE=WFS&VERSION=2.0.0&REQUEST=DescribeFeatureType&TYPENAME=test_6325&OUTPUTFORMAT=application%2Fgml%2Bxml%3B%20version%3D3.2 http://www.opengis.net/wfs/2.0 http://schemas.opengis.net/wfs/2.0/wfs.xsd http://www.opengis.net/gml/3.2 http://schemas.opengis.net/gml/3.2.1/gml.xsd" - timeStamp="" numberMatched="unknown" numberReturned="1"> + timeStamp="" numberMatched="unknown" numberReturned="1" + next="http://localhost/path/to/wfs_simple?SERVICE=WFS&VERSION=2.0.0&REQUEST=GetFeature&TYPENAMES=test_6325&BBOX=0.75%2C0.75%2C9%2C9&COUNT=1&STARTINDEX=1"> 0.00000 0.00000 From cf4756ec0c748c6656f3ca7d0ec7aef2ddf1265b Mon Sep 17 00:00:00 2001 From: Krister Wicksell Date: Wed, 12 May 2021 14:55:30 +0200 Subject: [PATCH 057/160] When a font is missing a glyph we try to fallback to using a question mark As it is now MapServer will fail to render if a layer is using characters that's not available in the font. This commit resolves this by trying to fallback to using a question mark in these situations. --- fontcache.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/fontcache.c b/fontcache.c index 28c5c56899..75ca778bfc 100644 --- a/fontcache.c +++ b/fontcache.c @@ -287,6 +287,12 @@ glyph_element* msGetGlyphByIndex(face_element *face, unsigned int size, unsigned FT_Set_Pixel_Sizes(face->face,0,MS_NINT(size * 96/72.0)); } error = FT_Load_Glyph(face->face,key.codepoint,FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP|FT_LOAD_NO_HINTING|FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH); + if (error) { + msDebug("Unable to load glyph %ud for font \"%s\". Using ? as fallback.", key.codepoint, face->font); + // If we can't find a glyph then try to fallback to a question mark. + unsigned int fallbackCodepoint = msGetGlyphIndex(face, 0x3F); + error = FT_Load_Glyph(face->face,fallbackCodepoint,FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP|FT_LOAD_NO_HINTING|FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH); + } if(error) { msSetError(MS_MISCERR, "unable to load glyph %ud for font \"%s\"", "msGetGlyphByIndex()",key.codepoint, face->font); free(gc); @@ -335,8 +341,14 @@ outline_element* msGetGlyphOutline(face_element *face, glyph_element *glyph) { pen.x = pen.y = 0; FT_Set_Transform(face->face, &matrix, &pen); error = FT_Load_Glyph(face->face,glyph->key.codepoint,FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP/*|FT_LOAD_IGNORE_TRANSFORM*/|FT_LOAD_NO_HINTING|FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH); + if (error) { + msDebug("Unable to load glyph %ud for font \"%s\". Using ? as fallback.", glyph->key.codepoint, face->font); + // If we can't find a glyph then try to fallback to a question mark. + unsigned int fallbackCodepoint = msGetGlyphIndex(face, 0x3F); + error = FT_Load_Glyph(face->face,fallbackCodepoint,FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP/*|FT_LOAD_IGNORE_TRANSFORM*/|FT_LOAD_NO_HINTING|FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH); + } if(error) { - msSetError(MS_MISCERR, "unable to load glyph %ud for font \"%s\"", "msGetGlyphByIndex()",glyph->key.codepoint, face->font); + msSetError(MS_MISCERR, "unable to load glyph %ud for font \"%s\"", "msGetGlyphOutline()",glyph->key.codepoint, face->font); #ifdef USE_THREAD if (use_global_ft_cache) msReleaseLock(TLOCK_TTF); From 0bbf943df3d81579f883f45d08ceb72f84f4236b Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sun, 23 May 2021 15:23:50 +0200 Subject: [PATCH 058/160] msGetGlyphByIndex() / msGetGlyphOutline(): tweak debug/error messages --- fontcache.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fontcache.c b/fontcache.c index 75ca778bfc..bf5c81353d 100644 --- a/fontcache.c +++ b/fontcache.c @@ -288,13 +288,13 @@ glyph_element* msGetGlyphByIndex(face_element *face, unsigned int size, unsigned } error = FT_Load_Glyph(face->face,key.codepoint,FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP|FT_LOAD_NO_HINTING|FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH); if (error) { - msDebug("Unable to load glyph %ud for font \"%s\". Using ? as fallback.", key.codepoint, face->font); + msDebug("Unable to load glyph %u for font \"%s\". Using ? as fallback.\n", key.codepoint, face->font); // If we can't find a glyph then try to fallback to a question mark. unsigned int fallbackCodepoint = msGetGlyphIndex(face, 0x3F); error = FT_Load_Glyph(face->face,fallbackCodepoint,FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP|FT_LOAD_NO_HINTING|FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH); } if(error) { - msSetError(MS_MISCERR, "unable to load glyph %ud for font \"%s\"", "msGetGlyphByIndex()",key.codepoint, face->font); + msSetError(MS_MISCERR, "unable to load glyph %u for font \"%s\"", "msGetGlyphByIndex()",key.codepoint, face->font); free(gc); #ifdef USE_THREAD if (use_global_ft_cache) @@ -342,13 +342,13 @@ outline_element* msGetGlyphOutline(face_element *face, glyph_element *glyph) { FT_Set_Transform(face->face, &matrix, &pen); error = FT_Load_Glyph(face->face,glyph->key.codepoint,FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP/*|FT_LOAD_IGNORE_TRANSFORM*/|FT_LOAD_NO_HINTING|FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH); if (error) { - msDebug("Unable to load glyph %ud for font \"%s\". Using ? as fallback.", glyph->key.codepoint, face->font); + msDebug("Unable to load glyph %u for font \"%s\". Using ? as fallback.\n", glyph->key.codepoint, face->font); // If we can't find a glyph then try to fallback to a question mark. unsigned int fallbackCodepoint = msGetGlyphIndex(face, 0x3F); error = FT_Load_Glyph(face->face,fallbackCodepoint,FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP/*|FT_LOAD_IGNORE_TRANSFORM*/|FT_LOAD_NO_HINTING|FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH); } if(error) { - msSetError(MS_MISCERR, "unable to load glyph %ud for font \"%s\"", "msGetGlyphOutline()",glyph->key.codepoint, face->font); + msSetError(MS_MISCERR, "unable to load glyph %u for font \"%s\"", "msGetGlyphOutline()",glyph->key.codepoint, face->font); #ifdef USE_THREAD if (use_global_ft_cache) msReleaseLock(TLOCK_TTF); From 53ee29ed152edb5f940faad254ee38a8c9120513 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sun, 23 May 2021 15:03:35 +0200 Subject: [PATCH 059/160] check_single_font(): use quotation mark when msGetGlyphIndex() fails to retrieve the glyph --- .../expected/font-fail-missing-glyph.png | Bin 0 -> 2307 bytes msautotest/renderers/font-fail.map | 18 ++++++++++++++++++ textlayout.c | 13 ++++++++++--- 3 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 msautotest/renderers/expected/font-fail-missing-glyph.png diff --git a/msautotest/renderers/expected/font-fail-missing-glyph.png b/msautotest/renderers/expected/font-fail-missing-glyph.png new file mode 100644 index 0000000000000000000000000000000000000000..611bdedc0e74767789f1c98f1f7fa5176e6ba179 GIT binary patch literal 2307 zcmd^B_dDBd7pHF?n>JFTlvd2Dnze!`qGF{K#e*V76@7>q^C60d)Cj8f=s=AUtr^6u zR-@V)jS-3%ZDQ44?fZRyeE){$hx&pG!=u|k>fa*1%Uu(0r&A`!O% z`wm#S9A|+2e3uo9g@t3t6k%YCEn3U7tFki}9vmq=D-Y!mtWuJf=fa5vG*f9*7dH<# z_Eef!)u2aQC=%@(yDdz+pBd{BOfjoW}gH1#y!-h zv2OiFpgAE@xylCr4kIy(b|Hn;Sr%%<9Pg#I{k^?ByuC6~c{;5bFw3NozEQ+Io9!x`pwz{+5}UnT5s4$)Q?!7?V1GcZdES^24*%lMb|Eh@^FDVH&Ixm zVhwov)tL-&c=YNIH4x}OXIT2?Ma$u#GyKT_A?vf9$D8wgrSO>42(kFI7a4;p7~FAZ zZCi`^^>obL@@`6xYfn{NN+n_ukd(Qa=d zH^FDstQ4+i!&qP+dufyWa_LH_+!sS2#om&Gf*0mb?l!o^EAd@|12KD<|Fgio6I)VLcg$EDc0B}L@>K*2IjlH7kXQZKFFCeIx)2GIGnAhSB z-Tnm~fTfR()9s;Gjp7`ZK0LV>K^$xJ9v-quDl^6l`V$DNW4H5l0My-w4{x6HdHd*s z0_K%tt=CYFL>%}eYG)PPg?Ds6Ko!Hr+d~f|N>Pu$< zR)_J_180J?JrQjT+7)2q73r5fhSt0Q^bgXor$dB6WO=MLpfFCiF8JJ+dRKi{&j@8E zkIPVr!@0`32JH{3hzBK4MGO`a_?5B%B-eV`5#tnGZkVXyPwoCgJFUj%DS}fW zSeREEipBIIuP?TrL?4K?yb|N|IlC;Ni;IZ^gzZCgtlNpm5Y6);14Sf>V6Z>Z+yT)2qjs;_8fUT zu6P;C0>lu%hT$U&T0%P3lafd1`AO{QkzCFS=M{tHU?HPvR<#O_jaJF@NI(f8DLM|1 zuh>+WJ(d^K#eN#+9553=3q)lrWnF%)c?g{X+ulB%rxHE-eEKU1ZimoWpg?3Ppnsa^ z54w=mD8#-oTfC*RKiTtrmj6_h|_fl!4{)diG3n8VNJbRH=E zllwCKmX)oGcxDDO!TmG^BHI{x1l4A7YEa6GMCGeW*WSW%FG3P5I&Li*#p+=A{kr2= zW67nkQ2Fn5q*s47$+uk-dS;R#Gn|TmaJNVXl7W(PmwW_YsYmWKy#1`oe=2J{+Y!+R z14;Cw>(tiUf>+YNvl-wZ`6O?dYP*_c+*9*d)1;Ya3j=tPbERP-7rY*=a@o%!0i>HH z@9Oz`H$O literal 0 HcmV?d00001 diff --git a/msautotest/renderers/font-fail.map b/msautotest/renderers/font-fail.map index 9271e3dfe8..0f86e9850f 100644 --- a/msautotest/renderers/font-fail.map +++ b/msautotest/renderers/font-fail.map @@ -1,5 +1,6 @@ # RUN_PARMS: font-fail-key.txt [MAPSERV] QUERY_STRING="map=[MAPFILE]&mode=map&layer=l1" > [RESULT_DEVERSION] # RUN_PARMS: font-fail-file.txt [MAPSERV] QUERY_STRING="map=[MAPFILE]&mode=map&layer=l2" > [RESULT_DEVERSION] +# RUN_PARMS: font-fail-missing-glyph.png [SHP2IMG] -m [MAPFILE] -l missing_glyph -i png -o [RESULT] MAP @@ -41,4 +42,21 @@ LAYER FEATURE POINTS 50 50 END END END +LAYER + NAME "missing_glyph" + STATUS ON + TYPE POLYGON + FEATURE + POINTS 0 0 400 0 400 300 0 300 0 0 END + END + CLASS + LABEL + TEXT 'this is a test: это проверка' + TYPE truetype + SIZE 8 + FONT "default" + END + END +END + END diff --git a/textlayout.c b/textlayout.c index 3fc706b0fb..2b73564cb9 100644 --- a/textlayout.c +++ b/textlayout.c @@ -214,7 +214,7 @@ static hb_position_t _ms_get_glyph_v_advance_func (hb_font_t *font, void *font_d } #endif -int WARN_UNUSED check_single_font(fontSetObj *fontset, char *fontkey, text_run *run, TextInfo *glyphs, int ignore_missing) { +static int check_single_font(fontSetObj *fontset, char *fontkey, text_run *run, TextInfo *glyphs, int ignore_missing) { int i; face_element *fcache = NULL; if(fontset && fontkey) { @@ -228,16 +228,23 @@ int WARN_UNUSED check_single_font(fontSetObj *fontset, char *fontkey, text_run * run->face = fcache; if(UNLIKELY(!fcache)) return MS_FAILURE; for(i=0; ilength; i++) { - int codepoint = msGetGlyphIndex(fcache, glyphs->unicodes[run->offset+i]); + unsigned int codepoint = msGetGlyphIndex(fcache, glyphs->unicodes[run->offset+i]); if(codepoint || ignore_missing) + { + if( codepoint == 0 ) + { + msDebug("Unable to find glyph for codepoint %u. Using ? as fallback.\n", glyphs->unicodes[run->offset+i]); + codepoint = msGetGlyphIndex(fcache, '?'); + } glyphs->codepoints[run->offset+i] = codepoint; + } else return MS_FAILURE; } return MS_SUCCESS; } -int WARN_UNUSED get_face_for_run(fontSetObj *fontset, char *fontlist, text_run *run, TextInfo *glyphs) { +static int get_face_for_run(fontSetObj *fontset, char *fontlist, text_run *run, TextInfo *glyphs) { char *startfont, *endfont; int ok; #if defined(USE_HARFBUZZ) && defined(USE_FRIBIDI) From e2ef15504f6e4e79537467568134b0bb4d5bdb8a Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 8 Jun 2021 17:46:19 +0200 Subject: [PATCH 060/160] PostGIS: use ST_Intersects instead of && for bounding box (fixes #6181, fixes #6230) --- mappostgis.c | 26 ++++++-- msautotest/create_postgis_test_data.sh | 8 +++ .../wxs/expected/wfs_postgis_issue_6181.xml | 35 +++++++++++ .../wfs_postgis_issue_6181_startindex_1.xml | 35 +++++++++++ msautotest/wxs/wfs_postgis_paging.map | 62 +++++++++++++++++++ 5 files changed, 160 insertions(+), 6 deletions(-) create mode 100644 msautotest/wxs/expected/wfs_postgis_issue_6181.xml create mode 100644 msautotest/wxs/expected/wfs_postgis_issue_6181_startindex_1.xml create mode 100644 msautotest/wxs/wfs_postgis_paging.map diff --git a/mappostgis.c b/mappostgis.c index e960d50ba4..4077fdbc5a 100644 --- a/mappostgis.c +++ b/mappostgis.c @@ -2055,7 +2055,6 @@ char *msPostGISBuildSQLWhere(layerObj *layer, rectObj *rect, long *uid, rectObj char *strBox = 0; char *strSRID = 0; size_t strBoxLength = 0; - static const char *strRectTemplate = "\"%s\" && %s"; /* We see to set the SRID on the box, but to what SRID? */ strSRID = msPostGISBuildSQLSRID(layer); @@ -2066,27 +2065,42 @@ char *msPostGISBuildSQLWhere(layerObj *layer, rectObj *rect, long *uid, rectObj } strBox = msPostGISBuildSQLBox(layer, rect, strSRID); - msFree(strSRID); if ( strBox ) { strBoxLength = strlen(strBox); } else { msSetError(MS_MISCERR, "Unable to build box SQL.", "msPostGISBuildSQLWhere()"); free( strLimit ); free( strOffset ); + msFree(strSRID); return NULL; } - strRect = (char*)msSmallMalloc(strlen(strRectTemplate) + strBoxLength + strlen(layerinfo->geomcolumn) +1 ); - sprintf(strRect, strRectTemplate, layerinfo->geomcolumn, strBox); + if( strstr(strSRID, "find_srid(") == NULL ) + { + // If the SRID is known, we can safely use ST_Intersects() + // otherwise if find_srid() would return 0, ST_Intersects() would not + // work at all, which breaks the msautotest/query/query_postgis.map + // tests, releated to bdry_counpy2 layer that has no SRID + static const char *strRectTemplate = "ST_Intersects(\"%s\", %s)"; + strRect = (char*)msSmallMalloc(strlen(strRectTemplate) + strBoxLength + strlen(layerinfo->geomcolumn) +1 ); + sprintf(strRect, strRectTemplate, layerinfo->geomcolumn, strBox); + } + else + { + static const char *strRectTemplate = "\"%s\" && %s"; + strRect = (char*)msSmallMalloc(strlen(strRectTemplate) + strBoxLength + strlen(layerinfo->geomcolumn) +1 ); + sprintf(strRect, strRectTemplate, layerinfo->geomcolumn, strBox); + } strRectLength = strlen(strRect); free(strBox); + msFree(strSRID); /* Combine with other rectangle expressed in another SRS */ /* (generally equivalent to the above in current code paths) */ if( rectInOtherSRID != NULL && otherSRID > 0 ) { char* strRectOtherSRID; - static const char *strRectOtherSRIDTemplate = "NOT ST_Disjoint(ST_Transform(%s,%d),%s)"; + static const char *strRectOtherSRIDTemplate = "ST_Intersects(ST_Transform(%s,%d),%s)"; char szSRID[32]; char* strTmp = NULL; @@ -2121,7 +2135,7 @@ char *msPostGISBuildSQLWhere(layerObj *layer, rectObj *rect, long *uid, rectObj { char* strSRID; char* strRectOtherSRID; - static const char *strRectOtherSRIDTemplate = "NOT ST_Disjoint(%s,%s)"; + static const char *strRectOtherSRIDTemplate = "ST_Intersects(%s,%s)"; char* strTmp = NULL; strSRID = msPostGISBuildSQLSRID(layer); diff --git a/msautotest/create_postgis_test_data.sh b/msautotest/create_postgis_test_data.sh index bc81b7f038..ef01dfd32d 100755 --- a/msautotest/create_postgis_test_data.sh +++ b/msautotest/create_postgis_test_data.sh @@ -129,3 +129,11 @@ CREATE TABLE multipolygon3d (ID SERIAL); SELECT AddGeometryColumn('public', 'multipolygon3d', 'the_geom', 27700, 'MULTIPOLYGON', 3); INSERT INTO multipolygon3d (the_geom) VALUES (GeomFromEWKT('SRID=27700;MULTIPOLYGON(((0 0 1,10 0 2,10 10 3,0 10 4,0 0 1),(1 1 2,1 9 3,9 9 4,9 1 5,1 1 2)),((10 10 0,10 20 1,20 20 2,20 10 3,10 10 0)))')); " + +psql -U postgres -d msautotest -c " +CREATE TABLE test_wfs_paging (ID SERIAL); +SELECT AddGeometryColumn('public', 'test_wfs_paging', 'the_geom', 27700, 'LINESTRING', 2); +INSERT INTO test_wfs_paging (the_geom) VALUES (GeomFromEWKT('SRID=27700;LINESTRING (1 0,0 1)')); +INSERT INTO test_wfs_paging (the_geom) VALUES (GeomFromEWKT('SRID=27700;LINESTRING(0 0,10 10)')); +INSERT INTO test_wfs_paging (the_geom) VALUES (GeomFromEWKT('SRID=27700;LINESTRING(5 2,5 8)')); +" diff --git a/msautotest/wxs/expected/wfs_postgis_issue_6181.xml b/msautotest/wxs/expected/wfs_postgis_issue_6181.xml new file mode 100644 index 0000000000..91fc7e0235 --- /dev/null +++ b/msautotest/wxs/expected/wfs_postgis_issue_6181.xml @@ -0,0 +1,35 @@ +Content-Type: text/xml; subtype="gml/3.2.1"; charset=UTF-8 + + + + + + 0.00000 0.00000 + 10.00000 10.00000 + + + + + + + 0.00000 0.00000 + 10.00000 10.00000 + + + + + 0.00000 0.00000 10.00000 10.00000 + + + 2 + + + + diff --git a/msautotest/wxs/expected/wfs_postgis_issue_6181_startindex_1.xml b/msautotest/wxs/expected/wfs_postgis_issue_6181_startindex_1.xml new file mode 100644 index 0000000000..052700081d --- /dev/null +++ b/msautotest/wxs/expected/wfs_postgis_issue_6181_startindex_1.xml @@ -0,0 +1,35 @@ +Content-Type: text/xml; subtype="gml/3.2.1"; charset=UTF-8 + + + + + + 5.00000 2.00000 + 5.00000 8.00000 + + + + + + + 5.00000 2.00000 + 5.00000 8.00000 + + + + + 5.00000 2.00000 5.00000 8.00000 + + + 3 + + + + diff --git a/msautotest/wxs/wfs_postgis_paging.map b/msautotest/wxs/wfs_postgis_paging.map new file mode 100644 index 0000000000..785ec9b786 --- /dev/null +++ b/msautotest/wxs/wfs_postgis_paging.map @@ -0,0 +1,62 @@ +# +# Test WFS paging with PostGIS +# +# REQUIRES: SUPPORTS=WFS INPUT=POSTGIS +# +# RUN_PARMS: wfs_postgis_issue_6181.xml [MAPSERV] QUERY_STRING="map=[MAPFILE]&SERVICE=WFS&VERSION=2.0.0&REQUEST=GetFeature&TYPENAMES=test_6181&BBOX=0.75,0.75,9,9&COUNT=1" > [RESULT_DEVERSION] +# RUN_PARMS: wfs_postgis_issue_6181_startindex_1.xml [MAPSERV] QUERY_STRING="map=[MAPFILE]&SERVICE=WFS&VERSION=2.0.0&REQUEST=GetFeature&TYPENAMES=test_6181&BBOX=0.75,0.75,9,9&COUNT=1&STARTINDEX=1" > [RESULT_DEVERSION] + +MAP + +NAME WFS_POSTGIS_PAGING +STATUS ON +SIZE 400 300 +EXTENT 6 30 21 50 +UNITS METERS +IMAGECOLOR 255 255 255 + +# +# Start of web interface definition +# + +WEB + + IMAGEPATH "tmp/" + IMAGEURL "/ms_tmp/" + + METADATA + "wfs_title" "Test simple wfs" + "wfs_onlineresource" "http://localhost/path/to/wfs_simple?" + "wfs_srs" "EPSG:27700" + "wfs_enable_request" "*" + END +END + +PROJECTION + "init=epsg:27700" +END + +# +# Start of layer definitions +# + +LAYER + NAME test_6181 + INCLUDE "postgis.include" + DATA "the_geom from (select * from test_wfs_paging order by id) as foo using srid=27700 using unique id" + METADATA + "wfs_title" "test_6181" + "wfs_description" "test_6181" + "gml_include_items" "all" + "wfs_featureid" "id" + END + TYPE LINE + STATUS ON + PROJECTION + "init=epsg:27700" + END + +END # Layer + + +END # Map File From 98ad83a21896b45e36583340914f2b24f8934ac4 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 8 Jun 2021 19:23:06 +0200 Subject: [PATCH 061/160] msQueryByFeatures(): avoid potential segfault when reslut set is empty --- mapquery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapquery.c b/mapquery.c index 3272ed9f9e..98292a251a 100644 --- a/mapquery.c +++ b/mapquery.c @@ -1617,7 +1617,7 @@ int msQueryByFeatures(mapObj *map) } /* next selection shape */ - if(lp->resultcache->numresults == 0) msLayerClose(lp); /* no need to keep the layer open */ + if(lp->resultcache == NULL || lp->resultcache->numresults == 0) msLayerClose(lp); /* no need to keep the layer open */ } /* next layer */ /* was anything found? */ From 1a98ac7e2cd89efb3d7df2ec39505469cc5e5be4 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 22 Jun 2021 19:21:52 +0200 Subject: [PATCH 062/160] WMS: make attribute color binding work with FILTER vendor parameter (fixes #6200) --- mapdraw.c | 5 +- .../wxs/expected/wms_filter_color_bind.png | Bin 0 -> 1922 bytes msautotest/wxs/wms_filter_color_bind.map | 106 ++++++++++++++++++ 3 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 msautotest/wxs/expected/wms_filter_color_bind.png create mode 100644 msautotest/wxs/wms_filter_color_bind.map diff --git a/mapdraw.c b/mapdraw.c index 5fc1bd4bf3..9020f90e79 100644 --- a/mapdraw.c +++ b/mapdraw.c @@ -1307,7 +1307,7 @@ int msDrawQueryLayer(mapObj *map, layerObj *layer, imageObj *image) { int i, status; char annotate=MS_TRUE, cache=MS_FALSE; - int drawmode = MS_DRAWMODE_FEATURES|MS_DRAWMODE_QUERY; + int drawmode = MS_DRAWMODE_FEATURES; shapeObj shape; int maxnumstyles=1; @@ -1356,6 +1356,9 @@ int msDrawQueryLayer(mapObj *map, layerObj *layer, imageObj *image) /* if MS_HILITE, alter the one style (always at least 1 style), and set a MINDISTANCE for the labelObj to avoid duplicates */ if(map->querymap.style == MS_HILITE) { + + drawmode |= MS_DRAWMODE_QUERY; + if (layer->numclasses > 0) { colorbuffer = (colorObj*)msSmallMalloc(layer->numclasses*sizeof(colorObj)); mindistancebuffer = (int*)msSmallMalloc(layer->numclasses*sizeof(int)); diff --git a/msautotest/wxs/expected/wms_filter_color_bind.png b/msautotest/wxs/expected/wms_filter_color_bind.png new file mode 100644 index 0000000000000000000000000000000000000000..81ff9c5f3f310eb436d6e849f6c11f1171cedc03 GIT binary patch literal 1922 zcmeAS@N?(olHy`uVBq!ia0y~yVEh8Y9Be?5)7S2IF)*-udb&7mmah%Ar|>u=kiJ$uR$pRBIQ36;}czfCb&y z?Pi3(eEPWW#H}```b7`-L>Enz?>=)fHBSD0;0uO=ytykcoL>87>co2|zCPyK`%&)K zFO#zPznf3IIkDO@`qQ>OPV5cE8JnE+!*A3-+4#2d&At<}Jh}efi+Sa){4u+GzM4i3 z(4OZWdoRRy#7y$r`||DH6WtS6E4^0Q|K#oGYM|qm#oTFE7I)uw_V~NLJ2Q`Md)0nv zW%Y}2_qchBF0g;PAN+#Bz;DVc*ZSpK^twKtJu!Xa^+!LM_b)H0tK9R?^2;aVr*kK| zEB8M={`A{V^PLN>KmSx`@Tv5k-+k$u6OO%G``&tQi+lI)e>Jf`({}PJ@w?B<)A;dG zKKk{!=TolB0ev{((reALosafubA9~bvihOK|4NIc+bo_htf(niruJ<6vRBuI7_@|4 zca&{f`}UYjcIM8-uXZvq2upaqycBVl&wu%a{`0fTi?ZF1zYRSv$snPAswTlIHSYX! z?J$wr+FJ8)+kA!vjv~Jr)42X#n562uHq3fg;p1c6tL9lV2=Fa4`JN)28ke?V>CzYP z-|L6J><_W6XHe)C{ndX~fE zYa~C@boGsGRi%3`l!x1v1FibmTX0$POYQ0D{nIM-zW9comuATL5becX7rUe2eMsuQ z`j?j;?VLC7qS5-RS<17`^Y?9fRmsfoxMZ10b$RsddGccVdTr0n$?AR-{`_3LJvzF3 zyOq_y*qP6-{%l+IoSVUC(eB;Vj#IvVOaJ|=Yx=HTUH^XEjCiv1O!nNlbDxLWhBG8^ z1zy`+U0ogSR#+dh`YKy-r+oLDo{z6r&68)iu;kmfZjUpgWWxM)8yA5k_$nqJzf1=);T3K0RSj81XBP2 literal 0 HcmV?d00001 diff --git a/msautotest/wxs/wms_filter_color_bind.map b/msautotest/wxs/wms_filter_color_bind.map new file mode 100644 index 0000000000..d06cd493d2 --- /dev/null +++ b/msautotest/wxs/wms_filter_color_bind.map @@ -0,0 +1,106 @@ +# +# Test WMS vendor-specific FILTER with attribute color binding +# +# REQUIRES: INPUT=GDAL OUTPUT=PNG SUPPORTS=WMS +# +# +# RUN_PARMS: wms_filter_color_bind.png [MAPSERV] QUERY_STRING="map=[MAPFILE]&SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&FORMAT=image/png&TRANSPARENT=true&LAYERS=test001&WIDTH=500&HEIGHT=500&CRS=EPSG:4326&BBOX=0,-1.5,1,0&STYLES=&FILTER=(%3CFilter%20xmlns=%22http://www.opengis.net/ogc%22%3E%3CPropertyIsEqualTo%3E%3CPropertyName%3Esymbol%3C/PropertyName%3E%3CLiteral%3Ecircle%3C/Literal%3E%3C/PropertyIsEqualTo%3E%3C/Filter%3E)" > [RESULT_DEMIME] + +MAP + NAME WMS_FILTER_COLOR_BIND + STATUS ON + SIZE 500 500 + EXTENT -1.5 0 -0.5 1 + UNITS DD + IMAGECOLOR 255 255 255 + SHAPEPATH ../misc/data + SYMBOLSET etc/symbols.sym + FONTSET ../misc/fonts.lst + + WEB + + IMAGEPATH "/tmp/ms_tmp/" + IMAGEURL "/ms_tmp/" + + METADATA + "wms_title" "Test simple wms" + "wms_onlineresource" "http://localhost/path/to/wms_simple?" + "wms_srs" "EPSG:4326" + "ows_schemas_location" "http://ogc.dmsolutions.ca" + "ows_enable_request" "*" + END + END + + PROJECTION + "init=epsg:4326" + END + + IMAGETYPE PNG + + SYMBOL + NAME 'triangle' + TYPE VECTOR + POINTS + 0 4 + 2 0 + 4 4 + 0 4 + END + FILLED TRUE + END + + SYMBOL + NAME 'square' + TYPE VECTOR + POINTS 0 0 1 0 1 1 0 1 0 0 END + FILLED TRUE + END + + SYMBOL + NAME 'circle' + TYPE ELLIPSE + POINTS 1 1 END + FILLED TRUE + END + + LAYER + METADATA + "wms_title" "test001" + "wms_description" "test001" + END + PROJECTION + "init=epsg:4326" + END + + NAME 'test001' + TYPE POINT + DATA 'attrbind' + STATUS OFF + LABELITEM 'text' + CLASS + STYLE # a shadow + COLOR 151 151 151 + SYMBOL [symbol] + OFFSET 2 2 + SIZE [size] + END + STYLE + COLOR [color] + SYMBOL [symbol] + SIZE [size] + END + LABEL + STYLE + GEOMTRANSFORM "labelpoly" + COLOR [color] + END + TYPE TRUETYPE + FONT 'default' + COLOR [lcolor] + SIZE [lsize] + POSITION UR + END + END + END +END + From 0d5865d5b489b3dbfc16f850ef6bd2c6bece2e0a Mon Sep 17 00:00:00 2001 From: jmckenna Date: Thu, 24 Jun 2021 12:52:56 -0300 Subject: [PATCH 063/160] attempt to get Travis working --- .travis.yml | 24 ++++++------------------ ci/travis/script.sh | 6 +++--- 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7ac04f09c4..75ed6d733e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,31 +2,19 @@ branches: except: - /^(cherry-pick-)?backport-\d+-to-/ -dist: trusty +dist: bionic language: php matrix: include: - - php: 5.6 - env: - - BUILD_NAME=PHP_5.6 - - - php: 7.0 - env: - - BUILD_NAME=PHP_7.0 - - - php: 7.1 - env: - - BUILD_NAME=PHP_7.1 - - - php: 7.2 + - php: 7.3 env: - - BUILD_NAME=PHP_7.2_WITH_ASAN + - BUILD_NAME=PHP_7.3_WITH_ASAN - - php: 7.3 + - php: 7.4 env: - - BUILD_NAME=PHP_7.3_WITH_PROJ6 + - BUILD_NAME=PHP_7.4_WITH_PROJ6 cache: apt: true @@ -83,6 +71,6 @@ after_success: notifications: irc: channels: - - "irc.freenode.org#mapserver" + - "irc.libera.chat#mapserver" use_notice: true diff --git a/ci/travis/script.sh b/ci/travis/script.sh index e17e4175ef..10a3401ac1 100755 --- a/ci/travis/script.sh +++ b/ci/travis/script.sh @@ -1,7 +1,7 @@ #!/bin/sh set -eu -if [ "$BUILD_NAME" = "PHP_7.2_WITH_ASAN" ]; then +if [ "$BUILD_NAME" = "PHP_7.3_WITH_ASAN" ]; then export CC="ccache clang" export CXX="ccache clang++" else @@ -16,7 +16,7 @@ mv proj-6.1.1 proj (cd proj; CFLAGS='-O2 -DPROJ_RENAME_SYMBOLS' CXXFLAGS='-O2 -DPROJ_RENAME_SYMBOLS' ./configure --disable-static --prefix=/usr/local && CCACHE_CPP2=yes make -j2 && sudo make -j3 install) sudo rm -f /usr/include/proj_api.h -if [ "$BUILD_NAME" = "PHP_7.2_WITH_ASAN" ]; then +if [ "$BUILD_NAME" = "PHP_7.3_WITH_ASAN" ]; then # Force use of PROJ 4 API sudo rm /usr/local/include/proj.h # -DNDEBUG to avoid issues with cairo cleanup @@ -24,7 +24,7 @@ if [ "$BUILD_NAME" = "PHP_7.2_WITH_ASAN" ]; then export AUTOTEST_OPTS="--strict --run_under_asan" # Only run tests that only involve mapserv/shp2img binaries. mspython, etc would require LD_PREOLOAD'ing the asan shared object make -j4 asan_compatible_tests -elif [ "$BUILD_NAME" = "PHP_7.3_WITH_PROJ6" ]; then +elif [ "$BUILD_NAME" = "PHP_7.4_WITH_PROJ6" ]; then # Avoid any use of PROJ 4 API sudo rm -f /usr/include/proj_api.h make cmakebuild MFLAGS="-j2" CMAKE_C_FLAGS="-O2 -DPROJ_RENAME_SYMBOLS" CMAKE_CXX_FLAGS="-O2 -DPROJ_RENAME_SYMBOLS" EXTRA_CMAKEFLAGS="-DPROJ_INCLUDE_DIR=/usr/local/include -DPROJ_LIBRARY=/usr/local/lib/libproj.so.15" From 5d5d996ecd287cf9f5f6d7464b0b51aff32c161e Mon Sep 17 00:00:00 2001 From: jmckenna Date: Thu, 24 Jun 2021 13:54:10 -0300 Subject: [PATCH 064/160] attempt to get Travis working --- ci/travis/after_success.sh | 6 ++--- ci/travis/before_install.sh | 49 +++++++++++++++++++++++-------------- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/ci/travis/after_success.sh b/ci/travis/after_success.sh index 0958b614a3..e04e6c3e80 100755 --- a/ci/travis/after_success.sh +++ b/ci/travis/after_success.sh @@ -1,11 +1,11 @@ #!/bin/sh set -eu -if [ "$BUILD_NAME" != "PHP_7.2_WITH_ASAN" ]; then - # Only run coverage when it is safe to do so (not on pull requests), and only on master branch +if [ "$BUILD_NAME" != "PHP_7.3_WITH_ASAN" ]; then + # Only run coverage when it is safe to do so (not on pull requests), and only on main branch echo "$TRAVIS_SECURE_ENV_VARS" echo "$TRAVIS_BRANCH" - sh -c 'if test "$TRAVIS_SECURE_ENV_VARS" = "true" -a "$TRAVIS_BRANCH" = "master"; then echo "run coverage"; ./run_code_coverage_upload.sh; fi' + sh -c 'if test "$TRAVIS_SECURE_ENV_VARS" = "true" -a "$TRAVIS_BRANCH" = "main"; then echo "run coverage"; ./run_code_coverage_upload.sh; fi' ln -s ../../../mapparser.y build/CMakeFiles/mapserver.dir/ ln -s ../../../maplexer.l build/CMakeFiles/mapserver.dir/ coveralls --exclude renderers --exclude mapscript --exclude apache --exclude build/mapscript/mapscriptJAVA_wrap.c --exclude build/mapscript/mapscriptPYTHON_wrap.c --exclude shp2img.c --exclude legend.c --exclude scalebar.c --exclude msencrypt.c --exclude sortshp.c --exclude shptreevis.c --exclude shptree.c --exclude testexpr.c --exclude sym2img.c --exclude testcopy.c --exclude shptreetst.c --exclude tile4ms.c --exclude proj --exclude swig-3.0.12 --extension .c --extension .cpp diff --git a/ci/travis/before_install.sh b/ci/travis/before_install.sh index 5b9129a5b0..595aa9b0a2 100755 --- a/ci/travis/before_install.sh +++ b/ci/travis/before_install.sh @@ -1,31 +1,42 @@ #!/bin/sh set -eu -sudo mv /etc/apt/sources.list.d/pgdg* /tmp -dpkg -l | grep postgresql -dpkg -l | grep postgis -sudo apt-get remove postgresql* -sudo add-apt-repository -y ppa:ubuntugis/ppa -sudo add-apt-repository -y ppa:ubuntugis/ubuntugis-testing +# Remove pre-installed things in Travis image +if ls /etc/apt/sources.list.d/pgdg* 2>/dev/null >/dev/null; then sudo mv /etc/apt/sources.list.d/pgdg* /tmp; fi +dpkg -l | grep postgresql || /bin/true +dpkg -l | grep postgis || /bin/true +sudo apt-get remove --purge postgresql* libpq-dev libpq5 || /bin/true + +sudo add-apt-repository -y ppa:ubuntugis/ubuntugis-unstable sudo apt-get update -sudo apt-get install --allow-unauthenticated protobuf-c-compiler libprotobuf-c0-dev bison flex python-lxml libfribidi-dev cmake librsvg2-dev colordiff libpq-dev libpng12-dev libjpeg-dev libgif-dev libgeos-dev libgd2-xpm-dev libfreetype6-dev libfcgi-dev libcurl4-gnutls-dev libcairo2-dev libgdal1-dev libproj-dev libxml2-dev python-dev libexempi-dev lcov lftp postgis libharfbuzz-dev gdal-bin ccache curl pyflakes -sudo apt-get install --allow-unauthenticated libmono-system-drawing4.0-cil mono-mcs -sudo apt-get install --allow-unauthenticated php5-dev || sudo apt-get install --allow-unauthenticated php7-dev -sudo apt-get install --allow-unauthenticated libperl-dev -sudo pip install cpp-coveralls +sudo apt-get install -y --allow-unauthenticated protobuf-c-compiler libprotobuf-c0-dev bison flex libfribidi-dev cmake librsvg2-dev colordiff libpq-dev libpng-dev libjpeg-dev libgif-dev libgeos-dev libfreetype6-dev libfcgi-dev libcurl4-gnutls-dev libcairo2-dev libgdal-dev libproj-dev libxml2-dev libexempi-dev lcov lftp postgis libharfbuzz-dev gdal-bin ccache curl postgresql-server-dev-10 postgresql-10-postgis-3 postgresql-10-postgis-3-scripts swig g++ +# following are already installed on Travis CI +#sudo apt-get install --allow-unauthenticated php-dev python-dev python3-dev +sudo apt-get install -y --allow-unauthenticated libmono-system-drawing4.0-cil mono-mcs +sudo apt-get install -y --allow-unauthenticated libperl-dev +sudo apt-get install -y --allow-unauthenticated openjdk-8-jdk + +pip install cpp-coveralls pyflakes lxml sudo pip install -U -r msautotest/requirements.txt -# install swig 3.0.12 (defaults to 2.0.11 on trusty) -wget http://prdownloads.sourceforge.net/swig/swig-3.0.12.tar.gz + export CC="ccache gcc" export CXX="ccache g++" -tar xf swig-3.0.12.tar.gz -cd swig-3.0.12 && ./configure --prefix=/usr && make -j2 && sudo make install -swig -version -cd .. + +sudo sed -i 's/md5/trust/' /etc/postgresql/10/main/pg_hba.conf +sudo sed -i 's/peer/trust/' /etc/postgresql/10/main/pg_hba.conf +sudo service postgresql restart 10 + cd msautotest -pyflakes . +python -m pyflakes . ./create_postgis_test_data.sh -python -m SimpleHTTPServer &> /dev/null & + +if [ $PYTHON_VERSION = "2.7" ]; then + python -m SimpleHTTPServer &> /dev/null & +else + # py3 + python -m http.server &> /dev/null & +fi + cd .. touch maplexer.l touch mapparser.y From b813da3c047feed8d087a355a5b4f87e1aaa2083 Mon Sep 17 00:00:00 2001 From: jmckenna Date: Thu, 24 Jun 2021 14:03:15 -0300 Subject: [PATCH 065/160] attempt to get Travis working --- ci/travis/before_install.sh | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/ci/travis/before_install.sh b/ci/travis/before_install.sh index 595aa9b0a2..9bc708f505 100755 --- a/ci/travis/before_install.sh +++ b/ci/travis/before_install.sh @@ -30,12 +30,8 @@ cd msautotest python -m pyflakes . ./create_postgis_test_data.sh -if [ $PYTHON_VERSION = "2.7" ]; then - python -m SimpleHTTPServer &> /dev/null & -else - # py3 - python -m http.server &> /dev/null & -fi +# py3 +python -m http.server &> /dev/null & cd .. touch maplexer.l From b9f9ff68011969c25971b08782fc68ac457b33a8 Mon Sep 17 00:00:00 2001 From: jmckenna Date: Thu, 24 Jun 2021 14:35:27 -0300 Subject: [PATCH 066/160] attempt to get Travis working --- ci/travis/script.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ci/travis/script.sh b/ci/travis/script.sh index 10a3401ac1..3c756cb23a 100755 --- a/ci/travis/script.sh +++ b/ci/travis/script.sh @@ -1,13 +1,13 @@ #!/bin/sh set -eu -if [ "$BUILD_NAME" = "PHP_7.3_WITH_ASAN" ]; then - export CC="ccache clang" - export CXX="ccache clang++" -else +#if [ "$BUILD_NAME" = "PHP_7.3_WITH_ASAN" ]; then +# export CC="ccache clang" +# export CXX="ccache clang++" +#else export CC="ccache gcc" export CXX="ccache g++" -fi +#fi curl http://download.osgeo.org/proj/proj-6.1.1.tar.gz > proj-6.1.1.tar.gz tar xzf proj-6.1.1.tar.gz From 44d5d9c93b2e6e64f6e8b766a30e7433735ff453 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 29 Jun 2021 11:26:09 +0200 Subject: [PATCH 067/160] PostGIS: fix ST_Intersects() with bounding box that is a point (follow-up of fixes #6181, fixes #6230) (fixes https://github.com/MapServer/MapServer/pull/6347#issuecomment-870002856) --- mappostgis.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/mappostgis.c b/mappostgis.c index 4077fdbc5a..2f29e7cbdb 100644 --- a/mappostgis.c +++ b/mappostgis.c @@ -1666,37 +1666,44 @@ char *msPostGISBuildSQLBox(layerObj *layer, rectObj *rect, char *strSRID) char *strBox = NULL; size_t sz; + const int bIsPoint = rect->minx == rect->maxx && rect->miny == rect->maxy; if (layer->debug) { msDebug("msPostGISBuildSQLBox called.\n"); } if ( strSRID ) { - static char *strBoxTemplate = "ST_GeomFromText('POLYGON((%.15g %.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g))',%s)"; + static const char *strBoxTemplate = "ST_GeomFromText('POLYGON((%.15g %.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g))',%s)"; + static const char *strBoxTemplatePoint = "ST_GeomFromText('POINT(%.15g %.15g)',%s)"; /* 10 doubles + 1 integer + template characters */ sz = 10 * 22 + strlen(strSRID) + strlen(strBoxTemplate); strBox = (char*)msSmallMalloc(sz+1); /* add space for terminating NULL */ - if ( sz <= snprintf(strBox, sz, strBoxTemplate, + if ( (bIsPoint && sz <= (size_t)(snprintf(strBox, sz, strBoxTemplatePoint, + rect->minx, rect->miny, strSRID))) || + (!bIsPoint && sz <= (size_t)(snprintf(strBox, sz, strBoxTemplate, rect->minx, rect->miny, rect->minx, rect->maxy, rect->maxx, rect->maxy, rect->maxx, rect->miny, rect->minx, rect->miny, - strSRID)) { + strSRID))) ) { msSetError(MS_MISCERR,"Bounding box digits truncated.","msPostGISBuildSQLBox"); return NULL; } } else { - static char *strBoxTemplate = "ST_GeomFromText('POLYGON((%.15g %.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g))')"; + static const char *strBoxTemplate = "ST_GeomFromText('POLYGON((%.15g %.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g))')"; + static const char *strBoxTemplatePoint = "ST_GeomFromText('POINT(%.15g %.15g)')"; /* 10 doubles + template characters */ sz = 10 * 22 + strlen(strBoxTemplate); strBox = (char*)msSmallMalloc(sz+1); /* add space for terminating NULL */ - if ( sz <= snprintf(strBox, sz, strBoxTemplate, + if ( (bIsPoint && sz <= (size_t)(snprintf(strBox, sz, strBoxTemplatePoint, + rect->minx, rect->miny))) || + (!bIsPoint && sz <= (size_t)(snprintf(strBox, sz, strBoxTemplate, rect->minx, rect->miny, rect->minx, rect->maxy, rect->maxx, rect->maxy, rect->maxx, rect->miny, - rect->minx, rect->miny) ) { + rect->minx, rect->miny))) ) { msSetError(MS_MISCERR,"Bounding box digits truncated.","msPostGISBuildSQLBox"); return NULL; } From ee6297a068a11dacdba362c402f3c8403f677d9c Mon Sep 17 00:00:00 2001 From: jmckenna Date: Mon, 5 Jul 2021 11:16:59 -0300 Subject: [PATCH 068/160] minor updates --- CONTRIBUTING.md | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c403a9f718..9704b24ac5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,28 +5,33 @@ tests, features, and patches. Note that all contributions are managed by the Ma ## Bugs and Help -GitHub issues should only be created to log bugs. For general help and support the MapServer [mailing lists](http://mapserver.org/community/lists.html) +GitHub issues should only be created to log bugs. For general help and support the MapServer [mailing lists](https://mapserver.org/community/lists.html) should be used. If you are unsure if you have discovered a bug, or may need help with configuring MapServer please -post to the [mapserver-users list](http://lists.osgeo.org/mailman/listinfo/mapserver-users). There is also -a [MapServer FAQ](http://www.mapserver.org/faq.html) which may have a solution to your problem. +post to the [mapserver-users list](https://lists.osgeo.org/mailman/listinfo/mapserver-users). There is also +a [MapServer FAQ](https://mapserver.org/faq.html) which may have a solution to your problem. -If you have discovered a bug, please refer to the [Bug Submission page](http://mapserver.org/development/bugs.html) for +If you have discovered a bug, please refer to the [Bug Submission page](https://mapserver.org/development/bugs.html) for guidelines on creating an issue on GitHub. Please also search the existing issues to see if the bug has already been reported, and add any further details to the existing issue. -For professional support please see the [MapServer Service Providers page](http://mapserver.org/community/service_providers.html). +For professional support please see the [MapServer Service Providers page](https://mapserver.org/community/service_providers.html). ## Development -A separate [mapserver-dev mailing list](http://lists.osgeo.org/mailman/listinfo/mapserver-dev) is available for developers -working on the MapServer code. +A separate [mapserver-dev mailing list](https://lists.osgeo.org/mailman/listinfo/mapserver-dev) is available for developers +working on the MapServer code. Send a short message there to introduce yourself +to the community, and mention what you are interested in working on. -Details on using GitHub can be found on the [MapServer GitHub page](http://mapserver.org/development/git.html) +Details on using GitHub can be found on the [MapServer GitHub page](https://mapserver.org/development/git.html). + +Request for Comments (RFCs), where upcoming major changes to the source +code are proposed, and a description of the various software tests & release +plans, can be found on the [MapServer Development page](https://mapserver.org/development/). Additional developer notes can be found in the [MapServer wiki](https://github.com/mapserver/mapserver/wiki#developer-notes), including coding style and guidelines, memory management, and working with Git. ## Documentation -The MapServer documentation is stored in a [separate repository](https://github.com/mapserver/docs). Please submit any documentation -issues or changes there. See the [Documentation Development Guide](http://mapserver.org/development/documentation.html) for further details. +The MapServer documentation is stored in a [separate repository](https://github.com/MapServer/MapServer-documentation). Please submit any documentation +issues or changes there. See the [Documentation Development Guide](https://mapserver.org/development/documentation.html) for further details. From 0901b574dd9da8fc2577a55b416d56b8189dca86 Mon Sep 17 00:00:00 2001 From: Steve Lime Date: Fri, 2 Jul 2021 11:29:17 -0500 Subject: [PATCH 069/160] Make sure requested class with mode=legendicon is not negative. (#6357) * Make sure the requested class index is not negative. --- mapservutil.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapservutil.c b/mapservutil.c index 1d69104d10..bee4250297 100644 --- a/mapservutil.c +++ b/mapservutil.c @@ -1651,7 +1651,7 @@ int msCGIDispatchLegendIconRequest(mapservObj *mapserv) if(numtokens == 2) { /* check the class index */ classindex = atoi(tokens[1]); - if(classindex >= GET_LAYER(mapserv->map, layerindex)->numclasses) { + if(classindex < 0 || classindex >= GET_LAYER(mapserv->map, layerindex)->numclasses) { msSetError(MS_WEBERR, "Icon class=%d not found in layer=%s.", "mapserv()", classindex, GET_LAYER(mapserv->map, layerindex)->name); status = MS_FAILURE; goto li_cleanup; From 955d14e08d970bc8579b9ab44cfef6f7ce8f6357 Mon Sep 17 00:00:00 2001 From: Wouter Date: Tue, 6 Jul 2021 08:24:22 +0200 Subject: [PATCH 070/160] spatialindex for sqlite sources in filter --- mapogr.cpp | 122 +++++++++++++++++++++++++++-------------------------- 1 file changed, 62 insertions(+), 60 deletions(-) diff --git a/mapogr.cpp b/mapogr.cpp index 09b1d47730..ec64d96d94 100644 --- a/mapogr.cpp +++ b/mapogr.cpp @@ -2272,66 +2272,6 @@ static int msOGRFileWhichShapes(layerObj *layer, rectObj rect, msOGRFileInfo *ps bool bSpatialiteOrGPKGAddOrderByFID = false; - if( psInfo->dialect && psInfo->pszMainTableName != NULL && - ( (EQUAL(psInfo->dialect, "Spatialite") && psInfo->bHasSpatialIndex) - || EQUAL(psInfo->dialect, "GPKG") ) && - bIsValidRect ) - { - select = msStringConcatenate(select, " JOIN "); - - char szSpatialIndexName[256]; - snprintf( szSpatialIndexName, sizeof(szSpatialIndexName), - "%s_%s_%s", - EQUAL(psInfo->dialect, "Spatialite") ? "idx" : "rtree", - psInfo->pszSpatialFilterTableName, - psInfo->pszSpatialFilterGeometryColumn ); - char* pszEscapedSpatialIndexName = msLayerEscapePropertyName( - layer, szSpatialIndexName); - select = msStringConcatenate(select, "\""); - select = msStringConcatenate(select, pszEscapedSpatialIndexName); - msFree(pszEscapedSpatialIndexName); - select = msStringConcatenate(select, "\" ms_spat_idx ON \""); - char* pszEscapedMainTableName = msLayerEscapePropertyName( - layer, psInfo->pszMainTableName); - select = msStringConcatenate(select, pszEscapedMainTableName); - msFree(pszEscapedMainTableName); - select = msStringConcatenate(select, "\"."); - if( psInfo->pszRowId ) - { - char* pszEscapedRowId = msLayerEscapePropertyName( - layer, psInfo->pszRowId); - select = msStringConcatenate(select, "\""); - select = msStringConcatenate(select, pszEscapedRowId); - select = msStringConcatenate(select, "\""); - msFree(pszEscapedRowId); - } - else - select = msStringConcatenate(select, "ROWID"); - if( EQUAL(psInfo->dialect, "Spatialite") ) - select = msStringConcatenate(select, " = ms_spat_idx.pkid AND "); - else - select = msStringConcatenate(select, " = ms_spat_idx.id AND "); - - char szCond[256]; - if( EQUAL(psInfo->dialect, "Spatialite") ) - { - snprintf(szCond, sizeof(szCond), - "ms_spat_idx.xmin <= %.15g AND ms_spat_idx.xmax >= %.15g AND " - "ms_spat_idx.ymin <= %.15g AND ms_spat_idx.ymax >= %.15g", - rect.maxx, rect.minx, rect.maxy, rect.miny); - } - else - { - snprintf(szCond, sizeof(szCond), - "ms_spat_idx.minx <= %.15g AND ms_spat_idx.maxx >= %.15g AND " - "ms_spat_idx.miny <= %.15g AND ms_spat_idx.maxy >= %.15g", - rect.maxx, rect.minx, rect.maxy, rect.miny); - } - select = msStringConcatenate(select, szCond); - - bSpatialiteOrGPKGAddOrderByFID = true; - } - const char *sql = layer->filter.native_string; if (psInfo->dialect && sql && *sql != '\0' && (EQUAL(psInfo->dialect, "Spatialite") || @@ -2373,6 +2313,68 @@ static int msOGRFileWhichShapes(layerObj *layer, rectObj rect, msOGRFileInfo *ps EQUAL(psInfo->dialect, "GPKG")) && psInfo->pszMainTableName != NULL ) { + + if( psInfo->dialect && psInfo->pszMainTableName != NULL && + ( (EQUAL(psInfo->dialect, "Spatialite") && psInfo->bHasSpatialIndex) + || EQUAL(psInfo->dialect, "GPKG") ) && + bIsValidRect ) + { + char* pszEscapedMainTableName = msLayerEscapePropertyName( + layer, psInfo->pszMainTableName); + filter = msStringConcatenate(filter, "\""); + filter = msStringConcatenate(filter, pszEscapedMainTableName); + filter = msStringConcatenate(filter, "\"."); + msFree(pszEscapedMainTableName); + filter = msStringConcatenate(filter, "ROWID"); + + filter = msStringConcatenate(filter, " IN "); + filter = msStringConcatenate(filter, "("); + filter = msStringConcatenate(filter, "SELECT "); + + if( EQUAL(psInfo->dialect, "Spatialite") ) + filter = msStringConcatenate(filter, "ms_spat_idx.pkid"); + else + filter = msStringConcatenate(filter, "ms_spat_idx.id"); + + filter = msStringConcatenate(filter, " FROM "); + + char szSpatialIndexName[256]; + snprintf( szSpatialIndexName, sizeof(szSpatialIndexName), + "%s_%s_%s", + EQUAL(psInfo->dialect, "Spatialite") ? "idx" : "rtree", + psInfo->pszSpatialFilterTableName, + psInfo->pszSpatialFilterGeometryColumn ); + char* pszEscapedSpatialIndexName = msLayerEscapePropertyName( + layer, szSpatialIndexName); + + filter = msStringConcatenate(filter, "\""); + filter = msStringConcatenate(filter, pszEscapedSpatialIndexName); + msFree(pszEscapedSpatialIndexName); + + filter = msStringConcatenate(filter, "\" ms_spat_idx WHERE "); + + char szCond[256]; + if( EQUAL(psInfo->dialect, "Spatialite") ) + { + snprintf(szCond, sizeof(szCond), + "ms_spat_idx.xmin <= %.15g AND ms_spat_idx.xmax >= %.15g AND " + "ms_spat_idx.ymin <= %.15g AND ms_spat_idx.ymax >= %.15g", + rect.maxx, rect.minx, rect.maxy, rect.miny); + } + else + { + snprintf(szCond, sizeof(szCond), + "ms_spat_idx.minx <= %.15g AND ms_spat_idx.maxx >= %.15g AND " + "ms_spat_idx.miny <= %.15g AND ms_spat_idx.maxy >= %.15g", + rect.maxx, rect.minx, rect.maxy, rect.miny); + } + filter = msStringConcatenate(filter, szCond); + + filter = msStringConcatenate(filter, ")"); + + bSpatialiteOrGPKGAddOrderByFID = true; + } + const bool isGPKG = EQUAL(psInfo->dialect, "GPKG"); if (filter) filter = msStringConcatenate(filter, " AND"); const char *col = OGR_L_GetGeometryColumn(psInfo->hLayer); // which geom field?? From 468ca1758a55b06a94619fb1c9c9041f7b1f107f Mon Sep 17 00:00:00 2001 From: Wouter Date: Tue, 6 Jul 2021 22:56:27 +0200 Subject: [PATCH 071/160] missing AND --- mapogr.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/mapogr.cpp b/mapogr.cpp index ec64d96d94..9aac7da005 100644 --- a/mapogr.cpp +++ b/mapogr.cpp @@ -2319,6 +2319,7 @@ static int msOGRFileWhichShapes(layerObj *layer, rectObj rect, msOGRFileInfo *ps || EQUAL(psInfo->dialect, "GPKG") ) && bIsValidRect ) { + if (filter) filter = msStringConcatenate(filter, " AND "); char* pszEscapedMainTableName = msLayerEscapePropertyName( layer, psInfo->pszMainTableName); filter = msStringConcatenate(filter, "\""); From 2c1fbaccef6c644815e196ee533b6a8ecff310ec Mon Sep 17 00:00:00 2001 From: Wouter Date: Wed, 7 Jul 2021 09:25:34 +0200 Subject: [PATCH 072/160] missing rowid-fid --- mapogr.cpp | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/mapogr.cpp b/mapogr.cpp index 9aac7da005..31feafd6ec 100644 --- a/mapogr.cpp +++ b/mapogr.cpp @@ -2313,7 +2313,6 @@ static int msOGRFileWhichShapes(layerObj *layer, rectObj rect, msOGRFileInfo *ps EQUAL(psInfo->dialect, "GPKG")) && psInfo->pszMainTableName != NULL ) { - if( psInfo->dialect && psInfo->pszMainTableName != NULL && ( (EQUAL(psInfo->dialect, "Spatialite") && psInfo->bHasSpatialIndex) || EQUAL(psInfo->dialect, "GPKG") ) && @@ -2324,27 +2323,37 @@ static int msOGRFileWhichShapes(layerObj *layer, rectObj rect, msOGRFileInfo *ps layer, psInfo->pszMainTableName); filter = msStringConcatenate(filter, "\""); filter = msStringConcatenate(filter, pszEscapedMainTableName); + msFree(pszEscapedMainTableName); filter = msStringConcatenate(filter, "\"."); - msFree(pszEscapedMainTableName); - filter = msStringConcatenate(filter, "ROWID"); + if( psInfo->pszRowId ) + { + char* pszEscapedRowId = msLayerEscapePropertyName( + layer, psInfo->pszRowId); + filter = msStringConcatenate(filter, "\""); + filter = msStringConcatenate(filter, pszEscapedRowId); + filter = msStringConcatenate(filter, "\""); + msFree(pszEscapedRowId); + } + else + filter = msStringConcatenate(filter, "ROWID"); filter = msStringConcatenate(filter, " IN "); - filter = msStringConcatenate(filter, "("); + filter = msStringConcatenate(filter, "("); filter = msStringConcatenate(filter, "SELECT "); if( EQUAL(psInfo->dialect, "Spatialite") ) filter = msStringConcatenate(filter, "ms_spat_idx.pkid"); else - filter = msStringConcatenate(filter, "ms_spat_idx.id"); + filter = msStringConcatenate(filter, "ms_spat_idx.id"); - filter = msStringConcatenate(filter, " FROM "); + filter = msStringConcatenate(filter, " FROM "); char szSpatialIndexName[256]; snprintf( szSpatialIndexName, sizeof(szSpatialIndexName), "%s_%s_%s", EQUAL(psInfo->dialect, "Spatialite") ? "idx" : "rtree", psInfo->pszSpatialFilterTableName, - psInfo->pszSpatialFilterGeometryColumn ); + psInfo->pszSpatialFilterGeometryColumn ); char* pszEscapedSpatialIndexName = msLayerEscapePropertyName( layer, szSpatialIndexName); @@ -2353,7 +2362,7 @@ static int msOGRFileWhichShapes(layerObj *layer, rectObj rect, msOGRFileInfo *ps msFree(pszEscapedSpatialIndexName); filter = msStringConcatenate(filter, "\" ms_spat_idx WHERE "); - + char szCond[256]; if( EQUAL(psInfo->dialect, "Spatialite") ) { From a330f938cdf8d6737ca66ccb47829d8cd28b0f44 Mon Sep 17 00:00:00 2001 From: Wouter Date: Wed, 7 Jul 2021 15:54:31 +0200 Subject: [PATCH 073/160] cleanup conditions --- mapogr.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/mapogr.cpp b/mapogr.cpp index 31feafd6ec..d5c00cd70c 100644 --- a/mapogr.cpp +++ b/mapogr.cpp @@ -2313,10 +2313,8 @@ static int msOGRFileWhichShapes(layerObj *layer, rectObj rect, msOGRFileInfo *ps EQUAL(psInfo->dialect, "GPKG")) && psInfo->pszMainTableName != NULL ) { - if( psInfo->dialect && psInfo->pszMainTableName != NULL && - ( (EQUAL(psInfo->dialect, "Spatialite") && psInfo->bHasSpatialIndex) - || EQUAL(psInfo->dialect, "GPKG") ) && - bIsValidRect ) + if( (EQUAL(psInfo->dialect, "Spatialite") && psInfo->bHasSpatialIndex) + || EQUAL(psInfo->dialect, "GPKG") ) { if (filter) filter = msStringConcatenate(filter, " AND "); char* pszEscapedMainTableName = msLayerEscapePropertyName( From 39a4eadd2d545a60f277a2d9bc2fc1a7f3cce242 Mon Sep 17 00:00:00 2001 From: Patrik Sylve Date: Mon, 5 Jul 2021 13:18:51 +0200 Subject: [PATCH 074/160] Change legend symbol scale factor when SIZEUNITS is set to METERS --- maplegend.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/maplegend.c b/maplegend.c index 4c2d377352..ac83d1aa09 100644 --- a/maplegend.c +++ b/maplegend.c @@ -189,6 +189,12 @@ int msDrawLegendIcon(mapObj *map, layerObj *lp, classObj *theclass, /* ** now draw the appropriate color/symbol/size combination */ + + /* Scalefactor will be infinity when SIZEUNITS is set in LAYER */ + if(lp->scalefactor == INFINITY) { + lp->scalefactor = 1.0; + } + switch(type) { case MS_LAYER_POINT: marker.x = dstX + MS_NINT(width / 2.0); From 36880040704eef085b9d1a3c6cebe5b43cc746aa Mon Sep 17 00:00:00 2001 From: Patrik Sylve Date: Wed, 7 Jul 2021 08:44:49 +0200 Subject: [PATCH 075/160] Check if sizeunits is not set to pixels --- maplegend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maplegend.c b/maplegend.c index ac83d1aa09..a71da95266 100644 --- a/maplegend.c +++ b/maplegend.c @@ -191,7 +191,7 @@ int msDrawLegendIcon(mapObj *map, layerObj *lp, classObj *theclass, */ /* Scalefactor will be infinity when SIZEUNITS is set in LAYER */ - if(lp->scalefactor == INFINITY) { + if(lp->sizeunits != MS_PIXELS) { lp->scalefactor = 1.0; } From 3a0b6f9e3bd309471ab1d5a1ab1c5d631e003171 Mon Sep 17 00:00:00 2001 From: Patrik Sylve Date: Thu, 8 Jul 2021 08:36:15 +0200 Subject: [PATCH 076/160] Updated expected image output in autotest --- .../expected/legend_sizeunits_meters.png | Bin 12659 -> 13145 bytes .../expected/legend_sizeunits_meters.svg | 46 +++++++++--------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/msautotest/renderers/expected/legend_sizeunits_meters.png b/msautotest/renderers/expected/legend_sizeunits_meters.png index 4e32ab4e06fc1ac88b80be691b090a6f365eee80..336c7558b0c719488b4463615f5fe68badf93578 100644 GIT binary patch literal 13145 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 diff --git a/msautotest/renderers/expected/legend_sizeunits_meters.svg b/msautotest/renderers/expected/legend_sizeunits_meters.svg index 9d7b9e64c9..e12a9d4bb4 100644 --- a/msautotest/renderers/expected/legend_sizeunits_meters.svg +++ b/msautotest/renderers/expected/legend_sizeunits_meters.svg @@ -4,38 +4,38 @@ - + - + - - - - + + + + - + - + - - - - + + + + - - - - - - - - - - - + + + + + + + + + + + From 1d9873646e66239e0105190714eea82403da4c48 Mon Sep 17 00:00:00 2001 From: Patrik Sylve Date: Fri, 9 Jul 2021 13:34:31 +0200 Subject: [PATCH 077/160] Updated expected img output - pdf & svg --- .../expected/legend_sizeunits_meters.svg | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/msautotest/renderers/expected/legend_sizeunits_meters.svg b/msautotest/renderers/expected/legend_sizeunits_meters.svg index e12a9d4bb4..1a21bac8ff 100644 --- a/msautotest/renderers/expected/legend_sizeunits_meters.svg +++ b/msautotest/renderers/expected/legend_sizeunits_meters.svg @@ -4,38 +4,38 @@ - + - + - - - - + + + + - + - + - - - - + + + + - + - + - + - - - - + + + + From a3c9d8456b59c25b6963d84776a5f85a6437469a Mon Sep 17 00:00:00 2001 From: Patrik Sylve Date: Fri, 9 Jul 2021 17:03:44 +0200 Subject: [PATCH 078/160] update expected pdf From b3f5085976c0934285e243bfe3a5b502f7df8234 Mon Sep 17 00:00:00 2001 From: Patrik Sylve Date: Sun, 11 Jul 2021 17:39:43 +0200 Subject: [PATCH 079/160] copy of legend.pdf From 3e42e356884225c8790b5cb5630a7585aea19355 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 12 Jul 2021 17:03:41 +0200 Subject: [PATCH 080/160] PostGIS: fix ST_Intersects() with collections with PostGIS < 2.5. Fixes https://github.com/MapServer/MapServer/pull/6355#issuecomment-877290417 --- mappostgis.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/mappostgis.c b/mappostgis.c index 4077fdbc5a..c48f867535 100644 --- a/mappostgis.c +++ b/mappostgis.c @@ -2081,9 +2081,20 @@ char *msPostGISBuildSQLWhere(layerObj *layer, rectObj *rect, long *uid, rectObj // otherwise if find_srid() would return 0, ST_Intersects() would not // work at all, which breaks the msautotest/query/query_postgis.map // tests, releated to bdry_counpy2 layer that has no SRID - static const char *strRectTemplate = "ST_Intersects(\"%s\", %s)"; - strRect = (char*)msSmallMalloc(strlen(strRectTemplate) + strBoxLength + strlen(layerinfo->geomcolumn) +1 ); - sprintf(strRect, strRectTemplate, layerinfo->geomcolumn, strBox); + if( layerinfo->version >= 20500 ) + { + static const char *strRectTemplate = "ST_Intersects(\"%s\", %s)"; + strRect = (char*)msSmallMalloc(strlen(strRectTemplate) + strBoxLength + strlen(layerinfo->geomcolumn) +1 ); + sprintf(strRect, strRectTemplate, layerinfo->geomcolumn, strBox); + } + else + { + // ST_Intersects() before PostGIS 2.5 doesn't support collections + // See https://github.com/MapServer/MapServer/pull/6355#issuecomment-877355007 + static const char *strRectTemplate = "(\"%s\" && %s) AND ST_Distance(\"%s\", %s) = 0"; + strRect = (char*)msSmallMalloc(strlen(strRectTemplate) + 2 * (strBoxLength + strlen(layerinfo->geomcolumn)) +1 ); + sprintf(strRect, strRectTemplate, layerinfo->geomcolumn, strBox, layerinfo->geomcolumn, strBox); + } } else { From 292ea2e2b179b1eb4b99ac0de8457bde4f59a326 Mon Sep 17 00:00:00 2001 From: Jeff McKenna Date: Mon, 12 Jul 2021 17:16:21 -0300 Subject: [PATCH 081/160] update for 7.6.4 release --- CMakeLists.txt | 2 +- HISTORY.TXT | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 265902ac32..97bfeade2b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ include(CheckCSourceCompiles) set (MapServer_VERSION_MAJOR 7) set (MapServer_VERSION_MINOR 6) -set (MapServer_VERSION_REVISION 3) +set (MapServer_VERSION_REVISION 4) set (MapServer_VERSION_SUFFIX "") # Set C++ version diff --git a/HISTORY.TXT b/HISTORY.TXT index b801625b2c..6a0f7378a1 100644 --- a/HISTORY.TXT +++ b/HISTORY.TXT @@ -10,7 +10,18 @@ the top of the list.) For a complete change history, please see the Git log comments. For more details about recent point releases, please see the online changelog at: -http://mapserver.org/development/changelog/ +https://mapserver.org/development/changelog/ + +7.6.4 release (2021-07-12) +-------------------------- + +- improved performance of GPKG and SpatiaLite queries (#6361) + +- WFS: fix paging with GPKG/Spatialite datasources and non-point geometries (#6325) + +- PostGIS: use ST_Intersects instead of && for bounding box (#6348) + +see detailed changelog for other fixes 7.6.3 release (2021-04-30) ------------------------- From b21ab27723e811f482ca082dd9495efcb5abf336 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 25 Aug 2021 11:41:51 +0200 Subject: [PATCH 082/160] MVT generation: fix writing of point/multipoint geometries The number associated with the MOVETO command should be the number of coordinate *pairs*. We wrote twice that number. This caused for example the OGR MVT driver to reject such geometries. --- mapmvt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapmvt.c b/mapmvt.c index 1b6e842cb9..b540d57531 100644 --- a/mapmvt.c +++ b/mapmvt.c @@ -319,7 +319,7 @@ int mvtWriteShape( layerObj *layer, shapeObj *shape, VectorTile__Tile__Layer *mv if(layer->type == MS_LAYER_POINT) { int idx=0, lastx=0, lasty=0; - mvt_feature->geometry[idx++] = COMMAND(MOVETO, mvt_feature->n_geometry-1); + mvt_feature->geometry[idx++] = COMMAND(MOVETO, (mvt_feature->n_geometry-1) / 2); for(i=0;inumlines;i++) { for(j=0;jline[i].numpoints;j++) { mvt_feature->geometry[idx++] = PARAMETER(MS_NINT(shape->line[i].point[j].x)-lastx); From ecb47d451281b4f2b116c0eda8913e61ba508120 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 5 Oct 2021 09:37:32 +0200 Subject: [PATCH 083/160] mapserver.h: check _WIN32 instead of WIN32 The canonical macro is _WIN32. WIN32 usually exists as well, but is a non-standard macro. See https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-160 --- mapregex.h | 2 +- mapscript/mapscript.i | 2 +- mapserv.c | 8 ++++---- mapserver.h | 4 ++-- mapwms.c | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/mapregex.h b/mapregex.h index 166c8d4fd8..77a358f0c1 100644 --- a/mapregex.h +++ b/mapregex.h @@ -36,7 +36,7 @@ extern "C" { /* We want these to match the POSIX standard, so we need these*/ /* === regex2.h === */ -#ifdef WIN32 +#ifdef _WIN32 #define MS_API_EXPORT(type) __declspec(dllexport) type __stdcall #elif defined(__GNUC__) && __GNUC__ >= 4 #define MS_API_EXPORT(type) __attribute__ ((visibility("default"))) type diff --git a/mapscript/mapscript.i b/mapscript/mapscript.i index 4ccf92daa3..19bd158a55 100644 --- a/mapscript/mapscript.i +++ b/mapscript/mapscript.i @@ -66,7 +66,7 @@ #include "../../mapprimitive.h" #include "../../mapshape.h" -#if defined(WIN32) && defined(SWIGCSHARP) +#if defined(_WIN32) && defined(SWIGCSHARP) /* is needed for GetExceptionCode() for unhandled exception */ #include #endif diff --git a/mapserv.c b/mapserv.c index 288af3af47..9e912721a5 100644 --- a/mapserv.c +++ b/mapserv.c @@ -45,7 +45,7 @@ #include "cpl_conv.h" -#ifndef WIN32 +#ifndef _WIN32 #include #endif @@ -53,7 +53,7 @@ /************************************************************************/ /* FastCGI cleanup functions. */ /************************************************************************/ -#ifndef WIN32 +#ifndef _WIN32 void msCleanupOnSignal( int nInData ) { /* For some reason, the fastcgi message code does not seem to work */ @@ -68,7 +68,7 @@ void msCleanupOnSignal( int nInData ) } #endif -#ifdef WIN32 +#ifdef _WIN32 void msCleanupOnExit( void ) { /* note that stderr and stdout seem to be non-functional in the */ @@ -246,7 +246,7 @@ int main(int argc, char *argv[]) /* -------------------------------------------------------------------- */ /* Setup cleanup magic, mainly for FastCGI case. */ /* -------------------------------------------------------------------- */ -#ifndef WIN32 +#ifndef _WIN32 signal( SIGUSR1, msCleanupOnSignal ); signal( SIGTERM, msCleanupOnSignal ); #endif diff --git a/mapserver.h b/mapserver.h index 143b9c54b0..6fb124de14 100644 --- a/mapserver.h +++ b/mapserver.h @@ -148,7 +148,7 @@ typedef const ms_uint32 *ms_const_bitarray; /* EQUAL and EQUALN are defined in cpl_port.h, so add them in here if ogr was not included */ #ifndef EQUAL -#if defined(WIN32) || defined(WIN32CE) +#if defined(_WIN32) || defined(WIN32CE) # define EQUAL(a,b) (stricmp(a,b)==0) #else # define EQUAL(a,b) (strcasecmp(a,b)==0) @@ -156,7 +156,7 @@ typedef const ms_uint32 *ms_const_bitarray; #endif #ifndef EQUALN -#if defined(WIN32) || defined(WIN32CE) +#if defined(_WIN32) || defined(WIN32CE) # define EQUALN(a,b,n) (strnicmp(a,b,n)==0) #else # define EQUALN(a,b,n) (strncasecmp(a,b,n)==0) diff --git a/mapwms.c b/mapwms.c index 73fd55131e..65f9b18528 100644 --- a/mapwms.c +++ b/mapwms.c @@ -48,7 +48,7 @@ #include #include -#ifdef WIN32 +#ifdef _WIN32 #include #endif From edc77838bf1c3eef8aa046ca8534167050d51ff4 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 5 Oct 2021 09:45:31 +0200 Subject: [PATCH 084/160] textlayout: declare variables when they are used Fixes various -Wunused warnings. --- mapogcfilter.c | 26 ++++++++++---------------- mapogcfiltercommon.cpp | 11 +++++------ textlayout.c | 7 ++++--- 3 files changed, 19 insertions(+), 25 deletions(-) diff --git a/mapogcfilter.c b/mapogcfilter.c index 2131d38651..1510b98a53 100644 --- a/mapogcfilter.c +++ b/mapogcfilter.c @@ -2176,10 +2176,6 @@ const char* FLTGetDuring(FilterEncodingNode *psFilterNode, const char** ppszTime char *FLTGetSQLExpression(FilterEncodingNode *psFilterNode, layerObj *lp) { char *pszExpression = NULL; - const char *pszAttribute = NULL; - char szTmp[256]; - char **tokens = NULL; - int nTokens = 0, i=0, bString=0; if (psFilterNode == NULL || lp == NULL) return NULL; @@ -2218,12 +2214,13 @@ char *FLTGetSQLExpression(FilterEncodingNode *psFilterNode, layerObj *lp) } else if (psFilterNode->eType == FILTER_NODE_TYPE_FEATUREID) { #if defined(USE_WMS_SVR) || defined (USE_WFS_SVR) || defined (USE_WCS_SVR) || defined(USE_SOS_SVR) if (psFilterNode->pszValue) { - pszAttribute = msOWSLookupMetadata(&(lp->metadata), "OFG", "featureid"); + const char *pszAttribute = msOWSLookupMetadata(&(lp->metadata), "OFG", "featureid"); if (pszAttribute) { - tokens = msStringSplit(psFilterNode->pszValue,',', &nTokens); - bString = 0; + int nTokens = 0; + char **tokens = msStringSplit(psFilterNode->pszValue,',', &nTokens); + int bString = 0; if (tokens && nTokens > 0) { - for (i=0; iconnectiontype == MS_OGR || lp->connectiontype == MS_POSTGIS ) @@ -3128,19 +3126,14 @@ static void FLTRemoveGroupName(FilterEncodingNode *psFilterNode, void FLTPreParseFilterForAliasAndGroup(FilterEncodingNode *psFilterNode, mapObj *map, int i, const char *namespaces) { - layerObj *lp=NULL; - char szTmp[256]; - const char *pszFullName = NULL; - int layerWasOpened = MS_FALSE; - #if defined(USE_WMS_SVR) || defined (USE_WFS_SVR) || defined (USE_WCS_SVR) || defined(USE_SOS_SVR) if (psFilterNode && map && i>=0 && inumlayers) { /*strip name spaces before hand*/ FLTStripNameSpacesFromPropertyName(psFilterNode); - lp = GET_LAYER(map, i); - layerWasOpened = msLayerIsOpen(lp); + layerObj *lp = GET_LAYER(map, i); + int layerWasOpened = msLayerIsOpen(lp); if (msLayerOpen(lp) == MS_SUCCESS && msLayerGetItems(lp) == MS_SUCCESS) { /* Remove group names from property names if using groupname/itemname syntax */ @@ -3152,8 +3145,9 @@ void FLTPreParseFilterForAliasAndGroup(FilterEncodingNode *psFilterNode, for(i=0; inumitems; i++) { if (!lp->items[i] || strlen(lp->items[i]) <= 0) continue; + char szTmp[256]; snprintf(szTmp, sizeof(szTmp), "%s_alias", lp->items[i]); - pszFullName = msOWSLookupMetadata(&(lp->metadata), namespaces, szTmp); + const char *pszFullName = msOWSLookupMetadata(&(lp->metadata), namespaces, szTmp); if (pszFullName) { FLTReplacePropertyName(psFilterNode, pszFullName, lp->items[i]); diff --git a/mapogcfiltercommon.cpp b/mapogcfiltercommon.cpp index 6896044703..8562c05032 100644 --- a/mapogcfiltercommon.cpp +++ b/mapogcfiltercommon.cpp @@ -579,17 +579,15 @@ char *FLTGetSpatialComparisonCommonExpression(FilterEncodingNode *psNode, layerO char *FLTGetFeatureIdCommonExpression(FilterEncodingNode *psFilterNode, layerObj *lp) { char *pszExpression = NULL; - int nTokens = 0, i=0, bString=0; - char **tokens = NULL; - const char *pszAttribute=NULL; #if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) || defined(USE_SOS_SVR) if (psFilterNode->pszValue) { - pszAttribute = msOWSLookupMetadata(&(lp->metadata), "OFG", "featureid"); + const char *pszAttribute = msOWSLookupMetadata(&(lp->metadata), "OFG", "featureid"); if (pszAttribute) { - tokens = msStringSplit(psFilterNode->pszValue,',', &nTokens); + int nTokens = 0; + char **tokens = msStringSplit(psFilterNode->pszValue,',', &nTokens); if (tokens && nTokens > 0) { - for (i=0; inumlines + runs[i].line_number) * tgret->line_height; if(peny != oldpeny) { @@ -714,7 +713,7 @@ int msLayoutTextSymbol(mapObj *map, textSymbolObj *ts, textPathObj *tgret) { unsigned int *codepoint = glyphs.codepoints + runs[i].offset; alloc_glyphs += runs[i].length; tgret->glyphs = msSmallRealloc(tgret->glyphs, alloc_glyphs * sizeof(glyphObj)); - for(j=0;jglyphs[tgret->numglyphs + j]; g->glyph = msGetGlyphByIndex(runs[i].face,tgret->glyph_size, *codepoint); g->face = runs[i].face; @@ -746,11 +745,13 @@ int msLayoutTextSymbol(mapObj *map, textSymbolObj *ts, textPathObj *tgret) { hb_buffer_set_direction(buf, (runs[i].rtl%2) ? HB_DIRECTION_RTL :HB_DIRECTION_LTR); hb_buffer_add_utf32(buf,glyphs.unicodes + runs[i].offset, runs[i].length, 0, runs[i].length); hb_shape(font,buf,hbfeatures,2); + + unsigned int glyph_count; glyph_info = hb_buffer_get_glyph_infos(buf, &glyph_count); glyph_pos = hb_buffer_get_glyph_positions(buf, &glyph_count); alloc_glyphs += glyph_count; tgret->glyphs = msSmallRealloc(tgret->glyphs, alloc_glyphs * sizeof(glyphObj)); - for(j=0;jglyphs[tgret->numglyphs + j]; g->glyph = msGetGlyphByIndex(runs[i].face,tgret->glyph_size,glyph_info[j].codepoint); g->face = runs[i].face; From 334ffee65e79f997600a37d44fab9cdb68182bcf Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 5 Oct 2021 09:53:20 +0200 Subject: [PATCH 085/160] mapogcfilter: disable several functions if they are not used Fixes various -Wunused warnings. --- mapogcfilter.c | 6 ++++++ mapogcsld.c | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/mapogcfilter.c b/mapogcfilter.c index 1510b98a53..1b77ffa612 100644 --- a/mapogcfilter.c +++ b/mapogcfilter.c @@ -3006,6 +3006,7 @@ void FLTDoAxisSwappingIfNecessary(mapObj *map, } } +#if defined(USE_WMS_SVR) || defined (USE_WFS_SVR) || defined (USE_WCS_SVR) || defined(USE_SOS_SVR) static void FLTReplacePropertyName(FilterEncodingNode *psFilterNode, const char *pszOldName, @@ -3028,6 +3029,7 @@ static void FLTReplacePropertyName(FilterEncodingNode *psFilterNode, } } +#endif static int FLTIsGMLDefaultProperty(const char* pszName) { @@ -3039,6 +3041,8 @@ static int FLTIsGMLDefaultProperty(const char* pszName) strcmp(pszName, "@gml:id") == 0); } +#if defined(USE_WMS_SVR) || defined (USE_WFS_SVR) || defined (USE_WCS_SVR) || defined(USE_SOS_SVR) + static void FLTStripNameSpacesFromPropertyName(FilterEncodingNode *psFilterNode) { char **tokens=NULL; @@ -3117,6 +3121,8 @@ static void FLTRemoveGroupName(FilterEncodingNode *psFilterNode, } +#endif + /************************************************************************/ /* FLTPreParseFilterForAliasAndGroup */ /* */ diff --git a/mapogcsld.c b/mapogcsld.c index 71a54c2bcd..8a2036739a 100644 --- a/mapogcsld.c +++ b/mapogcsld.c @@ -124,6 +124,8 @@ int msSLDApplySLDURL(mapObj *map, const char *szURL, int iLayer, #endif } +#if defined(USE_WMS_SVR) || defined (USE_WFS_SVR) || defined (USE_WCS_SVR) || defined(USE_SOS_SVR) + /* -------------------------------------------------------------------- */ /* If the same layer is given more that once, we need to */ /* duplicate it. */ @@ -164,6 +166,8 @@ static void msSLDApplySLD_DuplicateLayers(mapObj *map, int nSLDLayers, layerObj } } +#endif + /************************************************************************/ /* msSLDApplySLD */ /* */ From 59c6c030bef3987f33d0c4576264a60bfdebde5c Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 5 Oct 2021 09:57:56 +0200 Subject: [PATCH 086/160] mapdraw, ...: simplify pointObj initializers With some compile-time options, pointObj has less than 4 fields, so this patch also fixes the build with those options. --- mapdraw.c | 2 +- maplabel.c | 2 +- mappostgis.c | 2 +- mapprimitive.c | 2 +- mapproject.c | 2 +- mapsmoothing.c | 4 ++-- maptemplate.c | 4 ++-- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/mapdraw.c b/mapdraw.c index 9020f90e79..9018edca56 100644 --- a/mapdraw.c +++ b/mapdraw.c @@ -1956,7 +1956,7 @@ int polygonLayerDrawShape(mapObj *map, imageObj *image, layerObj *layer, { int c = shape->classindex; - pointObj annopnt = {0,0,0,0}; // initialize + pointObj annopnt = {0}; // initialize int i; if(MS_DRAW_FEATURES(drawmode)) { diff --git a/maplabel.c b/maplabel.c index 64dff2ad02..33a5146b06 100644 --- a/maplabel.c +++ b/maplabel.c @@ -881,7 +881,7 @@ int msGetTextSymbolSize(mapObj *map, textSymbolObj *ts, rectObj *r) { pointObj get_metrics(pointObj *p, int position, textPathObj *tp, int ox, int oy, double rotation, int buffer, label_bounds *bounds) { - pointObj q = {0,0,0,0}; // initialize + pointObj q = {0}; // initialize double x1=0, y1=0, x2=0, y2=0; double sin_a,cos_a; double w, h, x, y; diff --git a/mappostgis.c b/mappostgis.c index 9bc7a5c181..e67a7c61fb 100644 --- a/mappostgis.c +++ b/mappostgis.c @@ -752,7 +752,7 @@ arcSegmentSide(const pointObj *p1, const pointObj *p2, const pointObj *q) int arcCircleCenter(const pointObj *p1, const pointObj *p2, const pointObj *p3, pointObj *center, double *radius) { - pointObj c = {0,0,0,0}; // initialize + pointObj c = {0}; // initialize double dx21, dy21, dx31, dy31, h21, h31, d, r; /* Circle is closed, so p2 must be opposite p1 & p3. */ diff --git a/mapprimitive.c b/mapprimitive.c index 623eb20dcd..821639d5fa 100644 --- a/mapprimitive.c +++ b/mapprimitive.c @@ -1133,7 +1133,7 @@ void msTransformPixelToShape(shapeObj *shape, rectObj extent, double cellsize) */ static pointObj generateLineIntersection(pointObj a, pointObj b, pointObj c, pointObj d) { - pointObj p = {0,0,0,0}; // initialize + pointObj p = {0}; // initialize double r; double denominator, numerator; diff --git a/mapproject.c b/mapproject.c index b967af1f94..3edd94d991 100644 --- a/mapproject.c +++ b/mapproject.c @@ -1211,7 +1211,7 @@ static int msProjectShapeShouldDoLineCutting(reprojectionObj* reprojector) return MS_FALSE; } - pointObj p = {0,0,0,0}; // initialize + pointObj p = {0}; // initialize double invgt0 = out->gt.need_geotransform ? out->gt.invgeotransform[0] : 0.0; double invgt1 = out->gt.need_geotransform ? out->gt.invgeotransform[1] : 1.0; double invgt3 = out->gt.need_geotransform ? out->gt.invgeotransform[3] : 0.0; diff --git a/mapsmoothing.c b/mapsmoothing.c index b6c68dd085..f291696044 100644 --- a/mapsmoothing.c +++ b/mapsmoothing.c @@ -142,7 +142,7 @@ static int processShapePathDistance(shapeObj *shape, int force) while ((res = nextLineWindow(&lw)) != MS_DONE) { double ratio = 0; - pointObj point = {0,0,0,0}; // initialize + pointObj point = {0}; // initialize if (lw.lineIsRing && lw.pos==lw.line->numpoints-1) { point = newShape->line[i].point[0]; @@ -261,7 +261,7 @@ shapeObj* msSmoothShapeSIA(shapeObj *shape, int ss, int si, char *preprocessing) while ((res = nextLineWindow(&lw)) != MS_DONE) { double sum_x=0, sum_y=0, sum = 0; - pointObj point = {0,0,0,0}; // initialize + pointObj point = {0}; // initialize int k = 0; if (res == MS_FALSE) { /* invalid window */ diff --git a/maptemplate.c b/maptemplate.c index 3ca137bc43..e085e041fa 100644 --- a/maptemplate.c +++ b/maptemplate.c @@ -1728,8 +1728,8 @@ static int processShplabelTag(layerObj *layer, char **line, shapeObj *origshape) } if(labelposvalid == MS_TRUE) { - pointObj p1 = {0,0,0,0}; // initialize - pointObj p2 = {0,0,0,0}; + pointObj p1 = {0}; // initialize + pointObj p2 = {0}; int label_offset_x, label_offset_y; labelObj *label=NULL; label_bounds lbounds; From d82a348819a171f1437df7575a2d45f66370219a Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 5 Oct 2021 10:37:48 +0200 Subject: [PATCH 087/160] mapmetadata: disable if compile-time features are missing Fixes build breakage. --- mapmetadata.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mapmetadata.c b/mapmetadata.c index 4ce677d8a7..f8d7303e83 100644 --- a/mapmetadata.c +++ b/mapmetadata.c @@ -31,6 +31,8 @@ #include "mapowscommon.h" #include "maplibxml2.h" +#if defined(USE_WMS_SVR) || defined (USE_WFS_SVR) || defined (USE_WCS_SVR) || defined(USE_SOS_SVR) || defined(USE_WMS_LYR) || defined(USE_WFS_LYR) + #ifdef USE_LIBXML2 /************************************************************************/ @@ -949,3 +951,4 @@ void msMetadataSetGetMetadataURL(layerObj *lp, const char *url) msFree(pszMetadataURL); } +#endif From 78d9fe2d2ee071a3f1889a3574543b850b2d6a16 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 5 Oct 2021 10:47:41 +0200 Subject: [PATCH 088/160] mapstring: optimize msStringToUpper(), msStringToLower() Traverse the string only once. Also, this removes code which triggered -Wsign-compare. --- mapstring.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/mapstring.c b/mapstring.c index 148cd2bb5d..17df5dc5cc 100644 --- a/mapstring.c +++ b/mapstring.c @@ -377,25 +377,17 @@ char *msIntToString(int value) void msStringToUpper(char *string) { - int i; - if (string != NULL) { - for (i = 0; i < strlen(string); i++) { - string[i] = toupper(string[i]); - } - return; + for (; *string; ++string) + *string = toupper(*string); } } void msStringToLower(char *string) { - int i; - if (string != NULL) { - for (i = 0; i < strlen(string); i++) { - string[i] = tolower(string[i]); - } - return; + for (; *string; ++string) + *string = tolower(*string); } } From 89b4448ec9933bc356a64bc8945c27ad9e47c800 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 5 Oct 2021 09:39:09 +0200 Subject: [PATCH 089/160] mapshape: fix buffer overflow in msSHPReadShape() The data in panParts is never checked. The only check was "numpoints<=0", but that is not enough. Three very bad things can happen: - arbitrary huge values, leading to allocations of up to two billion elements (INT_MAX), bypassing the 50 million limit which was previously put on "nPoints" - overflowing the "pabyRec" buffer in the memcpy() call - integer overflow in the malloc() call, writing past the allocated buffer The latter is probably enough for remote code execution. Vulnerability found with libFuzzer. --- mapshape.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/mapshape.c b/mapshape.c index 0a607d052d..d47e4c8b14 100644 --- a/mapshape.c +++ b/mapshape.c @@ -1372,11 +1372,12 @@ void msSHPReadShape( SHPHandle psSHP, int hEntity, shapeObj *shape ) k = 0; /* overall point counter */ for( i = 0; i < nParts; i++) { - if( i == nParts-1) - shape->line[i].numpoints = nPoints - psSHP->panParts[i]; - else - shape->line[i].numpoints = psSHP->panParts[i+1] - psSHP->panParts[i]; - if (shape->line[i].numpoints <= 0) { + const ms_int32 end = i == nParts - 1 + ? nPoints + : psSHP->panParts[i+1]; + shape->line[i].numpoints = end - psSHP->panParts[i]; + if (psSHP->panParts[i] < 0 || end < 0 || end > nPoints || + psSHP->panParts[i] >= end) { msSetError(MS_SHPERR, "Corrupted .shp file : shape %d, shape->line[%d].numpoints=%d", "msSHPReadShape()", hEntity, i, shape->line[i].numpoints); while(--i >= 0) From 6fa2243c25726cb62718253ef260632301a05415 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 5 Oct 2021 09:39:40 +0200 Subject: [PATCH 090/160] mapshape: fix double free bug after error in msSHPReadShape() After freeing the "line" field, we need to clear it, or else it will be freed again in msFreeShape(). In two code paths, the "numlines" field was not cleared, which could lead to a use-after-free bug in msFreeShape(), which in turn could either crash or lead to another double-free bug in msFreeShape(). Vulnerability found with libFuzzer. --- mapshape.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mapshape.c b/mapshape.c index d47e4c8b14..1ad0915900 100644 --- a/mapshape.c +++ b/mapshape.c @@ -1393,6 +1393,7 @@ void msSHPReadShape( SHPHandle psSHP, int hEntity, shapeObj *shape ) while(--i >= 0) free(shape->line[i].point); free(shape->line); + shape->line = NULL; shape->numlines = 0; shape->type = MS_SHAPE_NULL; msSetError(MS_MEMERR, "Out of memory", "msSHPReadShape()"); @@ -1488,6 +1489,8 @@ void msSHPReadShape( SHPHandle psSHP, int hEntity, shapeObj *shape ) if (nPoints < 0 || nPoints > 50 * 1000 * 1000) { free(shape->line); + shape->line = NULL; + shape->numlines = 0; shape->type = MS_SHAPE_NULL; msSetError(MS_SHPERR, "Corrupted .shp file : shape %d, nPoints=%d.", "msSHPReadShape()", hEntity, nPoints); @@ -1499,6 +1502,8 @@ void msSHPReadShape( SHPHandle psSHP, int hEntity, shapeObj *shape ) nRequiredSize += 16 + nPoints * 8; if (nRequiredSize > nEntitySize) { free(shape->line); + shape->line = NULL; + shape->numlines = 0; shape->type = MS_SHAPE_NULL; msSetError(MS_SHPERR, "Corrupted .shp file : shape %d : nPoints = %d, nEntitySize = %d", "msSHPReadShape()", hEntity, nPoints, nEntitySize); @@ -1510,6 +1515,7 @@ void msSHPReadShape( SHPHandle psSHP, int hEntity, shapeObj *shape ) shape->line[0].point = (pointObj *) malloc( nPoints * sizeof(pointObj) ); if (shape->line[0].point == NULL) { free(shape->line); + shape->line = NULL; shape->numlines = 0; shape->type = MS_SHAPE_NULL; msSetError(MS_MEMERR, "Out of memory", "msSHPReadShape()"); From 434164fcc26f9d36df556d1809749db7fcaa4565 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 5 Oct 2021 12:09:19 +0200 Subject: [PATCH 091/160] mapshape: check msSHPReadBounds() return value, fix endless loop With a crafted shapefile, it was possible to put msShapefileWhichShapes() into an extremely long loop, calling msSHPReadBounds() over and over, even if all of those calls fail. This patch adds error checking, and if an error occurs, msShapefileWhichShapes() gives up, because after an I/O error, there is no reasonable chance that anything will ever work properly. Vulnerability found by libFuzzer. --- mapshape.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/mapshape.c b/mapshape.c index 1ad0915900..20eef83c52 100644 --- a/mapshape.c +++ b/mapshape.c @@ -1741,7 +1741,10 @@ int msShapefileOpen(shapefileObj *shpfile, const char *mode, const char *filenam return -1; } - msSHPReadBounds( shpfile->hSHP, -1, &(shpfile->bounds)); + if( msSHPReadBounds( shpfile->hSHP, -1, &(shpfile->bounds)) != MS_SUCCESS ) { + msSHPClose(shpfile->hSHP); + return -1; + } bufferSize = strlen(filename)+5; dbfFilename = (char *)msSmallMalloc(bufferSize); @@ -1870,8 +1873,10 @@ int msShapefileWhichShapes(shapefileObj *shpfile, rectObj rect, int debug) } for(i=0; inumshapes; i++) { - if(msSHPReadBounds(shpfile->hSHP, i, &shaperect) == MS_SUCCESS) - if(msRectOverlap(&shaperect, &rect) == MS_TRUE) msSetBit(shpfile->status, i, 1); + if(msSHPReadBounds(shpfile->hSHP, i, &shaperect) != MS_SUCCESS) + return(MS_FAILURE); + + if(msRectOverlap(&shaperect, &rect) == MS_TRUE) msSetBit(shpfile->status, i, 1); } } } From b08cf2dc8433bdedc93cb1a0f61deb215822a9ee Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sat, 24 Apr 2021 17:17:13 +0200 Subject: [PATCH 092/160] msSHPReadAllocateBuffer(): use correct realloc() pattern and validate size (CID 1503560) --- mapshape.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/mapshape.c b/mapshape.c index 20eef83c52..2332a80fc6 100644 --- a/mapshape.c +++ b/mapshape.c @@ -1018,20 +1018,24 @@ int msSHPWriteShape(SHPHandle psSHP, shapeObj *shape ) static int msSHPReadAllocateBuffer( SHPHandle psSHP, int hEntity, const char* pszCallingFunction) { - int nEntitySize = msSHXReadSize(psSHP, hEntity) + 8; + int nEntitySize = msSHXReadSize(psSHP, hEntity); + if( nEntitySize > INT_MAX - 8 ) { + msSetError(MS_MEMERR, "Out of memory. Cannot allocate %d bytes. Probably broken shapefile at feature %d", + pszCallingFunction, nEntitySize, hEntity); + return(MS_FAILURE); + } + nEntitySize += 8; /* -------------------------------------------------------------------- */ /* Ensure our record buffer is large enough. */ /* -------------------------------------------------------------------- */ if( nEntitySize > psSHP->nBufSize ) { - psSHP->pabyRec = (uchar *) SfRealloc(psSHP->pabyRec,nEntitySize); - if (psSHP->pabyRec == NULL) { - /* Reallocate previous successfull size for following features */ - psSHP->pabyRec = msSmallMalloc(psSHP->nBufSize); - + uchar* pabyRec = (uchar *) SfRealloc(psSHP->pabyRec,nEntitySize); + if (pabyRec == NULL) { msSetError(MS_MEMERR, "Out of memory. Cannot allocate %d bytes. Probably broken shapefile at feature %d", pszCallingFunction, nEntitySize, hEntity); return(MS_FAILURE); } + psSHP->pabyRec = pabyRec; psSHP->nBufSize = nEntitySize; } if (psSHP->pabyRec == NULL) { From 01ca4389ec64cf4357a59debee02f38cd3b8aefd Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 5 Oct 2021 17:43:08 +0200 Subject: [PATCH 093/160] mapshape: check for negative sizes in msSHPReadAllocateBuffer() Yet another buffer overflow found by libFuzzer. --- mapshape.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapshape.c b/mapshape.c index 2332a80fc6..24a6aae315 100644 --- a/mapshape.c +++ b/mapshape.c @@ -1019,7 +1019,7 @@ static int msSHPReadAllocateBuffer( SHPHandle psSHP, int hEntity, const char* ps { int nEntitySize = msSHXReadSize(psSHP, hEntity); - if( nEntitySize > INT_MAX - 8 ) { + if( nEntitySize < 0 || nEntitySize > INT_MAX - 8 ) { msSetError(MS_MEMERR, "Out of memory. Cannot allocate %d bytes. Probably broken shapefile at feature %d", pszCallingFunction, nEntitySize, hEntity); return(MS_FAILURE); From b07737618acc01b4db070b9c966dc2de3fa8e4ac Mon Sep 17 00:00:00 2001 From: jmckenna Date: Tue, 8 Feb 2022 15:47:35 -0400 Subject: [PATCH 094/160] minor fix for writing PostgreSQL JOIN through mapscript --- mapfile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapfile.c b/mapfile.c index d10229758c..ec0f54baad 100644 --- a/mapfile.c +++ b/mapfile.c @@ -835,7 +835,7 @@ static void writeJoin(FILE *stream, int indent, joinObj *join) writeString(stream, indent, "TABLE", NULL, join->table); writeString(stream, indent, "TEMPLATE", NULL, join->template); writeString(stream, indent, "TO", NULL, join->to); - writeKeyword(stream, indent, "CONNECTIONTYPE", join->connectiontype, 3, MS_DB_CSV, "CSV", MS_DB_POSTGRES, "POSTRESQL", MS_DB_MYSQL, "MYSQL"); + writeKeyword(stream, indent, "CONNECTIONTYPE", join->connectiontype, 3, MS_DB_CSV, "CSV", MS_DB_POSTGRES, "POSTGRESQL", MS_DB_MYSQL, "MYSQL"); writeKeyword(stream, indent, "TYPE", join->type, 1, MS_JOIN_ONE_TO_MANY, "ONE-TO-MANY"); writeBlockEnd(stream, indent, "JOIN"); } From 6076739213365195c4d9001e9f9c64bd474c1298 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sat, 12 Feb 2022 20:18:00 +0100 Subject: [PATCH 095/160] EnablePaging methods: check return of Open() method to avoid assertions --- mapmssql2008.c | 7 ++++++- mapogr.cpp | 14 ++++++++++++-- maporaclespatial.c | 7 ++++++- mappostgis.c | 7 ++++++- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/mapmssql2008.c b/mapmssql2008.c index 6cb8515a3c..58daa3e6fd 100644 --- a/mapmssql2008.c +++ b/mapmssql2008.c @@ -3027,7 +3027,12 @@ void msMSSQL2008EnablePaging(layerObj *layer, int value) msMSSQL2008LayerInfo *layerinfo = NULL; if (!msMSSQL2008LayerIsOpen(layer)) - msMSSQL2008LayerOpen(layer); + { + if(msMSSQL2008LayerOpen(layer) != MS_SUCCESS) + { + return; + } + } assert(layer->layerinfo != NULL); layerinfo = (msMSSQL2008LayerInfo *)layer->layerinfo; diff --git a/mapogr.cpp b/mapogr.cpp index d5c00cd70c..4a618b6fa5 100644 --- a/mapogr.cpp +++ b/mapogr.cpp @@ -5384,7 +5384,12 @@ static void msOGREnablePaging(layerObj *layer, int value) } if(!msOGRLayerIsOpen(layer)) - msOGRLayerOpenVT(layer); + { + if(msOGRLayerOpenVT(layer) != MS_SUCCESS) + { + return; + } + } assert( layer->layerinfo != NULL); @@ -5401,7 +5406,12 @@ static int msOGRGetPaging(layerObj *layer) } if(!msOGRLayerIsOpen(layer)) - msOGRLayerOpenVT(layer); + { + if(msOGRLayerOpenVT(layer) != MS_SUCCESS) + { + return FALSE; + } + } assert( layer->layerinfo != NULL); diff --git a/maporaclespatial.c b/maporaclespatial.c index 041323086e..8bbcc32417 100644 --- a/maporaclespatial.c +++ b/maporaclespatial.c @@ -3477,7 +3477,12 @@ void msOracleSpatialEnablePaging(layerObj *layer, int value) msDebug("msOracleSpatialLayerEnablePaging was called.\n"); if(!msOracleSpatialLayerIsOpen(layer)) - msOracleSpatialLayerOpen(layer); + { + if(msOracleSpatialLayerOpen(layer) != MS_SUCCESS) + { + return; + } + } assert( layer->layerinfo != NULL); layerinfo = (msOracleSpatialLayerInfo *)layer->layerinfo; diff --git a/mappostgis.c b/mappostgis.c index e67a7c61fb..56e404a190 100644 --- a/mappostgis.c +++ b/mappostgis.c @@ -3985,7 +3985,12 @@ void msPostGISEnablePaging(layerObj *layer, int value) } if(!msPostGISLayerIsOpen(layer)) - msPostGISLayerOpen(layer); + { + if(msPostGISLayerOpen(layer) != MS_SUCCESS) + { + return; + } + } assert( layer->layerinfo != NULL); From f119db1dbc28a55217550ce54a7ec396f59b99e0 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 14 Feb 2022 20:41:59 +0100 Subject: [PATCH 096/160] msProjectRect(): fix to deal with +over added, assuming https://github.com/OSGeo/PROJ/pull/3055 applied (PROJ 9) (fixes #6478) --- mapproject.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/mapproject.c b/mapproject.c index 3edd94d991..6fe74f4165 100644 --- a/mapproject.c +++ b/mapproject.c @@ -112,6 +112,28 @@ static int msProjectHasLonWrapOrOver(projectionObj *in) { return MS_FALSE; } +static char* getStringFromArgv(int argc, char** args) +{ + int i; + int len = 0; + for( i = 0; i < argc; i++ ) + { + len += strlen(args[i]) + 1; + } + char* str = msSmallMalloc(len + 1); + len = 0; + for( i = 0; i < argc; i++ ) + { + size_t arglen = strlen(args[i]); + memcpy(str + len, args[i], arglen); + len += arglen; + str[len] = ' '; + len ++; + } + str[len] = 0; + return str; +} + /************************************************************************/ /* createNormalizedPJ() */ /************************************************************************/ @@ -142,6 +164,8 @@ static PJ* createNormalizedPJ(projectionObj *in, projectionObj *out, int* pbFree PJ* pj_normalized; if( !in_str || !out_str ) return NULL; + char* in_str_for_cache = getStringFromArgv(in->numargs, in->args); + char* out_str_for_cache = getStringFromArgv(out->numargs, out->args); if( in->proj_ctx->proj_ctx == out->proj_ctx->proj_ctx ) { @@ -149,8 +173,8 @@ static PJ* createNormalizedPJ(projectionObj *in, projectionObj *out, int* pbFree pjCacheEntry* pj_cache = in->proj_ctx->pj_cache; for( i = 0; i < in->proj_ctx->pj_cache_size; i++ ) { - if (strcmp(pj_cache[i].inStr, in_str) == 0 && - strcmp(pj_cache[i].outStr, out_str) == 0 ) + if (strcmp(pj_cache[i].inStr, in_str_for_cache) == 0 && + strcmp(pj_cache[i].outStr, out_str_for_cache) == 0 ) { PJ* ret = pj_cache[i].pj; if( i != 0 ) @@ -165,6 +189,8 @@ static PJ* createNormalizedPJ(projectionObj *in, projectionObj *out, int* pbFree fprintf(stderr, "cache hit!\n"); #endif *pbFreePJ = FALSE; + msFree(in_str_for_cache); + msFree(out_str_for_cache); return ret; } } @@ -225,14 +251,26 @@ static PJ* createNormalizedPJ(projectionObj *in, projectionObj *out, int* pbFree else #endif { +#if PROJ_VERSION_MAJOR > 6 || (PROJ_VERSION_MAJOR == 6 && PROJ_VERSION_MINOR >= 2) + pj_raw = proj_create_crs_to_crs_from_pj(in->proj_ctx->proj_ctx, in->proj, out->proj, NULL, NULL); +#else pj_raw = proj_create_crs_to_crs(in->proj_ctx->proj_ctx, in_str, out_str, NULL); +#endif if( !pj_raw ) + { + msFree(in_str_for_cache); + msFree(out_str_for_cache); return NULL; + } pj_normalized = proj_normalize_for_visualization(in->proj_ctx->proj_ctx, pj_raw); proj_destroy(pj_raw); } if( !pj_normalized ) + { + msFree(in_str_for_cache); + msFree(out_str_for_cache); return NULL; + } if( in->proj_ctx->proj_ctx == out->proj_ctx->proj_ctx ) { @@ -255,8 +293,8 @@ static PJ* createNormalizedPJ(projectionObj *in, projectionObj *out, int* pbFree memmove(&pj_cache[1], &pj_cache[0], (PJ_CACHE_ENTRY_SIZE - 1) * sizeof(pjCacheEntry)); } - pj_cache[i].inStr = msStrdup(in_str); - pj_cache[i].outStr = msStrdup(out_str); + pj_cache[i].inStr = msStrdup(in_str_for_cache); + pj_cache[i].outStr = msStrdup(out_str_for_cache); pj_cache[i].pj = pj_normalized; *pbFreePJ = FALSE; } @@ -265,6 +303,9 @@ static PJ* createNormalizedPJ(projectionObj *in, projectionObj *out, int* pbFree *pbFreePJ = TRUE; } + msFree(in_str_for_cache); + msFree(out_str_for_cache); + return pj_normalized; } @@ -865,6 +906,11 @@ int msProcessProjection(projectionObj *p) #endif args[p->numargs] = (char*) "type=crs"; +#if 0 + for( int i = 0; i < p->numargs + 1; i++ ) + fprintf(stderr, "%s ", args[i]); + fprintf(stderr, "\n"); +#endif if( !(p->proj = proj_create_argv(p->proj_ctx->proj_ctx, p->numargs + 1, args)) ) { int l_pj_errno = proj_context_errno (p->proj_ctx->proj_ctx); if(p->numargs>1) { From 25171a48476558337dedfe449eb1e8daaa6a5b9a Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 14 Feb 2022 21:05:34 +0100 Subject: [PATCH 097/160] msProjectRect(): workaround for issue of #6478 for PROJ >= 6 and < 9 (fixes #6478) --- mapproject.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/mapproject.c b/mapproject.c index 6fe74f4165..92aad05e19 100644 --- a/mapproject.c +++ b/mapproject.c @@ -2059,7 +2059,26 @@ int msProjectRect(projectionObj *in, projectionObj *out, rectObj *rect) * parameter in order to disable dateline wrapping. */ else { - if(out) { + int apply_over = MS_TRUE; +#if PROJ_VERSION_MAJOR >= 6 && PROJ_VERSION_MAJOR < 9 + // Workaround PROJ [6,9[ bug (fixed per https://github.com/OSGeo/PROJ/pull/3055) + // that prevents datum shifts from being applied when +over is added to +init=epsg:XXXX + // This is far from being bullet proof but it should work for most common use cases + if(in && in->proj) + { + if( in->numargs == 1 && EQUAL(in->args[0], "init=epsg:4326") && + rect->minx >= -180 && rect->maxx <= 180 ) + { + apply_over = MS_FALSE; + } + else if( in->numargs == 1 && EQUAL(in->args[0], "init=epsg:3857") && + rect->minx >= -20037508.3427892 && rect->maxx <= 20037508.3427892 ) + { + apply_over = MS_FALSE; + } + } +#endif + if(out && apply_over) { bFreeOutOver = MS_TRUE; msInitProjection(&out_over); msCopyProjectionExtended(&out_over,out,&over,1); @@ -2071,7 +2090,7 @@ int msProjectRect(projectionObj *in, projectionObj *out, rectObj *rect) } else { outp = out; } - if(in) { + if(in && apply_over) { bFreeInOver = MS_TRUE; msInitProjection(&in_over); msCopyProjectionExtended(&in_over,in,&over,1); From 011f39bdf7516dc66ba9f9fd0cbd6ae6c59b9594 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 14 Feb 2022 22:12:00 +0100 Subject: [PATCH 098/160] msautotest: add test for #6478 --- msautotest/mspython/test_bug_check.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/msautotest/mspython/test_bug_check.py b/msautotest/mspython/test_bug_check.py index 3d00860238..3dec24fafc 100755 --- a/msautotest/mspython/test_bug_check.py +++ b/msautotest/mspython/test_bug_check.py @@ -145,3 +145,25 @@ def test_reprojection_lines_from_polar_stereographic_to_webmercator(): assert point12.x == pytest.approx(-20037508.34, abs=1e-2) assert point21.x == pytest.approx(20037508.34, abs=1e-2) assert point22.x == pytest.approx(19926188.85, abs=1e-2) + + +############################################################################### +# Test reprojection of rectangle involving a datum shift (#6478) + +def test_reprojection_rect_and_datum_shift(): + + webmercator = mapscript.projectionObj("init=epsg:3857") + epsg_28992 = mapscript.projectionObj("init=epsg:28992") # "Amersfoort / RD New" + + point = mapscript.pointObj(545287, 6867556) + point.project(webmercator, epsg_28992) + if point.x == pytest.approx(121685, abs=2): + # Builds of PROJ >= 6 and < 8 with -DACCEPT_USE_OF_DEPRECATED_PROJ_API_H + # use pj_transform() but it doesn't work well with datum shift + # This is a non-nominal configuration used in some CI confs when use a + # PROJ 7 build to test PROJ.4 API and PROJ 6 API. + pytest.skip('This test cannot run with PROJ [6,8[ in builds with ACCEPT_USE_OF_DEPRECATED_PROJ_API_H') + + rect = mapscript.rectObj(545287, 6867556, 545689, 6868025) + assert rect.project(webmercator, epsg_28992) == 0 + assert rect.minx == pytest.approx(121711, abs=2) From 5482592e606f9d629e6c0f42105330e6ede4f215 Mon Sep 17 00:00:00 2001 From: jmckenna Date: Mon, 28 Feb 2022 15:21:00 -0400 Subject: [PATCH 099/160] handle PROJ6 invalid coordinate message through DEBUG levels --- mapproject.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mapproject.c b/mapproject.c index 92aad05e19..5e8336a655 100644 --- a/mapproject.c +++ b/mapproject.c @@ -362,11 +362,11 @@ static void msProjErrorLogger(void * user_data, int level, const char * message) { (void)user_data; - if( level == PJ_LOG_ERROR ) + if( level == PJ_LOG_ERROR && msGetGlobalDebugLevel() >= MS_DEBUGLEVEL_VV) { msDebug( "PROJ: Error: %s\n", message ); } - else if( level == PJ_LOG_DEBUG ) + else if( level == PJ_LOG_DEBUG) { msDebug( "PROJ: Debug: %s\n", message ); } From 6eeff73e35490bd05cde1f8882775c8e48e28fce Mon Sep 17 00:00:00 2001 From: jmckenna Date: Mon, 28 Feb 2022 15:35:51 -0400 Subject: [PATCH 100/160] handle PROJ6 invalid coordinate message through DEBUG levels --- mapproject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapproject.c b/mapproject.c index 5e8336a655..1c7ab67ad7 100644 --- a/mapproject.c +++ b/mapproject.c @@ -366,7 +366,7 @@ static void msProjErrorLogger(void * user_data, { msDebug( "PROJ: Error: %s\n", message ); } - else if( level == PJ_LOG_DEBUG) + else if( level == PJ_LOG_DEBUG ) { msDebug( "PROJ: Debug: %s\n", message ); } From 93d4b6951e7723582b6761161679a5617ee3f275 Mon Sep 17 00:00:00 2001 From: jmckenna Date: Tue, 1 Mar 2022 08:25:07 -0400 Subject: [PATCH 101/160] check for PROJ6 --- mapproject.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mapproject.c b/mapproject.c index 1c7ab67ad7..b8f7f568f3 100644 --- a/mapproject.c +++ b/mapproject.c @@ -362,7 +362,11 @@ static void msProjErrorLogger(void * user_data, int level, const char * message) { (void)user_data; - if( level == PJ_LOG_ERROR && msGetGlobalDebugLevel() >= MS_DEBUGLEVEL_VV) +#if PROJ_VERSION_MAJOR >= 6 + if( level == PJ_LOG_ERROR && msGetGlobalDebugLevel() >= MS_DEBUGLEVEL_VV ) +#else + if( level == PJ_LOG_ERROR ) +#endif { msDebug( "PROJ: Error: %s\n", message ); } From e816d4c565045af9beffd661bdae77b684b7ab74 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 16 Mar 2022 19:32:29 +0100 Subject: [PATCH 102/160] Labeling lines with zero lengths may cause the application to crash (#6493) (#6495) Co-authored-by: Tamas Szekeres --- mapprimitive.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mapprimitive.c b/mapprimitive.c index 821639d5fa..76f92ee7f6 100644 --- a/mapprimitive.c +++ b/mapprimitive.c @@ -1562,7 +1562,7 @@ int msPolygonLabelPoint(shapeObj *p, pointObj *lp, double min_dimension) void msPolylineComputeLineSegments(shapeObj *shape, struct polyline_lengths *pll) { int i, j; - double max_line_length=0, max_segment_length=0, segment_length; + double max_line_length=-1, max_segment_length=-1, segment_length; pll->ll = msSmallMalloc(shape->numlines * sizeof(struct line_lengths)); pll->total_length = 0; @@ -1571,7 +1571,7 @@ void msPolylineComputeLineSegments(shapeObj *shape, struct polyline_lengths *pll for(i=0; inumlines; i++) { struct line_lengths *ll = &pll->ll[i]; - double max_subline_segment_length = 0; + double max_subline_segment_length = -1; if(shape->line[i].numpoints > 1) { ll->segment_lengths = (double*) msSmallMalloc(sizeof(double) * (shape->line[i].numpoints - 1)); From 2fa5f4af6c6f853b41c8e7b2177bba250bd6d785 Mon Sep 17 00:00:00 2001 From: sethg Date: Mon, 21 Mar 2022 21:22:54 +0100 Subject: [PATCH 103/160] Fix some CMake warnings --- cmake/FindCairo.cmake | 2 +- cmake/FindFriBiDi.cmake | 2 +- cmake/FindHarfBuzz.cmake | 2 +- cmake/FindProj.cmake | 2 +- cmake/FindSVGCairo.cmake | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cmake/FindCairo.cmake b/cmake/FindCairo.cmake index 8b1075436f..33dfc1c2cc 100644 --- a/cmake/FindCairo.cmake +++ b/cmake/FindCairo.cmake @@ -73,5 +73,5 @@ ENDIF () set(CAIRO_INCLUDE_DIRS ${CAIRO_INCLUDE_DIR} ${FC_INCLUDE_DIR}) set(CAIRO_LIBRARIES ${CAIRO_LIBRARY}) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(CAIRO DEFAULT_MSG CAIRO_LIBRARY CAIRO_INCLUDE_DIR) +find_package_handle_standard_args(Cairo DEFAULT_MSG CAIRO_LIBRARY CAIRO_INCLUDE_DIR) mark_as_advanced(CAIRO_LIBRARY CAIRO_INCLUDE_DIR) diff --git a/cmake/FindFriBiDi.cmake b/cmake/FindFriBiDi.cmake index 4356a03768..f8144a87df 100644 --- a/cmake/FindFriBiDi.cmake +++ b/cmake/FindFriBiDi.cmake @@ -26,5 +26,5 @@ find_library(FRIBIDI_LIBRARY set(FRIBIDI_INCLUDE_DIRS ${FRIBIDI_INCLUDE_DIR}) set(FRIBIDI_LIBRARIES ${FRIBIDI_LIBRARY}) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(FRIBIDI DEFAULT_MSG FRIBIDI_LIBRARY FRIBIDI_INCLUDE_DIR) +find_package_handle_standard_args(FriBiDi DEFAULT_MSG FRIBIDI_LIBRARY FRIBIDI_INCLUDE_DIR) mark_as_advanced(FRIBIDI_LIBRARY FRIBIDI_INCLUDE_DIR) diff --git a/cmake/FindHarfBuzz.cmake b/cmake/FindHarfBuzz.cmake index ba7d5d9bab..d036d59150 100644 --- a/cmake/FindHarfBuzz.cmake +++ b/cmake/FindHarfBuzz.cmake @@ -31,7 +31,7 @@ # HARFBUZZ_INCLUDE_DIR - containg the HarfBuzz headers # HARFBUZZ_LIBRARY - containg the HarfBuzz library -include(FindPkgConfig) +find_package(PkgConfig) pkg_check_modules(PC_HARFBUZZ harfbuzz>=0.9.18) diff --git a/cmake/FindProj.cmake b/cmake/FindProj.cmake index 63648d1afc..d71cea721e 100644 --- a/cmake/FindProj.cmake +++ b/cmake/FindProj.cmake @@ -11,7 +11,7 @@ FIND_PATH(PROJ_INCLUDE_DIR NAMES proj.h proj_api.h) FIND_LIBRARY(PROJ_LIBRARY NAMES proj proj_i) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(PROJ DEFAULT_MSG PROJ_LIBRARY PROJ_INCLUDE_DIR) +find_package_handle_standard_args(Proj DEFAULT_MSG PROJ_LIBRARY PROJ_INCLUDE_DIR) mark_as_advanced(PROJ_LIBRARY PROJ_INCLUDE_DIR) diff --git a/cmake/FindSVGCairo.cmake b/cmake/FindSVGCairo.cmake index 8da024fa58..0a2972ef87 100644 --- a/cmake/FindSVGCairo.cmake +++ b/cmake/FindSVGCairo.cmake @@ -48,5 +48,5 @@ find_package_handle_standard_args(SVG DEFAULT_MSG SVG_LIBRARY SVG_INCLUDE_DIR) mark_as_advanced(SVG_LIBRARY SVG_INCLUDE_DIR) set(SVGCAIRO_INCLUDE_DIRS ${SVGCAIRO_INCLUDE_DIR}) set(SVGCAIRO_LIBRARIES ${SVGCAIRO_LIBRARY}) -find_package_handle_standard_args(SVGCAIRO DEFAULT_MSG SVGCAIRO_LIBRARY SVGCAIRO_INCLUDE_DIR) +find_package_handle_standard_args(SVGCairo DEFAULT_MSG SVGCAIRO_LIBRARY SVGCAIRO_INCLUDE_DIR) mark_as_advanced(SVGCAIRO_LIBRARY SVGCAIRO_INCLUDE_DIR) From e4b27656f4000fa0f3f02294f6d23007dfd479c4 Mon Sep 17 00:00:00 2001 From: Tamas Szekeres Date: Mon, 2 May 2022 22:03:53 +0200 Subject: [PATCH 104/160] Handle null shapes in msShapefileWhichShapes (#6521) --- mapshape.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mapshape.c b/mapshape.c index 24a6aae315..fa6d536f1f 100644 --- a/mapshape.c +++ b/mapshape.c @@ -1877,8 +1877,12 @@ int msShapefileWhichShapes(shapefileObj *shpfile, rectObj rect, int debug) } for(i=0; inumshapes; i++) { - if(msSHPReadBounds(shpfile->hSHP, i, &shaperect) != MS_SUCCESS) + if(msSHPReadBounds(shpfile->hSHP, i, &shaperect) != MS_SUCCESS) { + if (msSHXReadSize(shpfile->hSHP, i) == 4) { /* handle NULL shape */ + continue; + } return(MS_FAILURE); + } if(msRectOverlap(&shaperect, &rect) == MS_TRUE) msSetBit(shpfile->status, i, 1); } From 6ceabc29065e77286575f90ee221cbccbd360e53 Mon Sep 17 00:00:00 2001 From: Tamas Szekeres Date: Wed, 4 May 2022 19:57:26 +0200 Subject: [PATCH 105/160] Fix shapefile driver crash in msSHPLayerNextShape (#6524) --- mapshape.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mapshape.c b/mapshape.c index fa6d536f1f..0524c56f69 100644 --- a/mapshape.c +++ b/mapshape.c @@ -2787,6 +2787,11 @@ int msSHPLayerNextShape(layerObj *layer, shapeObj *shape) return MS_FAILURE; } + if (!shpfile->status) { + /* probably whichShapes didn't overlap */ + return MS_DONE; + } + i = msGetNextBit(shpfile->status, shpfile->lastshape + 1, shpfile->numshapes); shpfile->lastshape = i; if(i == -1) return(MS_DONE); /* nothing else to read */ From eea8eba42c3aa953bc1d1125680a313ad7bcee78 Mon Sep 17 00:00:00 2001 From: Jeff McKenna Date: Tue, 20 Sep 2022 10:40:16 -0300 Subject: [PATCH 106/160] add security file to branch-7-6 --- SECURITY.md | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000..ac7faa18b5 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,58 @@ +# MapServer Security Policy + +## Reporting a Vulnerability in MapServer + +Security/vulnerability reports should not be submitted through GitHub tickets or the public mailing lists, but instead please send your report +to the email address: **mapserver-security nospam @ osgeo.org** (remove the blanks and ‘nospam’). + +Please follow the general guidelines for bug +submissions, when describing the vulnerability (see https://mapserver.org/development/bugs.html). + +## Supported Versions + +The MapServer PSC (Project Steering Committee) will release patches for security vulnerabilities +for the last release branch of the **two most recent release series** (such as 8.x, 7.x. 6.x, etc...). +Patches will only be provided **for a period of three years** from the release date of the current series. +For example, as 8.0 has been released, now only 8.0.x and 7.6.x will be supported/patched and 7.6.x will +only be supported for three years from the date of the 8.0 series release. + +Currently, the following versions are supported: + +| Version | Supported | +| ------- | ------------------ | +| 8.0.x | :white_check_mark: | +| 7.6.x | :white_check_mark: | +| 7.4.x | :x: | +| 7.2.x | :x: | +| 7.0.x | :x: | +| 6.4.x | :x: | +| < 6.4 | :x: | + +Note: _MapServer 8.0.0 was released on 2022-09-12._ +Note: _MapServer 7.0.0 was released on 2015-07-24._ + +## Version Numbering: Explained + +version x.y.z means: + +**x** +- Major release series number. +- Major releases indicate substantial changes to the software and + backwards compatibility is not guaranteed across series. Current + release series is 8. + +**y** +- Minor release series number. +- Minor releases indicate smaller, functional additions or improvements + to the software and should be generally backwards compatible within a + major release series. Users should be able to confidently upgrade + from one minor release to another within the same release series, so + from 7.4.x to 7.6.x. + +**z** +- Point release series number. +- Point releases indicate maintenance releases - usually a combination of + bug and security fixes and perhaps small feature additions. Backwards + compatibility should be preserved and users should be able to confidently + upgrade between point releases within the same release series, + so from 7.6.4 to 7.6.5. From 8c6df8a0819b507cef75d34d11dd54fc0d9c0a66 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 20 Sep 2022 19:08:03 +0200 Subject: [PATCH 107/160] Redact password= content in msError() and msDebug() messages (#6621) --- mapdebug.c | 20 +++++++++----------- maperror.c | 14 ++++++++++++++ maperror.h | 1 + mappostgis.c | 14 +------------- 4 files changed, 25 insertions(+), 24 deletions(-) diff --git a/mapdebug.c b/mapdebug.c index 2043a99502..2af1d5e8a3 100644 --- a/mapdebug.c +++ b/mapdebug.c @@ -324,10 +324,18 @@ void msDebug( const char * pszFormat, ... ) { va_list args; debugInfoObj *debuginfo = msGetDebugInfoObj(); + char szMessage[MESSAGELENGTH]; if (debuginfo == NULL || debuginfo->debug_mode == MS_DEBUGMODE_OFF) return; /* Don't waste time here! */ + va_start(args, pszFormat); + vsnprintf( szMessage, MESSAGELENGTH, pszFormat, args ); + va_end(args); + szMessage[MESSAGELENGTH-1] = '\0'; + + msRedactCredentials(szMessage); + if (debuginfo->fp) { /* Writing to a stdio file handle */ @@ -345,21 +353,11 @@ void msDebug( const char * pszFormat, ... ) msStringChop(ctime(&t)), (long)tv.tv_usec); } - va_start(args, pszFormat); - msIO_vfprintf(debuginfo->fp, pszFormat, args); - va_end(args); + msIO_fprintf(debuginfo->fp, "%s", szMessage); } #ifdef _WIN32 else if (debuginfo->debug_mode == MS_DEBUGMODE_WINDOWSDEBUG) { /* Writing to Windows Debug Console */ - - char szMessage[MESSAGELENGTH]; - - va_start(args, pszFormat); - vsnprintf( szMessage, MESSAGELENGTH, pszFormat, args ); - va_end(args); - - szMessage[MESSAGELENGTH-1] = '\0'; OutputDebugStringA(szMessage); } #endif diff --git a/maperror.c b/maperror.c index b3d7cbbdb3..8204d61b09 100644 --- a/maperror.c +++ b/maperror.c @@ -324,6 +324,18 @@ char *msGetErrorString(char *delimiter) return(errstr); } +void msRedactCredentials(char* str) +{ + char* password = strstr(str, "password="); + if (password != NULL) { + char* ptr = password + strlen("password="); + while (*ptr != '\0' && *ptr != ' ') { + *ptr = '*'; + ptr++; + } + } +} + void msSetError(int code, const char *message_fmt, const char *routine, ...) { errorObj *ms_error; @@ -356,6 +368,8 @@ void msSetError(int code, const char *message_fmt, const char *routine, ...) else ++ms_error->errorcount; + msRedactCredentials(ms_error->message); + /* Log a copy of errors to MS_ERRORFILE if set (handled automatically inside msDebug()) */ msDebug("%s: %s %s\n", ms_error->routine, ms_errorCodes[ms_error->code], ms_error->message); diff --git a/maperror.h b/maperror.h index 9921776892..bbda4e2da6 100644 --- a/maperror.h +++ b/maperror.h @@ -125,6 +125,7 @@ extern "C" { MS_DLL_EXPORT char *msGetErrorString(char *delimiter); #ifndef SWIG + MS_DLL_EXPORT void msRedactCredentials(char* str); MS_DLL_EXPORT void msSetError(int code, const char *message, const char *routine, ...) MS_PRINT_FUNC_FORMAT(2,4) ; MS_DLL_EXPORT void msWriteError(FILE *stream); MS_DLL_EXPORT void msWriteErrorXML(FILE *stream); diff --git a/mappostgis.c b/mappostgis.c index 56e404a190..fee3d8c494 100644 --- a/mappostgis.c +++ b/mappostgis.c @@ -2598,25 +2598,13 @@ int msPostGISLayerOpen(layerObj *layer) ** Connection failed, return error message with passwords ***ed out. */ if (!layerinfo->pgconn || PQstatus(layerinfo->pgconn) == CONNECTION_BAD) { - char *index, *maskeddata; if (layer->debug) msDebug("msPostGISLayerOpen: Connection failure.\n"); - maskeddata = msStrdup(layer->connection); - index = strstr(maskeddata, "password="); - if (index != NULL) { - index = (char*)(index + 9); - while (*index != '\0' && *index != ' ') { - *index = '*'; - index++; - } - } - - msDebug( "Database connection failed (%s) with connect string '%s'\nIs the database running? Is it allowing connections? Does the specified user exist? Is the password valid? Is the database on the standard port? in msPostGISLayerOpen()", PQerrorMessage(layerinfo->pgconn), maskeddata); + msDebug( "Database connection failed (%s) with connect string '%s'\nIs the database running? Is it allowing connections? Does the specified user exist? Is the password valid? Is the database on the standard port? in msPostGISLayerOpen()", PQerrorMessage(layerinfo->pgconn), layer->connection); msSetError(MS_QUERYERR, "Database connection failed. Check server logs for more details.Is the database running? Is it allowing connections? Does the specified user exist? Is the password valid? Is the database on the standard port?", "msPostGISLayerOpen()"); if(layerinfo->pgconn) PQfinish(layerinfo->pgconn); - free(maskeddata); free(layerinfo); return MS_FAILURE; } From 2c763f4505c419215a0df1bfe983ead76a738d48 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sun, 2 Oct 2022 21:11:25 +0200 Subject: [PATCH 108/160] loadProjection(): avoid write heap-bufer-overflow on invalid PROJECTION block Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=52066 --- mapfile.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mapfile.c b/mapfile.c index ec0f54baad..9f4abac3d8 100644 --- a/mapfile.c +++ b/mapfile.c @@ -1172,6 +1172,12 @@ static int loadProjection(projectionObj *p) break; case(MS_STRING): case(MS_AUTO): + if( i == MS_MAXPROJARGS ) { + msSetError(MS_MISCERR, "Parsing error near (%s):(line %d): Too many arguments in projection string", "loadProjection()", + msyystring_buffer, msyylineno); + p->numargs = i; + return -1; + } p->args[i] = msStrdup(msyystring_buffer); p->automatic = MS_TRUE; i++; From 3e573675072e91bbeee470fb8ce5f255e6b5869a Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 4 Oct 2022 01:23:58 +0200 Subject: [PATCH 109/160] Fix memory leak related to styles on invalid mapfile Found locally with ossfuzz ``` Direct leak of 1304 byte(s) in 1 object(s) allocated from: #0 0x54de9e in __interceptor_calloc /src/llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:77:3 #1 0x5c6ce8 in msGrowClassStyles /src/MapServer/mapfile.c:3020:48 #2 0x5c8513 in loadClass /src/MapServer/mapfile.c:3262:12 #3 0x5d0f1e in loadLayer /src/MapServer/mapfile.c:3968:12 #4 0x5ec0a0 in loadMapInternal /src/MapServer/mapfile.c:6053:12 #5 0x5ef850 in msLoadMap /src/MapServer/mapfile.c:6333:6 #6 0x58b1df in LLVMFuzzerTestOneInput /src/MapServer/build/../fuzzers/mapfuzzer.c:50:13 #7 0x45cb33 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:611:15 #8 0x45c31a in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool, bool*) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:514:3 #9 0x45d9e9 in fuzzer::Fuzzer::MutateAndTestOne() /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:757:19 #10 0x45e6b5 in fuzzer::Fuzzer::Loop(std::__Fuzzer::vector >&) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:895:5 #11 0x44da1f in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:912:6 #12 0x477072 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10 #13 0x7f8124cb8082 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x24082) (BuildId: 1878e6b475720c7c51969e69ab2d276fae6d1dee) ``` --- mapfile.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mapfile.c b/mapfile.c index 9f4abac3d8..e41ca5089f 100644 --- a/mapfile.c +++ b/mapfile.c @@ -2946,6 +2946,11 @@ int freeClass(classObj *class) } } } + if( class->numstyles == 0 && class->styles != NULL && + class->styles[0] != NULL ) { + /* msGrowClassStyles() creates class->styles[0] during the first call */ + msFree(class->styles[0]); + } msFree(class->styles); for(i=0; inumlabels; i++) { /* each label */ From 8a0b2cd40d35daecb5c35ed7ab7330174214ce44 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 5 Oct 2022 07:28:41 -0300 Subject: [PATCH 110/160] loadHashTable(): fix memory leak in error code path (#6661) Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=52111 --- mapfile.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/mapfile.c b/mapfile.c index e41ca5089f..12f6eebf14 100644 --- a/mapfile.c +++ b/mapfile.c @@ -2171,7 +2171,6 @@ static void writeExpression(FILE *stream, int indent, const char *name, expressi int loadHashTable(hashTableObj *ptable) { - char *key=NULL, *data=NULL; assert(ptable); for(;;) { @@ -2182,14 +2181,19 @@ int loadHashTable(hashTableObj *ptable) case(END): return(MS_SUCCESS); case(MS_STRING): - key = msStrdup(msyystring_buffer); /* the key is *always* a string */ - if(getString(&data) == MS_FAILURE) return(MS_FAILURE); + { + char* data = NULL; + char* key = msStrdup(msyystring_buffer); /* the key is *always* a string */ + if(getString(&data) == MS_FAILURE) { + free(key); + return(MS_FAILURE); + } msInsertHashTable(ptable, key, data); free(key); free(data); - data=NULL; break; + } default: msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)", "loadHashTable()", msyystring_buffer, msyylineno ); return(MS_FAILURE); From 7f2d651b23e148000c3149551fc8beaf7af399b1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 5 Oct 2022 07:29:26 -0300 Subject: [PATCH 111/160] msLoadMap(): fix double-free related to labels (#6659) Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=52102 --- mapfile.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mapfile.c b/mapfile.c index 12f6eebf14..a1e712f6c8 100644 --- a/mapfile.c +++ b/mapfile.c @@ -1594,7 +1594,6 @@ static int loadLabel(labelObj *label) break; case(EOF): msSetError(MS_EOFERR, NULL, "loadLabel()"); - freeLabel(label); /* free any structures allocated before EOF */ return(-1); case(EXPRESSION): if(loadExpression(&(label->expression)) == -1) return(-1); /* loadExpression() cleans up previously allocated expression */ @@ -3239,7 +3238,9 @@ int loadClass(classObj *class, layerObj *layer) initLabel(class->labels[class->numlabels]); class->labels[class->numlabels]->size = MS_MEDIUM; /* only set a default if the LABEL section is present */ if(loadLabel(class->labels[class->numlabels]) == -1) { - msFree(class->labels[class->numlabels]); + freeLabel(class->labels[class->numlabels]); + free(class->labels[class->numlabels]); + class->labels[class->numlabels] = NULL; return(-1); } class->numlabels++; From f071dca3ae64b01b5109144c8252696807a7cef8 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 4 Oct 2022 19:24:01 +0200 Subject: [PATCH 112/160] loadClass(): better fix for class->styles[] mem-leak Improved fix of https://github.com/MapServer/MapServer/pull/6651 Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=52107 --- mapfile.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mapfile.c b/mapfile.c index a1e712f6c8..289d765acb 100644 --- a/mapfile.c +++ b/mapfile.c @@ -2949,11 +2949,6 @@ int freeClass(classObj *class) } } } - if( class->numstyles == 0 && class->styles != NULL && - class->styles[0] != NULL ) { - /* msGrowClassStyles() creates class->styles[0] during the first call */ - msFree(class->styles[0]); - } msFree(class->styles); for(i=0; inumlabels; i++) { /* each label */ @@ -3276,7 +3271,12 @@ int loadClass(classObj *class, layerObj *layer) if(msGrowClassStyles(class) == NULL) return(-1); initStyle(class->styles[class->numstyles]); - if(loadStyle(class->styles[class->numstyles]) != MS_SUCCESS) return(-1); + if(loadStyle(class->styles[class->numstyles]) != MS_SUCCESS) { + freeStyle(class->styles[class->numstyles]); + free(class->styles[class->numstyles]); + class->styles[class->numstyles] = NULL; + return(-1); + } class->numstyles++; break; case(TEMPLATE): From 67ec093541c15cec2ffd1c7e94ca03d26cbec03e Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 4 Oct 2022 19:29:37 +0200 Subject: [PATCH 113/160] mapfile.c: fix very likely memory leaks in error code paths after call to msGrowXXXX() functions --- mapfile.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/mapfile.c b/mapfile.c index 289d765acb..d46f026dad 100644 --- a/mapfile.c +++ b/mapfile.c @@ -3098,6 +3098,9 @@ int msMaybeAllocateClassStyle(classObj* c, int idx) if ( initStyle(c->styles[c->numstyles]) == MS_FAILURE ) { msSetError(MS_MISCERR, "Failed to init new styleObj", "msMaybeAllocateClassStyle()"); + freeStyle(c->styles[c->numstyles]); + free(c->styles[c->numstyles]); + c->styles[c->numstyles] = NULL; return(MS_FAILURE); } c->numstyles++; @@ -4101,7 +4104,13 @@ int loadLayer(layerObj *layer, mapObj *map) if (msGrowLayerClasses(layer) == NULL) return(-1); initClass(layer->class[layer->numclasses]); - if(loadClass(layer->class[layer->numclasses], layer) == -1) return(-1); + if(loadClass(layer->class[layer->numclasses], layer) == -1) + { + freeClass(layer->class[layer->numclasses]); + free(layer->class[layer->numclasses]); + layer->class[layer->numclasses] = NULL; + return(-1); + } layer->numclasses++; break; case(CLUSTER): @@ -4454,7 +4463,10 @@ int loadLayer(layerObj *layer, mapObj *map) if (msGrowLayerScaletokens(layer) == NULL) return(-1); initScaleToken(&layer->scaletokens[layer->numscaletokens]); - if(loadScaletoken(&layer->scaletokens[layer->numscaletokens], layer) == -1) return(-1); + if(loadScaletoken(&layer->scaletokens[layer->numscaletokens], layer) == -1) { + freeScaleToken(&layer->scaletokens[layer->numscaletokens]); + return(-1); + } layer->numscaletokens++; break; case(SIZEUNITS): From 5152f14b31f37cf2e7c1fa76af6d4cf61d53b332 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 5 Oct 2022 22:05:28 +0200 Subject: [PATCH 114/160] msLoadMap(): fix memleak in error code path related to style loading Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=52124 --- mapfile.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mapfile.c b/mapfile.c index d46f026dad..4dd85210d9 100644 --- a/mapfile.c +++ b/mapfile.c @@ -1789,7 +1789,12 @@ static int loadLabel(labelObj *label) if(msGrowLabelStyles(label) == NULL) return(-1); initStyle(label->styles[label->numstyles]); - if(loadStyle(label->styles[label->numstyles]) != MS_SUCCESS) return(-1); + if(loadStyle(label->styles[label->numstyles]) != MS_SUCCESS) { + freeStyle(label->styles[label->numstyles]); + free(label->styles[label->numstyles]); + label->styles[label->numstyles] = NULL; + return(-1); + } if(label->styles[label->numstyles]->_geomtransform.type == MS_GEOMTRANSFORM_NONE) label->styles[label->numstyles]->_geomtransform.type = MS_GEOMTRANSFORM_LABELPOINT; /* set a default, a marker? */ label->numstyles++; From 4c423d3e52780616c11783bd636ee037ba1827ff Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 5 Oct 2022 22:23:58 +0200 Subject: [PATCH 115/160] msLoadFontSet(): fix null pointer dereference Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=52131 --- maplabel.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/maplabel.c b/maplabel.c index 33a5146b06..c8ccf45bbc 100644 --- a/maplabel.c +++ b/maplabel.c @@ -792,6 +792,7 @@ int msLoadFontSet(fontSetObj *fontset, mapObj *map) char szPath[MS_MAXPATHLEN]; int i; int bFullPath = 0; + const char* realpath; if(fontset->numfonts != 0) /* already initialized */ return(0); @@ -809,7 +810,12 @@ int msLoadFontSet(fontSetObj *fontset, mapObj *map) /* return(-1); */ /* } */ - stream = VSIFOpenL( msBuildPath(szPath, fontset->map->mappath, fontset->filename), "rb"); + realpath = msBuildPath(szPath, fontset->map->mappath, fontset->filename); + if( !realpath ) { + free(path); + return -1; + } + stream = VSIFOpenL( realpath, "rb"); if(!stream) { msSetError(MS_IOERR, "Error opening fontset %s.", "msLoadFontset()", fontset->filename); From 8bcd196175499d88d6d26353b2b8f8a6a234ddf9 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 5 Oct 2022 22:34:27 +0200 Subject: [PATCH 116/160] msLoadMap(): fix nullptr dereference when using LATLON keyword As far as I can see, this was broken in the nominal case since https://github.com/MapServer/MapServer/commit/f595e91f1b418db72b806162dba4470109ac8dc1 So this feature is not used in msautotest, nor documented (https://mapserver.org/search.html?q=LATLON doesn't return anything relevant) Probably that the mapObj::latlon member would be better initialized by taking the geographic CRS of the mapObj::projection. --- mapfile.c | 1 - 1 file changed, 1 deletion(-) diff --git a/mapfile.c b/mapfile.c index d46f026dad..1420aa0d35 100644 --- a/mapfile.c +++ b/mapfile.c @@ -6383,7 +6383,6 @@ static int loadMapInternal(mapObj *map) if((map->interlace = getSymbol(2, MS_ON,MS_OFF)) == -1) return MS_FAILURE; break; case(LATLON): - msFreeProjectionExceptContext(&map->latlon); if(loadProjection(&map->latlon) == -1) return MS_FAILURE; break; case(LAYER): From 98944a3495a599afc624a208bb7eb9907028f28c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 6 Oct 2022 16:24:25 -0300 Subject: [PATCH 117/160] mapshape.c: avoid 'undefined-shift' issue with SWAP_FOUR_BYTES() macro (#6671) --- mapshape.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mapshape.c b/mapshape.c index 0524c56f69..c15b378c7c 100644 --- a/mapshape.c +++ b/mapshape.c @@ -43,9 +43,7 @@ #include /* Only use this macro on 32-bit integers! */ -#define SWAP_FOUR_BYTES(data) \ - ( ((data >> 24) & 0x000000FF) | ((data >> 8) & 0x0000FF00) | \ - ((data << 8) & 0x00FF0000) | ((data << 24) & 0xFF000000) ) +#define SWAP_FOUR_BYTES(data) CPL_SWAP32(data) #define ByteCopy( a, b, c ) memcpy( b, a, c ) From 575ca3408117f4129d91da981411256b6989a84d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 6 Oct 2022 16:24:50 -0300 Subject: [PATCH 118/160] msLoadMap(): fix memleak in error code path related to symbol loading (#6672) --- mapfile.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mapfile.c b/mapfile.c index d46f026dad..1e2c68bf68 100644 --- a/mapfile.c +++ b/mapfile.c @@ -6448,7 +6448,12 @@ static int loadMapInternal(mapObj *map) case(SYMBOL): if(msGrowSymbolSet(&(map->symbolset)) == NULL) return MS_FAILURE; - if((loadSymbol(map->symbolset.symbol[map->symbolset.numsymbols], map->mappath) == -1)) return MS_FAILURE; + if((loadSymbol(map->symbolset.symbol[map->symbolset.numsymbols], map->mappath) == -1)) { + msFreeSymbol(map->symbolset.symbol[map->symbolset.numsymbols]); + free(map->symbolset.symbol[map->symbolset.numsymbols]); + map->symbolset.symbol[map->symbolset.numsymbols] = NULL; + return MS_FAILURE; + } map->symbolset.symbol[map->symbolset.numsymbols]->inmapfile = MS_TRUE; map->symbolset.numsymbols++; break; From e6afa64c788b2da2961ae62981bdc611dd2bb1cc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 6 Oct 2022 16:26:08 -0300 Subject: [PATCH 119/160] msLoadFontSet(): fix memleak in error code path (#6675) Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=52123 --- maplabel.c | 1 + 1 file changed, 1 insertion(+) diff --git a/maplabel.c b/maplabel.c index 33a5146b06..9499d56252 100644 --- a/maplabel.c +++ b/maplabel.c @@ -813,6 +813,7 @@ int msLoadFontSet(fontSetObj *fontset, mapObj *map) if(!stream) { msSetError(MS_IOERR, "Error opening fontset %s.", "msLoadFontset()", fontset->filename); + free(path); return(-1); } From da428d335a5bf6e4678af42f621f2a0db6f7e38b Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Fri, 7 Oct 2022 00:11:10 +0200 Subject: [PATCH 120/160] _msProcessAutoProjection(): fix memleak in error code path Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=52158 --- mapproject.c | 1 + 1 file changed, 1 insertion(+) diff --git a/mapproject.c b/mapproject.c index b8f7f568f3..1bbd90cb1c 100644 --- a/mapproject.c +++ b/mapproject.c @@ -731,6 +731,7 @@ static int _msProcessAutoProjection(projectionObj *p) "WMS/WFS AUTO/AUTO2 PROJECTION must be in the format " "'AUTO:proj_id,units_id,lon0,lat0' or 'AUTO2:crs_id,factor,lon0,lat0'(got '%s').\n", "_msProcessAutoProjection()", p->args[0]); + msFreeCharArray(args, numargs); return -1; } From d944923c8e415866041e71d371ffc571329ce785 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Fri, 7 Oct 2022 16:37:38 +0200 Subject: [PATCH 121/160] maplexer.l: avoid non-null terminated msyystring_buffer that can cause read heap-buffer-overflow Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=52175 --- maplexer.c | 1609 +++++++++++++++++++++++++++++----------------------- maplexer.l | 96 ++-- 2 files changed, 954 insertions(+), 751 deletions(-) diff --git a/maplexer.c b/maplexer.c index 060c1801c2..7ee518baac 100644 --- a/maplexer.c +++ b/maplexer.c @@ -8,11 +8,17 @@ #define yy_create_buffer msyy_create_buffer #define yy_delete_buffer msyy_delete_buffer -#define yy_flex_debug msyy_flex_debug +#define yy_scan_buffer msyy_scan_buffer +#define yy_scan_string msyy_scan_string +#define yy_scan_bytes msyy_scan_bytes #define yy_init_buffer msyy_init_buffer #define yy_flush_buffer msyy_flush_buffer #define yy_load_buffer_state msyy_load_buffer_state #define yy_switch_to_buffer msyy_switch_to_buffer +#define yypush_buffer_state msyypush_buffer_state +#define yypop_buffer_state msyypop_buffer_state +#define yyensure_buffer_stack msyyensure_buffer_stack +#define yy_flex_debug msyy_flex_debug #define yyin msyyin #define yyleng msyyleng #define yylex msyylex @@ -28,11 +34,245 @@ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 6 -#define YY_FLEX_SUBMINOR_VERSION 0 +#define YY_FLEX_SUBMINOR_VERSION 4 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif +#ifdef yy_create_buffer +#define msyy_create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer msyy_create_buffer +#endif + +#ifdef yy_delete_buffer +#define msyy_delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer msyy_delete_buffer +#endif + +#ifdef yy_scan_buffer +#define msyy_scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer msyy_scan_buffer +#endif + +#ifdef yy_scan_string +#define msyy_scan_string_ALREADY_DEFINED +#else +#define yy_scan_string msyy_scan_string +#endif + +#ifdef yy_scan_bytes +#define msyy_scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes msyy_scan_bytes +#endif + +#ifdef yy_init_buffer +#define msyy_init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer msyy_init_buffer +#endif + +#ifdef yy_flush_buffer +#define msyy_flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer msyy_flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define msyy_load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state msyy_load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define msyy_switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer msyy_switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define msyypush_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state msyypush_buffer_state +#endif + +#ifdef yypop_buffer_state +#define msyypop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state msyypop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define msyyensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack msyyensure_buffer_stack +#endif + +#ifdef yylex +#define msyylex_ALREADY_DEFINED +#else +#define yylex msyylex +#endif + +#ifdef yyrestart +#define msyyrestart_ALREADY_DEFINED +#else +#define yyrestart msyyrestart +#endif + +#ifdef yylex_init +#define msyylex_init_ALREADY_DEFINED +#else +#define yylex_init msyylex_init +#endif + +#ifdef yylex_init_extra +#define msyylex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra msyylex_init_extra +#endif + +#ifdef yylex_destroy +#define msyylex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy msyylex_destroy +#endif + +#ifdef yyget_debug +#define msyyget_debug_ALREADY_DEFINED +#else +#define yyget_debug msyyget_debug +#endif + +#ifdef yyset_debug +#define msyyset_debug_ALREADY_DEFINED +#else +#define yyset_debug msyyset_debug +#endif + +#ifdef yyget_extra +#define msyyget_extra_ALREADY_DEFINED +#else +#define yyget_extra msyyget_extra +#endif + +#ifdef yyset_extra +#define msyyset_extra_ALREADY_DEFINED +#else +#define yyset_extra msyyset_extra +#endif + +#ifdef yyget_in +#define msyyget_in_ALREADY_DEFINED +#else +#define yyget_in msyyget_in +#endif + +#ifdef yyset_in +#define msyyset_in_ALREADY_DEFINED +#else +#define yyset_in msyyset_in +#endif + +#ifdef yyget_out +#define msyyget_out_ALREADY_DEFINED +#else +#define yyget_out msyyget_out +#endif + +#ifdef yyset_out +#define msyyset_out_ALREADY_DEFINED +#else +#define yyset_out msyyset_out +#endif + +#ifdef yyget_leng +#define msyyget_leng_ALREADY_DEFINED +#else +#define yyget_leng msyyget_leng +#endif + +#ifdef yyget_text +#define msyyget_text_ALREADY_DEFINED +#else +#define yyget_text msyyget_text +#endif + +#ifdef yyget_lineno +#define msyyget_lineno_ALREADY_DEFINED +#else +#define yyget_lineno msyyget_lineno +#endif + +#ifdef yyset_lineno +#define msyyset_lineno_ALREADY_DEFINED +#else +#define yyset_lineno msyyset_lineno +#endif + +#ifdef yywrap +#define msyywrap_ALREADY_DEFINED +#else +#define yywrap msyywrap +#endif + +#ifdef yyalloc +#define msyyalloc_ALREADY_DEFINED +#else +#define yyalloc msyyalloc +#endif + +#ifdef yyrealloc +#define msyyrealloc_ALREADY_DEFINED +#else +#define yyrealloc msyyrealloc +#endif + +#ifdef yyfree +#define msyyfree_ALREADY_DEFINED +#else +#define yyfree msyyfree +#endif + +#ifdef yytext +#define msyytext_ALREADY_DEFINED +#else +#define yytext msyytext +#endif + +#ifdef yyleng +#define msyyleng_ALREADY_DEFINED +#else +#define yyleng msyyleng +#endif + +#ifdef yyin +#define msyyin_ALREADY_DEFINED +#else +#define yyin msyyin +#endif + +#ifdef yyout +#define msyyout_ALREADY_DEFINED +#else +#define yyout msyyout +#endif + +#ifdef yy_flex_debug +#define msyy_flex_debug_ALREADY_DEFINED +#else +#define yy_flex_debug msyy_flex_debug +#endif + +#ifdef yylineno +#define msyylineno_ALREADY_DEFINED +#else +#define yylineno msyylineno +#endif + /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ @@ -103,60 +343,48 @@ typedef unsigned int flex_uint32_t; #define UINT32_MAX (4294967295U) #endif +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + #endif /* ! C99 */ #endif /* ! FLEXINT_H */ -#ifdef __cplusplus - -/* The "const" storage-class-modifier is valid. */ -#define YY_USE_CONST - -#else /* ! __cplusplus */ - -/* C99 requires __STDC__ to be defined as 1. */ -#if defined (__STDC__) - -#define YY_USE_CONST +/* begin standard C++ headers. */ -#endif /* defined (__STDC__) */ -#endif /* ! __cplusplus */ - -#ifdef YY_USE_CONST +/* TODO: this is always defined, so inline it */ #define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) #else -#define yyconst +#define yynoreturn #endif /* Returned upon end-of-file. */ #define YY_NULL 0 -/* Promotes a possibly negative, possibly signed char to an unsigned - * integer for use as an array index. If the signed char is negative, - * we want to instead treat it as an 8-bit unsigned char, hence the - * double cast. +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. */ -#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) +#define YY_SC_TO_UI(c) ((YY_CHAR) (c)) /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ #define BEGIN (yy_start) = 1 + 2 * - /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ #define YY_START (((yy_start) - 1) / 2) #define YYSTATE YY_START - /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) - /* Special action meaning "start processing a new file". */ -#define YY_NEW_FILE msyyrestart(msyyin ) - +#define YY_NEW_FILE yyrestart( yyin ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ @@ -186,14 +414,14 @@ typedef struct yy_buffer_state *YY_BUFFER_STATE; typedef size_t yy_size_t; #endif -extern yy_size_t msyyleng; +extern int yyleng; -extern FILE *msyyin, *msyyout; +extern FILE *yyin, *yyout; #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 - + #define YY_LESS_LINENO(n) #define YY_LINENO_REWIND_TO(ptr) @@ -201,16 +429,15 @@ extern FILE *msyyin, *msyyout; #define yyless(n) \ do \ { \ - /* Undo effects of setting up msyytext. */ \ + /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = (yy_hold_char); \ YY_RESTORE_YY_MORE_OFFSET \ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ - YY_DO_BEFORE_ACTION; /* set up msyytext again */ \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) - #define unput(c) yyunput( c, (yytext_ptr) ) #ifndef YY_STRUCT_YY_BUFFER_STATE @@ -225,7 +452,7 @@ struct yy_buffer_state /* Size of input buffer in bytes, not including room for EOB * characters. */ - yy_size_t yy_buf_size; + int yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. @@ -253,7 +480,7 @@ struct yy_buffer_state int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ - + /* Whether to try to fill the input buffer when we reach the * end of it. */ @@ -270,8 +497,8 @@ struct yy_buffer_state * possible backing-up. * * When we actually see the EOF, we change the status to "new" - * (via msyyrestart()), so that the user can continue scanning by - * just pointing msyyin at a new input file. + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 @@ -281,7 +508,7 @@ struct yy_buffer_state /* Stack of input buffers. */ static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ -static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ +static YY_BUFFER_STATE * yy_buffer_stack = NULL; /**< Stack as an array. */ /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general @@ -292,109 +519,98 @@ static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ #define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ : NULL) - /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ #define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] -/* yy_hold_char holds the character lost when msyytext is formed. */ +/* yy_hold_char holds the character lost when yytext is formed. */ static char yy_hold_char; static int yy_n_chars; /* number of characters read into yy_ch_buf */ -yy_size_t msyyleng; +int yyleng; /* Points to current character in buffer. */ -static char *yy_c_buf_p = (char *) 0; +static char *yy_c_buf_p = NULL; static int yy_init = 0; /* whether we need to initialize */ static int yy_start = 0; /* start state number */ -/* Flag which is used to allow msyywrap()'s to do buffer switches - * instead of setting up a fresh msyyin. A bit of a hack ... +/* Flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... */ static int yy_did_buffer_switch_on_eof; -void msyyrestart (FILE *input_file ); -void msyy_switch_to_buffer (YY_BUFFER_STATE new_buffer ); -YY_BUFFER_STATE msyy_create_buffer (FILE *file,int size ); -void msyy_delete_buffer (YY_BUFFER_STATE b ); -void msyy_flush_buffer (YY_BUFFER_STATE b ); -void msyypush_buffer_state (YY_BUFFER_STATE new_buffer ); -void msyypop_buffer_state (void ); - -static void msyyensure_buffer_stack (void ); -static void msyy_load_buffer_state (void ); -static void msyy_init_buffer (YY_BUFFER_STATE b,FILE *file ); +void yyrestart ( FILE *input_file ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size ); +void yy_delete_buffer ( YY_BUFFER_STATE b ); +void yy_flush_buffer ( YY_BUFFER_STATE b ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer ); +void yypop_buffer_state ( void ); -#define YY_FLUSH_BUFFER msyy_flush_buffer(YY_CURRENT_BUFFER ) +static void yyensure_buffer_stack ( void ); +static void yy_load_buffer_state ( void ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER ) -YY_BUFFER_STATE msyy_scan_buffer (char *base,yy_size_t size ); -YY_BUFFER_STATE msyy_scan_string (yyconst char *yy_str ); -YY_BUFFER_STATE msyy_scan_bytes (yyconst char *bytes,yy_size_t len ); +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len ); -void *msyyalloc (yy_size_t ); -void *msyyrealloc (void *,yy_size_t ); -void msyyfree (void * ); - -#define yy_new_buffer msyy_create_buffer +void *yyalloc ( yy_size_t ); +void *yyrealloc ( void *, yy_size_t ); +void yyfree ( void * ); +#define yy_new_buffer yy_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ - msyyensure_buffer_stack (); \ + yyensure_buffer_stack (); \ YY_CURRENT_BUFFER_LVALUE = \ - msyy_create_buffer(msyyin,YY_BUF_SIZE ); \ + yy_create_buffer( yyin, YY_BUF_SIZE ); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } - #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ - msyyensure_buffer_stack (); \ + yyensure_buffer_stack (); \ YY_CURRENT_BUFFER_LVALUE = \ - msyy_create_buffer(msyyin,YY_BUF_SIZE ); \ + yy_create_buffer( yyin, YY_BUF_SIZE ); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } - #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) /* Begin user sect3 */ +typedef flex_uint8_t YY_CHAR; -typedef unsigned char YY_CHAR; - -FILE *msyyin = (FILE *) 0, *msyyout = (FILE *) 0; +FILE *yyin = NULL, *yyout = NULL; typedef int yy_state_type; -extern int msyylineno; - -int msyylineno = 1; +extern int yylineno; +int yylineno = 1; -extern char *msyytext; +extern char *yytext; #ifdef yytext_ptr #undef yytext_ptr #endif -#define yytext_ptr msyytext +#define yytext_ptr yytext -static yy_state_type yy_get_previous_state (void ); -static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); -static int yy_get_next_buffer (void ); -#if defined(__GNUC__) && __GNUC__ >= 3 -__attribute__((__noreturn__)) -#endif -static void yy_fatal_error (yyconst char msg[] ); +static yy_state_type yy_get_previous_state ( void ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state ); +static int yy_get_next_buffer ( void ); +static void yynoreturn yy_fatal_error ( const char* msg ); /* Done after the current pattern has been matched and before the - * corresponding action - sets up msyytext. + * corresponding action - sets up yytext. */ #define YY_DO_BEFORE_ACTION \ (yytext_ptr) = yy_bp; \ - msyyleng = (size_t) (yy_cp - yy_bp); \ + yyleng = (int) (yy_cp - yy_bp); \ (yy_hold_char) = *yy_cp; \ *yy_cp = '\0'; \ (yy_c_buf_p) = yy_cp; - #define YY_NUM_RULES 352 #define YY_END_OF_BUFFER 353 /* This struct is not used in this scanner, @@ -404,7 +620,7 @@ struct yy_trans_info flex_int32_t yy_verify; flex_int32_t yy_nxt; }; -static yyconst flex_int16_t yy_accept[2038] = +static const flex_int16_t yy_accept[2038] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 353, 350, 1, 348, 341, 2, @@ -632,7 +848,7 @@ static yyconst flex_int16_t yy_accept[2038] = 347, 347, 186, 347, 347, 182, 0 } ; -static yyconst YY_CHAR yy_ec[256] = +static const YY_CHAR yy_ec[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, @@ -664,7 +880,7 @@ static yyconst YY_CHAR yy_ec[256] = 1, 1, 1, 1, 1 } ; -static yyconst YY_CHAR yy_meta[89] = +static const YY_CHAR yy_meta[89] = { 0, 1, 1, 2, 1, 3, 1, 4, 1, 1, 5, 1, 1, 6, 1, 7, 8, 8, 8, 8, 3, @@ -677,7 +893,7 @@ static yyconst YY_CHAR yy_meta[89] = 1, 1, 1, 1, 1, 1, 1, 1 } ; -static yyconst flex_uint16_t yy_base[2065] = +static const flex_int16_t yy_base[2065] = { 0, 0, 0, 86, 172, 260, 0, 348, 0, 89, 93, 97, 99, 98, 102, 1126, 4871, 115, 4871, 4871, 0, @@ -908,7 +1124,7 @@ static yyconst flex_uint16_t yy_base[2065] = 4830, 4840, 4850, 4860 } ; -static yyconst flex_int16_t yy_def[2065] = +static const flex_int16_t yy_def[2065] = { 0, 2037, 1, 2038, 2038, 2037, 5, 2037, 7, 2039, 2039, 2040, 2040, 2041, 2041, 2037, 2037, 2037, 2037, 2037, 2042, @@ -1139,7 +1355,7 @@ static yyconst flex_int16_t yy_def[2065] = 2037, 2037, 2037, 2037 } ; -static yyconst flex_uint16_t yy_nxt[4960] = +static const flex_int16_t yy_nxt[4960] = { 0, 16, 17, 18, 17, 17, 16, 19, 20, 16, 19, 21, 16, 16, 16, 22, 23, 24, 25, 25, 16, @@ -1688,7 +1904,7 @@ static yyconst flex_uint16_t yy_nxt[4960] = 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037 } ; -static yyconst flex_int16_t yy_chk[4960] = +static const flex_int16_t yy_chk[4960] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -2240,8 +2456,8 @@ static yyconst flex_int16_t yy_chk[4960] = static yy_state_type yy_last_accepting_state; static char *yy_last_accepting_cpos; -extern int msyy_flex_debug; -int msyy_flex_debug = 0; +extern int yy_flex_debug; +int yy_flex_debug = 0; /* The intent behind this definition is that it'll catch * any uses of REJECT which flex missed. @@ -2250,7 +2466,7 @@ int msyy_flex_debug = 0; #define yymore() yymore_used_but_not_detected #define YY_MORE_ADJ 0 #define YY_RESTORE_YY_MORE_OFFSET -char *msyytext; +char *yytext; #line 1 "maplexer.l" #line 4 "maplexer.l" /* @@ -2298,7 +2514,6 @@ double msyynumber; int msyystate=MS_TOKENIZE_DEFAULT; char *msyystring=NULL; char *msyybasepath=NULL; -char *msyystring_buffer_ptr; int msyystring_buffer_size = 0; int msyystring_size; char msyystring_begin; @@ -2306,22 +2521,21 @@ char *msyystring_buffer = NULL; int msyystring_icase = MS_FALSE; int msyystring_return_state; int msyystring_begin_state; -int msyystring_size_tmp; int msyyreturncomments = 0; -#define MS_LEXER_STRING_REALLOC(string, string_size, max_size, string_ptr) \ - if (string_size >= max_size) { \ - msyystring_size_tmp = max_size; \ - max_size = ((max_size*2) > string_size) ? max_size*2 : string_size+1; \ - string = (char *) msSmallRealloc(string, sizeof(char *) * max_size); \ - string_ptr = string; \ - string_ptr += msyystring_size_tmp; \ - } +#define MS_LEXER_STRING_REALLOC(string, string_size, max_size) \ + do { \ + const int string_size_macro = (int)(string_size); \ + if (string_size_macro >= (int)(max_size)) { \ + max_size = (((int)(max_size)*2) > string_size_macro) ? ((int)(max_size))*2 : string_size_macro+1; \ + string = (char *) msSmallRealloc(string, sizeof(char *) * (max_size)); \ + } \ + } while(0) #define MS_LEXER_RETURN_TOKEN(token) \ MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), \ - msyystring_buffer_size, msyystring_buffer_ptr); \ + msyystring_buffer_size); \ strcpy(msyystring_buffer, msyytext); \ return(token); @@ -2331,13 +2545,9 @@ int include_lineno[MAX_INCLUDE_DEPTH]; int include_stack_ptr = 0; char path[MS_MAXPATHLEN]; +#line 2549 "/home/even/mapserver/mapserver/maplexer.c" - - - - - -#line 2341 "/home/even/mapserver/mapserver/maplexer.c" +#line 2551 "/home/even/mapserver/mapserver/maplexer.c" #define INITIAL 0 #define URL_VARIABLE 1 @@ -2351,36 +2561,36 @@ char path[MS_MAXPATHLEN]; #define YY_EXTRA_TYPE void * #endif -static int yy_init_globals (void ); +static int yy_init_globals ( void ); /* Accessor methods to globals. These are made visible to non-reentrant scanners for convenience. */ -int msyylex_destroy (void ); +int yylex_destroy ( void ); -int msyyget_debug (void ); +int yyget_debug ( void ); -void msyyset_debug (int debug_flag ); +void yyset_debug ( int debug_flag ); -YY_EXTRA_TYPE msyyget_extra (void ); +YY_EXTRA_TYPE yyget_extra ( void ); -void msyyset_extra (YY_EXTRA_TYPE user_defined ); +void yyset_extra ( YY_EXTRA_TYPE user_defined ); -FILE *msyyget_in (void ); +FILE *yyget_in ( void ); -void msyyset_in (FILE * _in_str ); +void yyset_in ( FILE * _in_str ); -FILE *msyyget_out (void ); +FILE *yyget_out ( void ); -void msyyset_out (FILE * _out_str ); +void yyset_out ( FILE * _out_str ); -yy_size_t msyyget_leng (void ); + int yyget_leng ( void ); -char *msyyget_text (void ); +char *yyget_text ( void ); -int msyyget_lineno (void ); +int yyget_lineno ( void ); -void msyyset_lineno (int _line_number ); +void yyset_lineno ( int _line_number ); /* Macros after this point can all be overridden by user definitions in * section 1. @@ -2388,32 +2598,31 @@ void msyyset_lineno (int _line_number ); #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus -extern "C" int msyywrap (void ); +extern "C" int yywrap ( void ); #else -extern int msyywrap (void ); +extern int yywrap ( void ); #endif #endif #ifndef YY_NO_UNPUT - static void yyunput (int c,char *buf_ptr ); + static void yyunput ( int c, char *buf_ptr ); #endif #ifndef yytext_ptr -static void yy_flex_strncpy (char *,yyconst char *,int ); +static void yy_flex_strncpy ( char *, const char *, int ); #endif #ifdef YY_NEED_STRLEN -static int yy_flex_strlen (yyconst char * ); +static int yy_flex_strlen ( const char * ); #endif #ifndef YY_NO_INPUT - #ifdef __cplusplus -static int yyinput (void ); +static int yyinput ( void ); #else -static int input (void ); +static int input ( void ); #endif #endif @@ -2433,7 +2642,7 @@ static int input (void ); /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ -#define ECHO do { if (fwrite( msyytext, msyyleng, 1, msyyout )) {} } while (0) +#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, @@ -2444,20 +2653,20 @@ static int input (void ); if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ int c = '*'; \ - size_t n; \ + int n; \ for ( n = 0; n < max_size && \ - (c = getc( msyyin )) != EOF && c != '\n'; ++n ) \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ if ( c == '\n' ) \ buf[n++] = (char) c; \ - if ( c == EOF && ferror( msyyin ) ) \ + if ( c == EOF && ferror( yyin ) ) \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ result = n; \ } \ else \ { \ errno=0; \ - while ( (result = fread(buf, 1, max_size, msyyin))==0 && ferror(msyyin)) \ + while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \ { \ if( errno != EINTR) \ { \ @@ -2465,7 +2674,7 @@ static int input (void ); break; \ } \ errno=0; \ - clearerr(msyyin); \ + clearerr(yyin); \ } \ }\ \ @@ -2498,12 +2707,12 @@ static int input (void ); #ifndef YY_DECL #define YY_DECL_IS_OURS 1 -extern int msyylex (void); +extern int yylex (void); -#define YY_DECL int msyylex (void) +#define YY_DECL int yylex (void) #endif /* !YY_DECL */ -/* Code executed at the beginning of each rule, after msyytext and msyyleng +/* Code executed at the beginning of each rule, after yytext and yyleng * have been set up. */ #ifndef YY_USER_ACTION @@ -2537,24 +2746,25 @@ YY_DECL if ( ! (yy_start) ) (yy_start) = 1; /* first start state */ - if ( ! msyyin ) - msyyin = stdin; + if ( ! yyin ) + yyin = stdin; - if ( ! msyyout ) - msyyout = stdout; + if ( ! yyout ) + yyout = stdout; if ( ! YY_CURRENT_BUFFER ) { - msyyensure_buffer_stack (); + yyensure_buffer_stack (); YY_CURRENT_BUFFER_LVALUE = - msyy_create_buffer(msyyin,YY_BUF_SIZE ); + yy_create_buffer( yyin, YY_BUF_SIZE ); } - msyy_load_buffer_state( ); + yy_load_buffer_state( ); } { -#line 91 "maplexer.l" +#line 89 "maplexer.l" +#line 91 "maplexer.l" if (msyystring_buffer == NULL) { msyystring_buffer_size = 256; @@ -2629,13 +2839,13 @@ YY_DECL break; } -#line 2633 "/home/even/mapserver/mapserver/maplexer.c" +#line 2843 "/home/even/mapserver/mapserver/maplexer.c" while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ { yy_cp = (yy_c_buf_p); - /* Support of msyytext. */ + /* Support of yytext. */ *yy_cp = (yy_hold_char); /* yy_bp points to the position in yy_ch_buf of the start of @@ -2657,9 +2867,9 @@ YY_DECL { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 2038 ) - yy_c = yy_meta[(unsigned int) yy_c]; + yy_c = yy_meta[yy_c]; } - yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; ++yy_cp; } while ( yy_current_state != 2037 ); @@ -2684,1645 +2894,1645 @@ YY_DECL case 1: YY_RULE_SETUP -#line 166 "maplexer.l" +#line 165 "maplexer.l" ; YY_BREAK case 2: YY_RULE_SETUP -#line 168 "maplexer.l" +#line 167 "maplexer.l" { if (msyyreturncomments) return(MS_COMMENT); } YY_BREAK case 3: YY_RULE_SETUP -#line 170 "maplexer.l" +#line 169 "maplexer.l" { BEGIN(MULTILINE_COMMENT); } YY_BREAK case 4: YY_RULE_SETUP -#line 171 "maplexer.l" +#line 170 "maplexer.l" { BEGIN(INITIAL); } YY_BREAK case 5: YY_RULE_SETUP -#line 172 "maplexer.l" +#line 171 "maplexer.l" ; YY_BREAK case 6: YY_RULE_SETUP -#line 173 "maplexer.l" +#line 172 "maplexer.l" ; YY_BREAK case 7: /* rule 7 can match eol */ YY_RULE_SETUP -#line 174 "maplexer.l" +#line 173 "maplexer.l" { msyylineno++; } YY_BREAK case 8: YY_RULE_SETUP -#line 176 "maplexer.l" +#line 175 "maplexer.l" ; YY_BREAK case 9: YY_RULE_SETUP -#line 178 "maplexer.l" +#line 177 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_LOGICAL_OR); } YY_BREAK case 10: YY_RULE_SETUP -#line 179 "maplexer.l" +#line 178 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_LOGICAL_AND); } YY_BREAK case 11: YY_RULE_SETUP -#line 180 "maplexer.l" +#line 179 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_LOGICAL_NOT); } YY_BREAK case 12: YY_RULE_SETUP -#line 181 "maplexer.l" +#line 180 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_COMPARISON_EQ); } YY_BREAK case 13: YY_RULE_SETUP -#line 182 "maplexer.l" +#line 181 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_COMPARISON_NE); } YY_BREAK case 14: YY_RULE_SETUP -#line 183 "maplexer.l" +#line 182 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_COMPARISON_GT); } YY_BREAK case 15: YY_RULE_SETUP -#line 184 "maplexer.l" +#line 183 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_COMPARISON_LT); } YY_BREAK case 16: YY_RULE_SETUP -#line 185 "maplexer.l" +#line 184 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_COMPARISON_GE); } YY_BREAK case 17: YY_RULE_SETUP -#line 186 "maplexer.l" +#line 185 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_COMPARISON_LE); } YY_BREAK case 18: YY_RULE_SETUP -#line 187 "maplexer.l" +#line 186 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_COMPARISON_RE); } YY_BREAK case 19: YY_RULE_SETUP -#line 189 "maplexer.l" +#line 188 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_COMPARISON_IEQ); } YY_BREAK case 20: YY_RULE_SETUP -#line 190 "maplexer.l" +#line 189 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_COMPARISON_IRE); } YY_BREAK case 21: YY_RULE_SETUP -#line 192 "maplexer.l" +#line 191 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_COMPARISON_IN); /* was IN */ } YY_BREAK case 22: YY_RULE_SETUP -#line 194 "maplexer.l" +#line 193 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_FUNCTION_AREA); } YY_BREAK case 23: YY_RULE_SETUP -#line 195 "maplexer.l" +#line 194 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_FUNCTION_LENGTH); } YY_BREAK case 24: YY_RULE_SETUP -#line 196 "maplexer.l" +#line 195 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_FUNCTION_TOSTRING); } YY_BREAK case 25: YY_RULE_SETUP -#line 197 "maplexer.l" +#line 196 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_FUNCTION_COMMIFY); } YY_BREAK case 26: YY_RULE_SETUP -#line 198 "maplexer.l" +#line 197 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_FUNCTION_ROUND); } YY_BREAK case 27: YY_RULE_SETUP -#line 199 "maplexer.l" +#line 198 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_FUNCTION_UPPER); } YY_BREAK case 28: YY_RULE_SETUP -#line 200 "maplexer.l" +#line 199 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_FUNCTION_LOWER); } YY_BREAK case 29: YY_RULE_SETUP -#line 201 "maplexer.l" +#line 200 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_FUNCTION_INITCAP); } YY_BREAK case 30: YY_RULE_SETUP -#line 202 "maplexer.l" +#line 201 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_FUNCTION_FIRSTCAP); } YY_BREAK case 31: YY_RULE_SETUP -#line 204 "maplexer.l" +#line 203 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_FUNCTION_BUFFER); } YY_BREAK case 32: YY_RULE_SETUP -#line 205 "maplexer.l" +#line 204 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_FUNCTION_DIFFERENCE); } YY_BREAK case 33: YY_RULE_SETUP -#line 206 "maplexer.l" +#line 205 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_FUNCTION_SIMPLIFY); } YY_BREAK case 34: YY_RULE_SETUP -#line 207 "maplexer.l" +#line 206 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_FUNCTION_SIMPLIFYPT); } YY_BREAK case 35: YY_RULE_SETUP -#line 208 "maplexer.l" +#line 207 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_FUNCTION_GENERALIZE); } YY_BREAK case 36: YY_RULE_SETUP -#line 209 "maplexer.l" +#line 208 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_FUNCTION_SMOOTHSIA); } YY_BREAK case 37: YY_RULE_SETUP -#line 210 "maplexer.l" +#line 209 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_FUNCTION_JAVASCRIPT); } YY_BREAK case 38: YY_RULE_SETUP -#line 212 "maplexer.l" +#line 211 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_COMPARISON_INTERSECTS); } YY_BREAK case 39: YY_RULE_SETUP -#line 213 "maplexer.l" +#line 212 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_COMPARISON_DISJOINT); } YY_BREAK case 40: YY_RULE_SETUP -#line 214 "maplexer.l" +#line 213 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_COMPARISON_TOUCHES); } YY_BREAK case 41: YY_RULE_SETUP -#line 215 "maplexer.l" +#line 214 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_COMPARISON_OVERLAPS); } YY_BREAK case 42: YY_RULE_SETUP -#line 216 "maplexer.l" +#line 215 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_COMPARISON_CROSSES); } YY_BREAK case 43: YY_RULE_SETUP -#line 217 "maplexer.l" +#line 216 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_COMPARISON_WITHIN); } YY_BREAK case 44: YY_RULE_SETUP -#line 218 "maplexer.l" +#line 217 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_COMPARISON_CONTAINS); } YY_BREAK case 45: YY_RULE_SETUP -#line 219 "maplexer.l" +#line 218 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_COMPARISON_EQUALS); } YY_BREAK case 46: YY_RULE_SETUP -#line 220 "maplexer.l" +#line 219 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_COMPARISON_BEYOND); } YY_BREAK case 47: YY_RULE_SETUP -#line 221 "maplexer.l" +#line 220 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_COMPARISON_DWITHIN); } YY_BREAK case 48: YY_RULE_SETUP -#line 223 "maplexer.l" +#line 222 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TOKEN_FUNCTION_FROMTEXT); } YY_BREAK case 49: YY_RULE_SETUP -#line 225 "maplexer.l" +#line 224 "maplexer.l" { msyynumber=MS_TRUE; return(MS_TOKEN_LITERAL_BOOLEAN); } YY_BREAK case 50: YY_RULE_SETUP -#line 226 "maplexer.l" +#line 225 "maplexer.l" { msyynumber=MS_FALSE; return(MS_TOKEN_LITERAL_BOOLEAN); } YY_BREAK case 51: YY_RULE_SETUP -#line 228 "maplexer.l" +#line 227 "maplexer.l" { MS_LEXER_RETURN_TOKEN(COLORRANGE); } YY_BREAK case 52: YY_RULE_SETUP -#line 229 "maplexer.l" +#line 228 "maplexer.l" { MS_LEXER_RETURN_TOKEN(DATARANGE); } YY_BREAK case 53: YY_RULE_SETUP -#line 230 "maplexer.l" +#line 229 "maplexer.l" { MS_LEXER_RETURN_TOKEN(RANGEITEM); } YY_BREAK case 54: YY_RULE_SETUP -#line 232 "maplexer.l" +#line 231 "maplexer.l" { MS_LEXER_RETURN_TOKEN(ALIGN); } YY_BREAK case 55: YY_RULE_SETUP -#line 233 "maplexer.l" +#line 232 "maplexer.l" { MS_LEXER_RETURN_TOKEN(ANCHORPOINT); } YY_BREAK case 56: YY_RULE_SETUP -#line 234 "maplexer.l" +#line 233 "maplexer.l" { MS_LEXER_RETURN_TOKEN(ANGLE); } YY_BREAK case 57: YY_RULE_SETUP -#line 235 "maplexer.l" +#line 234 "maplexer.l" { MS_LEXER_RETURN_TOKEN(ANTIALIAS); } YY_BREAK case 58: YY_RULE_SETUP -#line 236 "maplexer.l" +#line 235 "maplexer.l" { MS_LEXER_RETURN_TOKEN(BACKGROUNDCOLOR); } YY_BREAK case 59: YY_RULE_SETUP -#line 237 "maplexer.l" +#line 236 "maplexer.l" { MS_LEXER_RETURN_TOKEN(BANDSITEM); } YY_BREAK case 60: YY_RULE_SETUP -#line 238 "maplexer.l" +#line 237 "maplexer.l" { MS_LEXER_RETURN_TOKEN(BINDVALS); } YY_BREAK case 61: YY_RULE_SETUP -#line 239 "maplexer.l" +#line 238 "maplexer.l" { MS_LEXER_RETURN_TOKEN(BOM); } YY_BREAK case 62: YY_RULE_SETUP -#line 240 "maplexer.l" +#line 239 "maplexer.l" { MS_LEXER_RETURN_TOKEN(BROWSEFORMAT); } YY_BREAK case 63: YY_RULE_SETUP -#line 241 "maplexer.l" +#line 240 "maplexer.l" { MS_LEXER_RETURN_TOKEN(BUFFER); } YY_BREAK case 64: YY_RULE_SETUP -#line 242 "maplexer.l" +#line 241 "maplexer.l" { MS_LEXER_RETURN_TOKEN(CHARACTER); } YY_BREAK case 65: YY_RULE_SETUP -#line 243 "maplexer.l" +#line 242 "maplexer.l" { MS_LEXER_RETURN_TOKEN(CLASS); } YY_BREAK case 66: YY_RULE_SETUP -#line 244 "maplexer.l" +#line 243 "maplexer.l" { MS_LEXER_RETURN_TOKEN(CLASSITEM); } YY_BREAK case 67: YY_RULE_SETUP -#line 245 "maplexer.l" +#line 244 "maplexer.l" { MS_LEXER_RETURN_TOKEN(CLASSGROUP); } YY_BREAK case 68: YY_RULE_SETUP -#line 246 "maplexer.l" +#line 245 "maplexer.l" { MS_LEXER_RETURN_TOKEN(CLUSTER); } YY_BREAK case 69: YY_RULE_SETUP -#line 247 "maplexer.l" +#line 246 "maplexer.l" { MS_LEXER_RETURN_TOKEN(COLOR); } YY_BREAK case 70: YY_RULE_SETUP -#line 248 "maplexer.l" +#line 247 "maplexer.l" { MS_LEXER_RETURN_TOKEN(COMPFILTER); } YY_BREAK case 71: YY_RULE_SETUP -#line 249 "maplexer.l" +#line 248 "maplexer.l" { MS_LEXER_RETURN_TOKEN(COMPOSITE); } YY_BREAK case 72: YY_RULE_SETUP -#line 250 "maplexer.l" +#line 249 "maplexer.l" { MS_LEXER_RETURN_TOKEN(COMPOP); } YY_BREAK case 73: YY_RULE_SETUP -#line 251 "maplexer.l" +#line 250 "maplexer.l" { MS_LEXER_RETURN_TOKEN(CONFIG); } YY_BREAK case 74: YY_RULE_SETUP -#line 252 "maplexer.l" +#line 251 "maplexer.l" { MS_LEXER_RETURN_TOKEN(CONNECTION); } YY_BREAK case 75: YY_RULE_SETUP -#line 253 "maplexer.l" +#line 252 "maplexer.l" { MS_LEXER_RETURN_TOKEN(CONNECTIONTYPE); } YY_BREAK case 76: YY_RULE_SETUP -#line 254 "maplexer.l" +#line 253 "maplexer.l" { MS_LEXER_RETURN_TOKEN(DATA); } YY_BREAK case 77: YY_RULE_SETUP -#line 255 "maplexer.l" +#line 254 "maplexer.l" { MS_LEXER_RETURN_TOKEN(DATAPATTERN); } YY_BREAK case 78: YY_RULE_SETUP -#line 256 "maplexer.l" +#line 255 "maplexer.l" { MS_LEXER_RETURN_TOKEN(DEBUG); } YY_BREAK case 79: YY_RULE_SETUP -#line 257 "maplexer.l" +#line 256 "maplexer.l" { MS_LEXER_RETURN_TOKEN(DRIVER); } YY_BREAK case 80: YY_RULE_SETUP -#line 258 "maplexer.l" +#line 257 "maplexer.l" { MS_LEXER_RETURN_TOKEN(DUMP); } YY_BREAK case 81: YY_RULE_SETUP -#line 259 "maplexer.l" +#line 258 "maplexer.l" { MS_LEXER_RETURN_TOKEN(EMPTY); } YY_BREAK case 82: YY_RULE_SETUP -#line 260 "maplexer.l" +#line 259 "maplexer.l" { MS_LEXER_RETURN_TOKEN(ENCODING); } YY_BREAK case 83: YY_RULE_SETUP -#line 261 "maplexer.l" +#line 260 "maplexer.l" { MS_LEXER_RETURN_TOKEN(END); } YY_BREAK case 84: YY_RULE_SETUP -#line 262 "maplexer.l" +#line 261 "maplexer.l" { MS_LEXER_RETURN_TOKEN(ERROR); } YY_BREAK case 85: YY_RULE_SETUP -#line 263 "maplexer.l" +#line 262 "maplexer.l" { MS_LEXER_RETURN_TOKEN(EXPRESSION); } YY_BREAK case 86: YY_RULE_SETUP -#line 264 "maplexer.l" +#line 263 "maplexer.l" { MS_LEXER_RETURN_TOKEN(EXTENT); } YY_BREAK case 87: YY_RULE_SETUP -#line 265 "maplexer.l" +#line 264 "maplexer.l" { MS_LEXER_RETURN_TOKEN(EXTENSION); } YY_BREAK case 88: YY_RULE_SETUP -#line 266 "maplexer.l" +#line 265 "maplexer.l" { MS_LEXER_RETURN_TOKEN(FEATURE); } YY_BREAK case 89: YY_RULE_SETUP -#line 267 "maplexer.l" +#line 266 "maplexer.l" { MS_LEXER_RETURN_TOKEN(FILLED); } YY_BREAK case 90: YY_RULE_SETUP -#line 268 "maplexer.l" +#line 267 "maplexer.l" { MS_LEXER_RETURN_TOKEN(FILTER); } YY_BREAK case 91: YY_RULE_SETUP -#line 269 "maplexer.l" +#line 268 "maplexer.l" { MS_LEXER_RETURN_TOKEN(FILTERITEM); } YY_BREAK case 92: YY_RULE_SETUP -#line 270 "maplexer.l" +#line 269 "maplexer.l" { MS_LEXER_RETURN_TOKEN(FOOTER); } YY_BREAK case 93: YY_RULE_SETUP -#line 271 "maplexer.l" +#line 270 "maplexer.l" { MS_LEXER_RETURN_TOKEN(FONT); } YY_BREAK case 94: YY_RULE_SETUP -#line 272 "maplexer.l" +#line 271 "maplexer.l" { MS_LEXER_RETURN_TOKEN(FONTSET); } YY_BREAK case 95: YY_RULE_SETUP -#line 273 "maplexer.l" +#line 272 "maplexer.l" { MS_LEXER_RETURN_TOKEN(FORCE); } YY_BREAK case 96: YY_RULE_SETUP -#line 274 "maplexer.l" +#line 273 "maplexer.l" { MS_LEXER_RETURN_TOKEN(FORMATOPTION); } YY_BREAK case 97: YY_RULE_SETUP -#line 275 "maplexer.l" +#line 274 "maplexer.l" { MS_LEXER_RETURN_TOKEN(FROM); } YY_BREAK case 98: YY_RULE_SETUP -#line 276 "maplexer.l" +#line 275 "maplexer.l" { MS_LEXER_RETURN_TOKEN(GAP); } YY_BREAK case 99: YY_RULE_SETUP -#line 277 "maplexer.l" +#line 276 "maplexer.l" { MS_LEXER_RETURN_TOKEN(GEOMTRANSFORM); } YY_BREAK case 100: YY_RULE_SETUP -#line 278 "maplexer.l" +#line 277 "maplexer.l" { MS_LEXER_RETURN_TOKEN(GRID); } YY_BREAK case 101: YY_RULE_SETUP -#line 279 "maplexer.l" +#line 278 "maplexer.l" { MS_LEXER_RETURN_TOKEN(GRIDSTEP); } YY_BREAK case 102: YY_RULE_SETUP -#line 280 "maplexer.l" +#line 279 "maplexer.l" { MS_LEXER_RETURN_TOKEN(GRATICULE); } YY_BREAK case 103: YY_RULE_SETUP -#line 281 "maplexer.l" +#line 280 "maplexer.l" { MS_LEXER_RETURN_TOKEN(GROUP); } YY_BREAK case 104: YY_RULE_SETUP -#line 282 "maplexer.l" +#line 281 "maplexer.l" { MS_LEXER_RETURN_TOKEN(HEADER); } YY_BREAK case 105: YY_RULE_SETUP -#line 283 "maplexer.l" +#line 282 "maplexer.l" { MS_LEXER_RETURN_TOKEN(IMAGE); } YY_BREAK case 106: YY_RULE_SETUP -#line 284 "maplexer.l" +#line 283 "maplexer.l" { MS_LEXER_RETURN_TOKEN(IMAGECOLOR); } YY_BREAK case 107: YY_RULE_SETUP -#line 285 "maplexer.l" +#line 284 "maplexer.l" { MS_LEXER_RETURN_TOKEN(IMAGETYPE); } YY_BREAK case 108: YY_RULE_SETUP -#line 286 "maplexer.l" +#line 285 "maplexer.l" { MS_LEXER_RETURN_TOKEN(IMAGEQUALITY); } YY_BREAK case 109: YY_RULE_SETUP -#line 287 "maplexer.l" +#line 286 "maplexer.l" { MS_LEXER_RETURN_TOKEN(IMAGEMODE); } YY_BREAK case 110: YY_RULE_SETUP -#line 288 "maplexer.l" +#line 287 "maplexer.l" { MS_LEXER_RETURN_TOKEN(IMAGEPATH); } YY_BREAK case 111: YY_RULE_SETUP -#line 289 "maplexer.l" +#line 288 "maplexer.l" { MS_LEXER_RETURN_TOKEN(TEMPPATH); } YY_BREAK case 112: YY_RULE_SETUP -#line 290 "maplexer.l" +#line 289 "maplexer.l" { MS_LEXER_RETURN_TOKEN(IMAGEURL); } YY_BREAK case 113: YY_RULE_SETUP -#line 291 "maplexer.l" +#line 290 "maplexer.l" { BEGIN(INCLUDE); } YY_BREAK case 114: YY_RULE_SETUP -#line 292 "maplexer.l" +#line 291 "maplexer.l" { MS_LEXER_RETURN_TOKEN(INDEX); } YY_BREAK case 115: YY_RULE_SETUP -#line 293 "maplexer.l" +#line 292 "maplexer.l" { MS_LEXER_RETURN_TOKEN(INITIALGAP); } YY_BREAK case 116: YY_RULE_SETUP -#line 294 "maplexer.l" +#line 293 "maplexer.l" { MS_LEXER_RETURN_TOKEN(INTERLACE); } YY_BREAK case 117: YY_RULE_SETUP -#line 295 "maplexer.l" +#line 294 "maplexer.l" { MS_LEXER_RETURN_TOKEN(INTERVALS); } YY_BREAK case 118: YY_RULE_SETUP -#line 296 "maplexer.l" +#line 295 "maplexer.l" { MS_LEXER_RETURN_TOKEN(JOIN); } YY_BREAK case 119: YY_RULE_SETUP -#line 297 "maplexer.l" +#line 296 "maplexer.l" { MS_LEXER_RETURN_TOKEN(KEYIMAGE); } YY_BREAK case 120: YY_RULE_SETUP -#line 298 "maplexer.l" +#line 297 "maplexer.l" { MS_LEXER_RETURN_TOKEN(KEYSIZE); } YY_BREAK case 121: YY_RULE_SETUP -#line 299 "maplexer.l" +#line 298 "maplexer.l" { MS_LEXER_RETURN_TOKEN(KEYSPACING); } YY_BREAK case 122: YY_RULE_SETUP -#line 300 "maplexer.l" +#line 299 "maplexer.l" { MS_LEXER_RETURN_TOKEN(LABEL); } YY_BREAK case 123: YY_RULE_SETUP -#line 301 "maplexer.l" +#line 300 "maplexer.l" { MS_LEXER_RETURN_TOKEN(LABELCACHE); } YY_BREAK case 124: YY_RULE_SETUP -#line 302 "maplexer.l" +#line 301 "maplexer.l" { MS_LEXER_RETURN_TOKEN(LABELFORMAT); } YY_BREAK case 125: YY_RULE_SETUP -#line 303 "maplexer.l" +#line 302 "maplexer.l" { MS_LEXER_RETURN_TOKEN(LABELITEM); } YY_BREAK case 126: YY_RULE_SETUP -#line 304 "maplexer.l" +#line 303 "maplexer.l" { MS_LEXER_RETURN_TOKEN(LABELMAXSCALE); } YY_BREAK case 127: YY_RULE_SETUP -#line 305 "maplexer.l" +#line 304 "maplexer.l" { MS_LEXER_RETURN_TOKEN(LABELMAXSCALEDENOM); } YY_BREAK case 128: YY_RULE_SETUP -#line 306 "maplexer.l" +#line 305 "maplexer.l" { MS_LEXER_RETURN_TOKEN(LABELMINSCALE); } YY_BREAK case 129: YY_RULE_SETUP -#line 307 "maplexer.l" +#line 306 "maplexer.l" { MS_LEXER_RETURN_TOKEN(LABELMINSCALEDENOM); } YY_BREAK case 130: YY_RULE_SETUP -#line 308 "maplexer.l" +#line 307 "maplexer.l" { MS_LEXER_RETURN_TOKEN(LABELREQUIRES); } YY_BREAK case 131: YY_RULE_SETUP -#line 309 "maplexer.l" +#line 308 "maplexer.l" { MS_LEXER_RETURN_TOKEN(LATLON); } YY_BREAK case 132: YY_RULE_SETUP -#line 310 "maplexer.l" +#line 309 "maplexer.l" { MS_LEXER_RETURN_TOKEN(LAYER); } YY_BREAK case 133: YY_RULE_SETUP -#line 311 "maplexer.l" +#line 310 "maplexer.l" { MS_LEXER_RETURN_TOKEN(LEADER); } YY_BREAK case 134: YY_RULE_SETUP -#line 312 "maplexer.l" +#line 311 "maplexer.l" { MS_LEXER_RETURN_TOKEN(LEGEND); } YY_BREAK case 135: YY_RULE_SETUP -#line 313 "maplexer.l" +#line 312 "maplexer.l" { MS_LEXER_RETURN_TOKEN(LEGENDFORMAT); } YY_BREAK case 136: YY_RULE_SETUP -#line 314 "maplexer.l" +#line 313 "maplexer.l" { MS_LEXER_RETURN_TOKEN(LINECAP); } YY_BREAK case 137: YY_RULE_SETUP -#line 315 "maplexer.l" +#line 314 "maplexer.l" { MS_LEXER_RETURN_TOKEN(LINEJOIN); } YY_BREAK case 138: YY_RULE_SETUP -#line 316 "maplexer.l" +#line 315 "maplexer.l" { MS_LEXER_RETURN_TOKEN(LINEJOINMAXSIZE); } YY_BREAK case 139: YY_RULE_SETUP -#line 317 "maplexer.l" +#line 316 "maplexer.l" { MS_LEXER_RETURN_TOKEN(LOG); } YY_BREAK case 140: YY_RULE_SETUP -#line 318 "maplexer.l" +#line 317 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MAP); } YY_BREAK case 141: YY_RULE_SETUP -#line 319 "maplexer.l" +#line 318 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MARKER); } YY_BREAK case 142: YY_RULE_SETUP -#line 320 "maplexer.l" +#line 319 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MARKERSIZE); } YY_BREAK case 143: YY_RULE_SETUP -#line 321 "maplexer.l" +#line 320 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MASK); } YY_BREAK case 144: YY_RULE_SETUP -#line 322 "maplexer.l" +#line 321 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MAXARCS); } YY_BREAK case 145: YY_RULE_SETUP -#line 323 "maplexer.l" +#line 322 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MAXBOXSIZE); } YY_BREAK case 146: YY_RULE_SETUP -#line 324 "maplexer.l" +#line 323 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MAXDISTANCE); } YY_BREAK case 147: YY_RULE_SETUP -#line 325 "maplexer.l" +#line 324 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MAXFEATURES); } YY_BREAK case 148: YY_RULE_SETUP -#line 326 "maplexer.l" +#line 325 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MAXINTERVAL); } YY_BREAK case 149: YY_RULE_SETUP -#line 327 "maplexer.l" +#line 326 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MAXSCALE); } YY_BREAK case 150: YY_RULE_SETUP -#line 328 "maplexer.l" +#line 327 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MAXSCALEDENOM); } YY_BREAK case 151: YY_RULE_SETUP -#line 329 "maplexer.l" +#line 328 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MAXGEOWIDTH); } YY_BREAK case 152: YY_RULE_SETUP -#line 330 "maplexer.l" +#line 329 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MAXLENGTH); } YY_BREAK case 153: YY_RULE_SETUP -#line 331 "maplexer.l" +#line 330 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MAXSIZE); } YY_BREAK case 154: YY_RULE_SETUP -#line 332 "maplexer.l" +#line 331 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MAXSUBDIVIDE); } YY_BREAK case 155: YY_RULE_SETUP -#line 333 "maplexer.l" +#line 332 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MAXTEMPLATE); } YY_BREAK case 156: YY_RULE_SETUP -#line 334 "maplexer.l" +#line 333 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MAXWIDTH); } YY_BREAK case 157: YY_RULE_SETUP -#line 335 "maplexer.l" +#line 334 "maplexer.l" { MS_LEXER_RETURN_TOKEN(METADATA); } YY_BREAK case 158: YY_RULE_SETUP -#line 336 "maplexer.l" +#line 335 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MIMETYPE); } YY_BREAK case 159: YY_RULE_SETUP -#line 337 "maplexer.l" +#line 336 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MINARCS); } YY_BREAK case 160: YY_RULE_SETUP -#line 338 "maplexer.l" +#line 337 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MINBOXSIZE); } YY_BREAK case 161: YY_RULE_SETUP -#line 339 "maplexer.l" +#line 338 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MINDISTANCE); } YY_BREAK case 162: YY_RULE_SETUP -#line 340 "maplexer.l" +#line 339 "maplexer.l" { MS_LEXER_RETURN_TOKEN(REPEATDISTANCE); } YY_BREAK case 163: YY_RULE_SETUP -#line 341 "maplexer.l" +#line 340 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MAXOVERLAPANGLE); } YY_BREAK case 164: YY_RULE_SETUP -#line 342 "maplexer.l" +#line 341 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MINFEATURESIZE); } YY_BREAK case 165: YY_RULE_SETUP -#line 343 "maplexer.l" +#line 342 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MININTERVAL); } YY_BREAK case 166: YY_RULE_SETUP -#line 344 "maplexer.l" +#line 343 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MINSCALE); } YY_BREAK case 167: YY_RULE_SETUP -#line 345 "maplexer.l" +#line 344 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MINSCALEDENOM); } YY_BREAK case 168: YY_RULE_SETUP -#line 346 "maplexer.l" +#line 345 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MINGEOWIDTH); } YY_BREAK case 169: YY_RULE_SETUP -#line 347 "maplexer.l" +#line 346 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MINLENGTH); } YY_BREAK case 170: YY_RULE_SETUP -#line 348 "maplexer.l" +#line 347 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MINSIZE); } YY_BREAK case 171: YY_RULE_SETUP -#line 349 "maplexer.l" +#line 348 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MINSUBDIVIDE); } YY_BREAK case 172: YY_RULE_SETUP -#line 350 "maplexer.l" +#line 349 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MINTEMPLATE); } YY_BREAK case 173: YY_RULE_SETUP -#line 351 "maplexer.l" +#line 350 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MINWIDTH); } YY_BREAK case 174: YY_RULE_SETUP -#line 352 "maplexer.l" +#line 351 "maplexer.l" { MS_LEXER_RETURN_TOKEN(NAME); } YY_BREAK case 175: YY_RULE_SETUP -#line 353 "maplexer.l" +#line 352 "maplexer.l" { MS_LEXER_RETURN_TOKEN(OFFSET); } YY_BREAK case 176: YY_RULE_SETUP -#line 354 "maplexer.l" +#line 353 "maplexer.l" { MS_LEXER_RETURN_TOKEN(OFFSITE); } YY_BREAK case 177: YY_RULE_SETUP -#line 355 "maplexer.l" +#line 354 "maplexer.l" { MS_LEXER_RETURN_TOKEN(OPACITY); } YY_BREAK case 178: YY_RULE_SETUP -#line 356 "maplexer.l" +#line 355 "maplexer.l" { MS_LEXER_RETURN_TOKEN(CONNECTIONOPTIONS); } YY_BREAK case 179: YY_RULE_SETUP -#line 357 "maplexer.l" +#line 356 "maplexer.l" { MS_LEXER_RETURN_TOKEN(OUTLINECOLOR); } YY_BREAK case 180: YY_RULE_SETUP -#line 358 "maplexer.l" +#line 357 "maplexer.l" { MS_LEXER_RETURN_TOKEN(OUTLINEWIDTH); } YY_BREAK case 181: YY_RULE_SETUP -#line 359 "maplexer.l" +#line 358 "maplexer.l" { MS_LEXER_RETURN_TOKEN(OUTPUTFORMAT); } YY_BREAK case 182: YY_RULE_SETUP -#line 360 "maplexer.l" +#line 359 "maplexer.l" { MS_LEXER_RETURN_TOKEN(OVERLAYBACKGROUNDCOLOR); } YY_BREAK case 183: YY_RULE_SETUP -#line 361 "maplexer.l" +#line 360 "maplexer.l" { MS_LEXER_RETURN_TOKEN(OVERLAYCOLOR); } YY_BREAK case 184: YY_RULE_SETUP -#line 362 "maplexer.l" +#line 361 "maplexer.l" { MS_LEXER_RETURN_TOKEN(OVERLAYMAXSIZE); } YY_BREAK case 185: YY_RULE_SETUP -#line 363 "maplexer.l" +#line 362 "maplexer.l" { MS_LEXER_RETURN_TOKEN(OVERLAYMINSIZE); } YY_BREAK case 186: YY_RULE_SETUP -#line 364 "maplexer.l" +#line 363 "maplexer.l" { MS_LEXER_RETURN_TOKEN(OVERLAYOUTLINECOLOR); } YY_BREAK case 187: YY_RULE_SETUP -#line 365 "maplexer.l" +#line 364 "maplexer.l" { MS_LEXER_RETURN_TOKEN(OVERLAYSIZE); } YY_BREAK case 188: YY_RULE_SETUP -#line 366 "maplexer.l" +#line 365 "maplexer.l" { MS_LEXER_RETURN_TOKEN(OVERLAYSYMBOL); } YY_BREAK case 189: YY_RULE_SETUP -#line 367 "maplexer.l" +#line 366 "maplexer.l" { MS_LEXER_RETURN_TOKEN(PARTIALS); } YY_BREAK case 190: YY_RULE_SETUP -#line 368 "maplexer.l" +#line 367 "maplexer.l" { MS_LEXER_RETURN_TOKEN(PATTERN); } YY_BREAK case 191: YY_RULE_SETUP -#line 369 "maplexer.l" +#line 368 "maplexer.l" { MS_LEXER_RETURN_TOKEN(POINTS); } YY_BREAK case 192: YY_RULE_SETUP -#line 370 "maplexer.l" +#line 369 "maplexer.l" { MS_LEXER_RETURN_TOKEN(ITEMS); } YY_BREAK case 193: YY_RULE_SETUP -#line 371 "maplexer.l" +#line 370 "maplexer.l" { MS_LEXER_RETURN_TOKEN(POSITION); } YY_BREAK case 194: YY_RULE_SETUP -#line 372 "maplexer.l" +#line 371 "maplexer.l" { MS_LEXER_RETURN_TOKEN(POSTLABELCACHE); } YY_BREAK case 195: YY_RULE_SETUP -#line 373 "maplexer.l" +#line 372 "maplexer.l" { MS_LEXER_RETURN_TOKEN(PRIORITY); } YY_BREAK case 196: YY_RULE_SETUP -#line 374 "maplexer.l" +#line 373 "maplexer.l" { MS_LEXER_RETURN_TOKEN(PROCESSING); } YY_BREAK case 197: YY_RULE_SETUP -#line 375 "maplexer.l" +#line 374 "maplexer.l" { MS_LEXER_RETURN_TOKEN(PROJECTION); } YY_BREAK case 198: YY_RULE_SETUP -#line 376 "maplexer.l" +#line 375 "maplexer.l" { MS_LEXER_RETURN_TOKEN(QUERYFORMAT); } YY_BREAK case 199: YY_RULE_SETUP -#line 377 "maplexer.l" +#line 376 "maplexer.l" { MS_LEXER_RETURN_TOKEN(QUERYMAP); } YY_BREAK case 200: YY_RULE_SETUP -#line 378 "maplexer.l" +#line 377 "maplexer.l" { MS_LEXER_RETURN_TOKEN(REFERENCE); } YY_BREAK case 201: YY_RULE_SETUP -#line 379 "maplexer.l" +#line 378 "maplexer.l" { MS_LEXER_RETURN_TOKEN(REGION); } YY_BREAK case 202: YY_RULE_SETUP -#line 380 "maplexer.l" +#line 379 "maplexer.l" { MS_LEXER_RETURN_TOKEN(RELATIVETO); } YY_BREAK case 203: YY_RULE_SETUP -#line 381 "maplexer.l" +#line 380 "maplexer.l" { MS_LEXER_RETURN_TOKEN(REQUIRES); } YY_BREAK case 204: YY_RULE_SETUP -#line 382 "maplexer.l" +#line 381 "maplexer.l" { MS_LEXER_RETURN_TOKEN(RESOLUTION); } YY_BREAK case 205: YY_RULE_SETUP -#line 383 "maplexer.l" +#line 382 "maplexer.l" { MS_LEXER_RETURN_TOKEN(DEFRESOLUTION); } YY_BREAK case 206: YY_RULE_SETUP -#line 384 "maplexer.l" +#line 383 "maplexer.l" { MS_LEXER_RETURN_TOKEN(SCALE); } YY_BREAK case 207: YY_RULE_SETUP -#line 385 "maplexer.l" +#line 384 "maplexer.l" { MS_LEXER_RETURN_TOKEN(SCALEDENOM); } YY_BREAK case 208: YY_RULE_SETUP -#line 386 "maplexer.l" +#line 385 "maplexer.l" { MS_LEXER_RETURN_TOKEN(SCALEBAR); } YY_BREAK case 209: YY_RULE_SETUP -#line 387 "maplexer.l" +#line 386 "maplexer.l" { MS_LEXER_RETURN_TOKEN(SCALETOKEN); } YY_BREAK case 210: YY_RULE_SETUP -#line 388 "maplexer.l" +#line 387 "maplexer.l" { MS_LEXER_RETURN_TOKEN(SHADOWCOLOR); } YY_BREAK case 211: YY_RULE_SETUP -#line 389 "maplexer.l" +#line 388 "maplexer.l" { MS_LEXER_RETURN_TOKEN(SHADOWSIZE); } YY_BREAK case 212: YY_RULE_SETUP -#line 390 "maplexer.l" +#line 389 "maplexer.l" { MS_LEXER_RETURN_TOKEN(SHAPEPATH); } YY_BREAK case 213: YY_RULE_SETUP -#line 391 "maplexer.l" +#line 390 "maplexer.l" { MS_LEXER_RETURN_TOKEN(SIZE); } YY_BREAK case 214: YY_RULE_SETUP -#line 392 "maplexer.l" +#line 391 "maplexer.l" { MS_LEXER_RETURN_TOKEN(SIZEUNITS); } YY_BREAK case 215: YY_RULE_SETUP -#line 393 "maplexer.l" +#line 392 "maplexer.l" { MS_LEXER_RETURN_TOKEN(STATUS); } YY_BREAK case 216: YY_RULE_SETUP -#line 394 "maplexer.l" +#line 393 "maplexer.l" { MS_LEXER_RETURN_TOKEN(STYLE); } YY_BREAK case 217: YY_RULE_SETUP -#line 395 "maplexer.l" +#line 394 "maplexer.l" { MS_LEXER_RETURN_TOKEN(STYLEITEM); } YY_BREAK case 218: YY_RULE_SETUP -#line 396 "maplexer.l" +#line 395 "maplexer.l" { MS_LEXER_RETURN_TOKEN(SYMBOL); } YY_BREAK case 219: YY_RULE_SETUP -#line 397 "maplexer.l" +#line 396 "maplexer.l" { MS_LEXER_RETURN_TOKEN(SYMBOLSCALE); } YY_BREAK case 220: YY_RULE_SETUP -#line 398 "maplexer.l" +#line 397 "maplexer.l" { MS_LEXER_RETURN_TOKEN(SYMBOLSCALEDENOM); } YY_BREAK case 221: YY_RULE_SETUP -#line 399 "maplexer.l" +#line 398 "maplexer.l" { MS_LEXER_RETURN_TOKEN(SYMBOLSET); } YY_BREAK case 222: YY_RULE_SETUP -#line 400 "maplexer.l" +#line 399 "maplexer.l" { MS_LEXER_RETURN_TOKEN(TABLE); } YY_BREAK case 223: YY_RULE_SETUP -#line 401 "maplexer.l" +#line 400 "maplexer.l" { MS_LEXER_RETURN_TOKEN(TEMPLATE); } YY_BREAK case 224: YY_RULE_SETUP -#line 402 "maplexer.l" +#line 401 "maplexer.l" { MS_LEXER_RETURN_TOKEN(TEMPLATEPATTERN); } YY_BREAK case 225: YY_RULE_SETUP -#line 403 "maplexer.l" +#line 402 "maplexer.l" { MS_LEXER_RETURN_TOKEN(TEXT); } YY_BREAK case 226: YY_RULE_SETUP -#line 404 "maplexer.l" +#line 403 "maplexer.l" { MS_LEXER_RETURN_TOKEN(TILEINDEX); } YY_BREAK case 227: YY_RULE_SETUP -#line 405 "maplexer.l" +#line 404 "maplexer.l" { MS_LEXER_RETURN_TOKEN(TILEITEM); } YY_BREAK case 228: YY_RULE_SETUP -#line 406 "maplexer.l" +#line 405 "maplexer.l" { MS_LEXER_RETURN_TOKEN(TILESRS); } YY_BREAK case 229: YY_RULE_SETUP -#line 407 "maplexer.l" +#line 406 "maplexer.l" { MS_LEXER_RETURN_TOKEN(TITLE); } YY_BREAK case 230: YY_RULE_SETUP -#line 408 "maplexer.l" +#line 407 "maplexer.l" { MS_LEXER_RETURN_TOKEN(TO); } YY_BREAK case 231: YY_RULE_SETUP -#line 409 "maplexer.l" +#line 408 "maplexer.l" { MS_LEXER_RETURN_TOKEN(TOLERANCE); } YY_BREAK case 232: YY_RULE_SETUP -#line 410 "maplexer.l" +#line 409 "maplexer.l" { MS_LEXER_RETURN_TOKEN(TOLERANCEUNITS); } YY_BREAK case 233: YY_RULE_SETUP -#line 411 "maplexer.l" +#line 410 "maplexer.l" { MS_LEXER_RETURN_TOKEN(TRANSPARENCY); } YY_BREAK case 234: YY_RULE_SETUP -#line 412 "maplexer.l" +#line 411 "maplexer.l" { MS_LEXER_RETURN_TOKEN(TRANSPARENT); } YY_BREAK case 235: YY_RULE_SETUP -#line 413 "maplexer.l" +#line 412 "maplexer.l" { MS_LEXER_RETURN_TOKEN(TRANSFORM); } YY_BREAK case 236: YY_RULE_SETUP -#line 414 "maplexer.l" +#line 413 "maplexer.l" { MS_LEXER_RETURN_TOKEN(TYPE); } YY_BREAK case 237: YY_RULE_SETUP -#line 415 "maplexer.l" +#line 414 "maplexer.l" { MS_LEXER_RETURN_TOKEN(UNITS); } YY_BREAK case 238: YY_RULE_SETUP -#line 416 "maplexer.l" +#line 415 "maplexer.l" { MS_LEXER_RETURN_TOKEN(UTFDATA); } YY_BREAK case 239: YY_RULE_SETUP -#line 417 "maplexer.l" +#line 416 "maplexer.l" { MS_LEXER_RETURN_TOKEN(UTFITEM); } YY_BREAK case 240: YY_RULE_SETUP -#line 418 "maplexer.l" +#line 417 "maplexer.l" { MS_LEXER_RETURN_TOKEN(VALIDATION); } YY_BREAK case 241: YY_RULE_SETUP -#line 419 "maplexer.l" +#line 418 "maplexer.l" { MS_LEXER_RETURN_TOKEN(VALUES); } YY_BREAK case 242: YY_RULE_SETUP -#line 420 "maplexer.l" +#line 419 "maplexer.l" { MS_LEXER_RETURN_TOKEN(WEB); } YY_BREAK case 243: YY_RULE_SETUP -#line 421 "maplexer.l" +#line 420 "maplexer.l" { MS_LEXER_RETURN_TOKEN(WIDTH); } YY_BREAK case 244: YY_RULE_SETUP -#line 422 "maplexer.l" +#line 421 "maplexer.l" { MS_LEXER_RETURN_TOKEN(WKT); } YY_BREAK case 245: YY_RULE_SETUP -#line 423 "maplexer.l" +#line 422 "maplexer.l" { MS_LEXER_RETURN_TOKEN(WRAP); } YY_BREAK case 246: YY_RULE_SETUP -#line 425 "maplexer.l" +#line 424 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_LAYER_ANNOTATION); } YY_BREAK case 247: YY_RULE_SETUP -#line 426 "maplexer.l" +#line 425 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_AUTO); } YY_BREAK case 248: YY_RULE_SETUP -#line 427 "maplexer.l" +#line 426 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_AUTO2); } YY_BREAK case 249: YY_RULE_SETUP -#line 428 "maplexer.l" +#line 427 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_CJC_BEVEL); } YY_BREAK case 250: YY_RULE_SETUP -#line 429 "maplexer.l" +#line 428 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_BITMAP); } YY_BREAK case 251: YY_RULE_SETUP -#line 430 "maplexer.l" +#line 429 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_CJC_BUTT); } YY_BREAK case 252: YY_RULE_SETUP -#line 431 "maplexer.l" +#line 430 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_CC); } YY_BREAK case 253: YY_RULE_SETUP -#line 432 "maplexer.l" +#line 431 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_ALIGN_CENTER); } YY_BREAK case 254: YY_RULE_SETUP -#line 433 "maplexer.l" +#line 432 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_LAYER_CHART); } YY_BREAK case 255: YY_RULE_SETUP -#line 434 "maplexer.l" +#line 433 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_LAYER_CIRCLE); } YY_BREAK case 256: YY_RULE_SETUP -#line 435 "maplexer.l" +#line 434 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_CL); } YY_BREAK case 257: YY_RULE_SETUP -#line 436 "maplexer.l" +#line 435 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_CR); } YY_BREAK case 258: YY_RULE_SETUP -#line 437 "maplexer.l" +#line 436 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_DB_CSV); } YY_BREAK case 259: YY_RULE_SETUP -#line 438 "maplexer.l" +#line 437 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_DB_POSTGRES); } YY_BREAK case 260: YY_RULE_SETUP -#line 439 "maplexer.l" +#line 438 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_DB_MYSQL); } YY_BREAK case 261: YY_RULE_SETUP -#line 440 "maplexer.l" +#line 439 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_DEFAULT); } YY_BREAK case 262: YY_RULE_SETUP -#line 441 "maplexer.l" +#line 440 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_DD); } YY_BREAK case 263: YY_RULE_SETUP -#line 442 "maplexer.l" +#line 441 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_SYMBOL_ELLIPSE); } YY_BREAK case 264: YY_RULE_SETUP -#line 443 "maplexer.l" +#line 442 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_EMBED); } YY_BREAK case 265: YY_RULE_SETUP -#line 444 "maplexer.l" +#line 443 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_FALSE); } YY_BREAK case 266: YY_RULE_SETUP -#line 445 "maplexer.l" +#line 444 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_FEET); } YY_BREAK case 267: YY_RULE_SETUP -#line 446 "maplexer.l" +#line 445 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_FOLLOW); } YY_BREAK case 268: YY_RULE_SETUP -#line 447 "maplexer.l" +#line 446 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_GIANT); } YY_BREAK case 269: YY_RULE_SETUP -#line 448 "maplexer.l" +#line 447 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_SYMBOL_HATCH); } YY_BREAK case 270: YY_RULE_SETUP -#line 449 "maplexer.l" +#line 448 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_KERNELDENSITY); } YY_BREAK case 271: YY_RULE_SETUP -#line 450 "maplexer.l" +#line 449 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_HILITE); } YY_BREAK case 272: YY_RULE_SETUP -#line 451 "maplexer.l" +#line 450 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_INCHES); } YY_BREAK case 273: YY_RULE_SETUP -#line 452 "maplexer.l" +#line 451 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_KILOMETERS); } YY_BREAK case 274: YY_RULE_SETUP -#line 453 "maplexer.l" +#line 452 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_LARGE); } YY_BREAK case 275: YY_RULE_SETUP -#line 454 "maplexer.l" +#line 453 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_LC); } YY_BREAK case 276: YY_RULE_SETUP -#line 455 "maplexer.l" +#line 454 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_ALIGN_LEFT); } YY_BREAK case 277: YY_RULE_SETUP -#line 456 "maplexer.l" +#line 455 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_LAYER_LINE); } YY_BREAK case 278: YY_RULE_SETUP -#line 457 "maplexer.l" +#line 456 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_LL); } YY_BREAK case 279: YY_RULE_SETUP -#line 458 "maplexer.l" +#line 457 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_LR); } YY_BREAK case 280: YY_RULE_SETUP -#line 459 "maplexer.l" +#line 458 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_MEDIUM); } YY_BREAK case 281: YY_RULE_SETUP -#line 460 "maplexer.l" +#line 459 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_METERS); } YY_BREAK case 282: YY_RULE_SETUP -#line 461 "maplexer.l" +#line 460 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_NAUTICALMILES); } YY_BREAK case 283: YY_RULE_SETUP -#line 462 "maplexer.l" +#line 461 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_MILES); } YY_BREAK case 284: YY_RULE_SETUP -#line 463 "maplexer.l" +#line 462 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_CJC_MITER); } YY_BREAK case 285: YY_RULE_SETUP -#line 464 "maplexer.l" +#line 463 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_MULTIPLE); } YY_BREAK case 286: YY_RULE_SETUP -#line 465 "maplexer.l" +#line 464 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_CJC_NONE); } YY_BREAK case 287: YY_RULE_SETUP -#line 466 "maplexer.l" +#line 465 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_NORMAL); } YY_BREAK case 288: YY_RULE_SETUP -#line 467 "maplexer.l" +#line 466 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_OFF); } YY_BREAK case 289: YY_RULE_SETUP -#line 468 "maplexer.l" +#line 467 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_OGR); } YY_BREAK case 290: YY_RULE_SETUP -#line 469 "maplexer.l" +#line 468 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_ON); } YY_BREAK case 291: YY_RULE_SETUP -#line 470 "maplexer.l" +#line 469 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_JOIN_ONE_TO_ONE); } YY_BREAK case 292: YY_RULE_SETUP -#line 471 "maplexer.l" +#line 470 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_JOIN_ONE_TO_MANY); } YY_BREAK case 293: YY_RULE_SETUP -#line 472 "maplexer.l" +#line 471 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_ORACLESPATIAL); } YY_BREAK case 294: YY_RULE_SETUP -#line 473 "maplexer.l" +#line 472 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_PERCENTAGES); } YY_BREAK case 295: YY_RULE_SETUP -#line 474 "maplexer.l" +#line 473 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_SYMBOL_PIXMAP); } YY_BREAK case 296: YY_RULE_SETUP -#line 475 "maplexer.l" +#line 474 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_PIXELS); } YY_BREAK case 297: YY_RULE_SETUP -#line 476 "maplexer.l" +#line 475 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_LAYER_POINT); } YY_BREAK case 298: YY_RULE_SETUP -#line 477 "maplexer.l" +#line 476 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_LAYER_POLYGON); } YY_BREAK case 299: YY_RULE_SETUP -#line 478 "maplexer.l" +#line 477 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_POSTGIS); } YY_BREAK case 300: YY_RULE_SETUP -#line 479 "maplexer.l" +#line 478 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_PLUGIN); } YY_BREAK case 301: YY_RULE_SETUP -#line 480 "maplexer.l" +#line 479 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_LAYER_QUERY); } YY_BREAK case 302: YY_RULE_SETUP -#line 481 "maplexer.l" +#line 480 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_LAYER_RASTER); } YY_BREAK case 303: YY_RULE_SETUP -#line 482 "maplexer.l" +#line 481 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_ALIGN_RIGHT); } YY_BREAK case 304: YY_RULE_SETUP -#line 483 "maplexer.l" +#line 482 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_CJC_ROUND); } YY_BREAK case 305: YY_RULE_SETUP -#line 484 "maplexer.l" +#line 483 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_SELECTED); } YY_BREAK case 306: YY_RULE_SETUP -#line 485 "maplexer.l" +#line 484 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_SYMBOL_SIMPLE); } YY_BREAK case 307: YY_RULE_SETUP -#line 486 "maplexer.l" +#line 485 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_SINGLE); } YY_BREAK case 308: YY_RULE_SETUP -#line 487 "maplexer.l" +#line 486 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_SMALL); } YY_BREAK case 309: YY_RULE_SETUP -#line 488 "maplexer.l" +#line 487 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_CJC_SQUARE); } YY_BREAK case 310: YY_RULE_SETUP -#line 489 "maplexer.l" +#line 488 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_SYMBOL_SVG); } YY_BREAK case 311: YY_RULE_SETUP -#line 490 "maplexer.l" +#line 489 "maplexer.l" { MS_LEXER_RETURN_TOKEN(POLAROFFSET); } YY_BREAK case 312: YY_RULE_SETUP -#line 491 "maplexer.l" +#line 490 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TINY); } YY_BREAK case 313: YY_RULE_SETUP -#line 492 "maplexer.l" +#line 491 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_CJC_TRIANGLE); } YY_BREAK case 314: YY_RULE_SETUP -#line 493 "maplexer.l" +#line 492 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TRUE); } YY_BREAK case 315: YY_RULE_SETUP -#line 494 "maplexer.l" +#line 493 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_TRUETYPE); } YY_BREAK case 316: YY_RULE_SETUP -#line 495 "maplexer.l" +#line 494 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_UC); } YY_BREAK case 317: YY_RULE_SETUP -#line 496 "maplexer.l" +#line 495 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_UL); } YY_BREAK case 318: YY_RULE_SETUP -#line 497 "maplexer.l" +#line 496 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_UR); } YY_BREAK case 319: YY_RULE_SETUP -#line 498 "maplexer.l" +#line 497 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_UNION); } YY_BREAK case 320: YY_RULE_SETUP -#line 499 "maplexer.l" +#line 498 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_UVRASTER); } YY_BREAK case 321: YY_RULE_SETUP -#line 500 "maplexer.l" +#line 499 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_CONTOUR); } YY_BREAK case 322: YY_RULE_SETUP -#line 501 "maplexer.l" +#line 500 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_SYMBOL_VECTOR); } YY_BREAK case 323: YY_RULE_SETUP -#line 502 "maplexer.l" +#line 501 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_WFS); } YY_BREAK case 324: YY_RULE_SETUP -#line 503 "maplexer.l" +#line 502 "maplexer.l" { MS_LEXER_RETURN_TOKEN(MS_WMS); } YY_BREAK case 325: YY_RULE_SETUP -#line 505 "maplexer.l" +#line 504 "maplexer.l" { msyytext++; msyytext[strlen(msyytext)-1] = '\0'; MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyystring_buffer_size); strcpy(msyystring_buffer,msyytext); return(MS_STRING); } YY_BREAK case 326: YY_RULE_SETUP -#line 513 "maplexer.l" +#line 512 "maplexer.l" { msyytext++; msyytext[strlen(msyytext)-1] = '\0'; MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyystring_buffer_size); strcpy(msyystring_buffer,msyytext); msyynumber = atof(msyytext); return(MS_NUMBER); @@ -4331,19 +4541,19 @@ YY_RULE_SETUP case 327: /* rule 327 can match eol */ YY_RULE_SETUP -#line 523 "maplexer.l" +#line 522 "maplexer.l" { msyytext++; msyytext[strlen(msyytext)-1] = '\0'; MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyystring_buffer_size); strcpy(msyystring_buffer,msyytext); return(MS_BINDING); } YY_BREAK case 328: YY_RULE_SETUP -#line 532 "maplexer.l" +#line 531 "maplexer.l" { /* attribute binding - shape (fixed value) */ return(MS_TOKEN_BINDING_SHAPE); @@ -4351,7 +4561,7 @@ YY_RULE_SETUP YY_BREAK case 329: YY_RULE_SETUP -#line 536 "maplexer.l" +#line 535 "maplexer.l" { /* attribute binding - map cellsize */ return(MS_TOKEN_BINDING_MAP_CELLSIZE); @@ -4359,7 +4569,7 @@ YY_RULE_SETUP YY_BREAK case 330: YY_RULE_SETUP -#line 540 "maplexer.l" +#line 539 "maplexer.l" { /* attribute binding - data cellsize */ return(MS_TOKEN_BINDING_DATA_CELLSIZE); @@ -4368,13 +4578,13 @@ YY_RULE_SETUP case 331: /* rule 331 can match eol */ YY_RULE_SETUP -#line 544 "maplexer.l" +#line 543 "maplexer.l" { /* attribute binding - numeric (no quotes) */ msyytext++; msyytext[strlen(msyytext)-1] = '\0'; MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_TOKEN_BINDING_DOUBLE); } @@ -4382,13 +4592,13 @@ YY_RULE_SETUP case 332: /* rule 332 can match eol */ YY_RULE_SETUP -#line 553 "maplexer.l" +#line 552 "maplexer.l" { /* attribute binding - string (single or double quotes) */ msyytext+=2; msyytext[strlen(msyytext)-2] = '\0'; MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_TOKEN_BINDING_STRING); } @@ -4396,23 +4606,23 @@ YY_RULE_SETUP case 333: /* rule 333 can match eol */ YY_RULE_SETUP -#line 562 "maplexer.l" +#line 561 "maplexer.l" { /* attribute binding - time */ msyytext+=2; msyytext[strlen(msyytext)-2] = '\0'; MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_TOKEN_BINDING_TIME); } YY_BREAK case 334: YY_RULE_SETUP -#line 572 "maplexer.l" +#line 571 "maplexer.l" { MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyystring_buffer_size); strcpy(msyystring_buffer,msyytext); msyynumber = atof(msyytext); return(MS_NUMBER); @@ -4420,10 +4630,10 @@ YY_RULE_SETUP YY_BREAK case 335: YY_RULE_SETUP -#line 580 "maplexer.l" +#line 579 "maplexer.l" { MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyystring_buffer_size); strcpy(msyystring_buffer,msyytext); msyynumber = atof(msyytext); return(MS_TOKEN_LITERAL_NUMBER); @@ -4432,12 +4642,12 @@ YY_RULE_SETUP case 336: /* rule 336 can match eol */ YY_RULE_SETUP -#line 588 "maplexer.l" +#line 587 "maplexer.l" { msyytext++; msyytext[strlen(msyytext)-1] = '\0'; MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_TOKEN_LITERAL_TIME); } @@ -4445,12 +4655,12 @@ YY_RULE_SETUP case 337: /* rule 337 can match eol */ YY_RULE_SETUP -#line 597 "maplexer.l" +#line 596 "maplexer.l" { msyytext++; msyytext[strlen(msyytext)-2] = '\0'; MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_IREGEX); } @@ -4458,62 +4668,57 @@ YY_RULE_SETUP case 338: /* rule 338 can match eol */ YY_RULE_SETUP -#line 606 "maplexer.l" +#line 605 "maplexer.l" { msyytext++; msyytext[strlen(msyytext)-1] = '\0'; MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_REGEX); } YY_BREAK case 339: YY_RULE_SETUP -#line 615 "maplexer.l" +#line 614 "maplexer.l" { msyytext++; msyytext[strlen(msyytext)-1] = '\0'; MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_EXPRESSION); } YY_BREAK case 340: YY_RULE_SETUP -#line 624 "maplexer.l" +#line 623 "maplexer.l" { msyytext++; msyytext[strlen(msyytext)-1] = '\0'; MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_LIST); } YY_BREAK case 341: YY_RULE_SETUP -#line 633 "maplexer.l" +#line 632 "maplexer.l" { msyystring_return_state = MS_STRING; msyystring_begin = msyytext[0]; msyystring_size = 0; - msyystring_buffer_ptr = msyystring_buffer; + msyystring_buffer[0] = '\0'; BEGIN(MSSTRING); } YY_BREAK case 342: YY_RULE_SETUP -#line 641 "maplexer.l" +#line 640 "maplexer.l" { - MS_LEXER_STRING_REALLOC(msyystring_buffer, msyystring_size, - msyystring_buffer_size, msyystring_buffer_ptr); if (msyystring_begin == msyytext[0]) { BEGIN(msyystring_begin_state); - - *msyystring_buffer_ptr = '\0'; - if (msyystring_return_state == MS_STRING) { if (msyystring_icase && strlen(msyytext)==2) { msyystring_icase = MS_FALSE; // reset @@ -4525,49 +4730,50 @@ YY_RULE_SETUP } else { - ++msyystring_size; - *msyystring_buffer_ptr++ = *msyytext; + int old_size = msyystring_size; + msyystring_size += (strlen(msyytext)==2) ? 2 : 1; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyystring_size, + msyystring_buffer_size); + msyystring_buffer[old_size] = *msyytext; if (strlen(msyytext)==2) { - MS_LEXER_STRING_REALLOC(msyystring_buffer, msyystring_size, - msyystring_buffer_size, msyystring_buffer_ptr); - ++msyystring_size; - *msyystring_buffer_ptr++ = msyytext[1]; + msyystring_buffer[old_size+1] = msyytext[1]; } + msyystring_buffer[msyystring_size] = '\0'; } } YY_BREAK case 343: YY_RULE_SETUP -#line 671 "maplexer.l" +#line 666 "maplexer.l" { - MS_LEXER_STRING_REALLOC(msyystring_buffer, msyystring_size, - msyystring_buffer_size, msyystring_buffer_ptr); - ++msyystring_size; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyystring_size, + msyystring_buffer_size); + if (strlen(msyytext) == 2) - *msyystring_buffer_ptr++ = msyytext[1]; + msyystring_buffer[msyystring_size-1] = msyytext[1]; else - *msyystring_buffer_ptr++ = msyytext[0]; + msyystring_buffer[msyystring_size-1] = msyytext[0]; + msyystring_buffer[msyystring_size] = '\0'; } YY_BREAK case 344: /* rule 344 can match eol */ YY_RULE_SETUP -#line 682 "maplexer.l" +#line 678 "maplexer.l" { - char *yptr = msyytext; - while ( *yptr ) { - MS_LEXER_STRING_REALLOC(msyystring_buffer, msyystring_size, - msyystring_buffer_size, msyystring_buffer_ptr); - ++msyystring_size; - *msyystring_buffer_ptr++ = *yptr++; - } + int old_size = msyystring_size; + int msyytext_len = (int)strlen(msyytext); + msyystring_size += msyytext_len; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyystring_size, + msyystring_buffer_size); + memcpy(msyystring_buffer + old_size, msyytext, msyytext_len + 1); } YY_BREAK case 345: /* rule 345 can match eol */ YY_RULE_SETUP -#line 692 "maplexer.l" +#line 687 "maplexer.l" { msyytext++; msyytext[strlen(msyytext)-1] = '\0'; @@ -4596,21 +4802,21 @@ YY_RULE_SETUP YY_BREAK case 346: YY_RULE_SETUP -#line 718 "maplexer.l" +#line 713 "maplexer.l" { msyystring_return_state = MS_TOKEN_LITERAL_STRING; msyystring_begin = msyytext[0]; msyystring_size = 0; - msyystring_buffer_ptr = msyystring_buffer; + msyystring_buffer[0] = '\0'; BEGIN(MSSTRING); } YY_BREAK case 347: YY_RULE_SETUP -#line 726 "maplexer.l" +#line 721 "maplexer.l" { MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_STRING); } @@ -4618,11 +4824,11 @@ YY_RULE_SETUP case 348: /* rule 348 can match eol */ YY_RULE_SETUP -#line 733 "maplexer.l" +#line 728 "maplexer.l" { msyylineno++; } YY_BREAK case YY_STATE_EOF(INITIAL): -#line 735 "maplexer.l" +#line 730 "maplexer.l" { if( --include_stack_ptr < 0 ) return(EOF); /* end of main file */ @@ -4637,32 +4843,32 @@ case YY_STATE_EOF(INITIAL): case 349: /* rule 349 can match eol */ YY_RULE_SETUP -#line 746 "maplexer.l" +#line 741 "maplexer.l" { return(0); } YY_BREAK case 350: YY_RULE_SETUP -#line 750 "maplexer.l" +#line 745 "maplexer.l" { MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(0); } YY_BREAK case 351: YY_RULE_SETUP -#line 756 "maplexer.l" +#line 751 "maplexer.l" { return(msyytext[0]); } YY_BREAK case 352: YY_RULE_SETUP -#line 757 "maplexer.l" +#line 752 "maplexer.l" ECHO; YY_BREAK -#line 4666 "/home/even/mapserver/mapserver/maplexer.c" +#line 4872 "/home/even/mapserver/mapserver/maplexer.c" case YY_STATE_EOF(URL_VARIABLE): case YY_STATE_EOF(URL_STRING): case YY_STATE_EOF(EXPRESSION_STRING): @@ -4684,15 +4890,15 @@ case YY_STATE_EOF(MULTILINE_COMMENT): { /* We're scanning a new file or input source. It's * possible that this happened because the user - * just pointed msyyin at a new source and called - * msyylex(). If so, then we have to assure + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; - YY_CURRENT_BUFFER_LVALUE->yy_input_file = msyyin; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } @@ -4746,11 +4952,11 @@ case YY_STATE_EOF(MULTILINE_COMMENT): { (yy_did_buffer_switch_on_eof) = 0; - if ( msyywrap( ) ) + if ( yywrap( ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up - * msyytext, we can now set up + * yytext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the @@ -4800,7 +5006,7 @@ case YY_STATE_EOF(MULTILINE_COMMENT): } /* end of action switch */ } /* end of scanning one token */ } /* end of user's declarations */ -} /* end of msyylex */ +} /* end of yylex */ /* yy_get_next_buffer - try to read in a new buffer * @@ -4813,7 +5019,7 @@ static int yy_get_next_buffer (void) { char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; char *source = (yytext_ptr); - yy_size_t number_to_move, i; + int number_to_move, i; int ret_val; if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) @@ -4842,7 +5048,7 @@ static int yy_get_next_buffer (void) /* Try to read more data. */ /* First move last chars to start of buffer. */ - number_to_move = (yy_size_t) ((yy_c_buf_p) - (yytext_ptr)) - 1; + number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr) - 1); for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); @@ -4855,7 +5061,7 @@ static int yy_get_next_buffer (void) else { - yy_size_t num_to_read = + int num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) @@ -4869,7 +5075,7 @@ static int yy_get_next_buffer (void) if ( b->yy_is_our_buffer ) { - yy_size_t new_size = b->yy_buf_size * 2; + int new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; @@ -4878,11 +5084,12 @@ static int yy_get_next_buffer (void) b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ - msyyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) ); } else /* Can't grow it, we don't own it. */ - b->yy_ch_buf = 0; + b->yy_ch_buf = NULL; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( @@ -4910,7 +5117,7 @@ static int yy_get_next_buffer (void) if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; - msyyrestart(msyyin ); + yyrestart( yyin ); } else @@ -4924,12 +5131,15 @@ static int yy_get_next_buffer (void) else ret_val = EOB_ACT_CONTINUE_SCAN; - if ((int) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + if (((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { /* Extend the array by 50%, plus the number we really need. */ int new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) msyyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size ); if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); } (yy_n_chars) += number_to_move; @@ -4962,9 +5172,9 @@ static int yy_get_next_buffer (void) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 2038 ) - yy_c = yy_meta[(unsigned int) yy_c]; + yy_c = yy_meta[yy_c]; } - yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; } return yy_current_state; @@ -4990,9 +5200,9 @@ static int yy_get_next_buffer (void) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 2038 ) - yy_c = yy_meta[(unsigned int) yy_c]; + yy_c = yy_meta[yy_c]; } - yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; yy_is_jam = (yy_current_state == 2037); return yy_is_jam ? 0 : yy_current_state; @@ -5006,13 +5216,13 @@ static int yy_get_next_buffer (void) yy_cp = (yy_c_buf_p); - /* undo effects of setting up msyytext */ + /* undo effects of setting up yytext */ *yy_cp = (yy_hold_char); if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) { /* need to shift things up to make room */ /* +2 for EOB chars. */ - yy_size_t number_to_move = (yy_n_chars) + 2; + int number_to_move = (yy_n_chars) + 2; char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; char *source = @@ -5024,7 +5234,7 @@ static int yy_get_next_buffer (void) yy_cp += (int) (dest - source); yy_bp += (int) (dest - source); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = - (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; + (yy_n_chars) = (int) YY_CURRENT_BUFFER_LVALUE->yy_buf_size; if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) YY_FATAL_ERROR( "flex scanner push-back overflow" ); @@ -5063,7 +5273,7 @@ static int yy_get_next_buffer (void) else { /* need more input */ - yy_size_t offset = (yy_c_buf_p) - (yytext_ptr); + int offset = (int) ((yy_c_buf_p) - (yytext_ptr)); ++(yy_c_buf_p); switch ( yy_get_next_buffer( ) ) @@ -5080,14 +5290,14 @@ static int yy_get_next_buffer (void) */ /* Reset buffer status. */ - msyyrestart(msyyin ); + yyrestart( yyin ); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { - if ( msyywrap( ) ) - return EOF; + if ( yywrap( ) ) + return 0; if ( ! (yy_did_buffer_switch_on_eof) ) YY_NEW_FILE; @@ -5106,7 +5316,7 @@ static int yy_get_next_buffer (void) } c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ - *(yy_c_buf_p) = '\0'; /* preserve msyytext */ + *(yy_c_buf_p) = '\0'; /* preserve yytext */ (yy_hold_char) = *++(yy_c_buf_p); return c; @@ -5118,32 +5328,32 @@ static int yy_get_next_buffer (void) * * @note This function does not reset the start condition to @c INITIAL . */ - void msyyrestart (FILE * input_file ) + void yyrestart (FILE * input_file ) { if ( ! YY_CURRENT_BUFFER ){ - msyyensure_buffer_stack (); + yyensure_buffer_stack (); YY_CURRENT_BUFFER_LVALUE = - msyy_create_buffer(msyyin,YY_BUF_SIZE ); + yy_create_buffer( yyin, YY_BUF_SIZE ); } - msyy_init_buffer(YY_CURRENT_BUFFER,input_file ); - msyy_load_buffer_state( ); + yy_init_buffer( YY_CURRENT_BUFFER, input_file ); + yy_load_buffer_state( ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. * */ - void msyy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) { /* TODO. We should be able to replace this entire function body * with - * msyypop_buffer_state(); - * msyypush_buffer_state(new_buffer); + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); */ - msyyensure_buffer_stack (); + yyensure_buffer_stack (); if ( YY_CURRENT_BUFFER == new_buffer ) return; @@ -5156,21 +5366,21 @@ static int yy_get_next_buffer (void) } YY_CURRENT_BUFFER_LVALUE = new_buffer; - msyy_load_buffer_state( ); + yy_load_buffer_state( ); /* We don't actually know whether we did this switch during - * EOF (msyywrap()) processing, but the only time this flag - * is looked at is after msyywrap() is called, so it's safe + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe * to go ahead and always set it. */ (yy_did_buffer_switch_on_eof) = 1; } -static void msyy_load_buffer_state (void) +static void yy_load_buffer_state (void) { (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; - msyyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; (yy_hold_char) = *(yy_c_buf_p); } @@ -5180,35 +5390,35 @@ static void msyy_load_buffer_state (void) * * @return the allocated buffer state. */ - YY_BUFFER_STATE msyy_create_buffer (FILE * file, int size ) + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size ) { YY_BUFFER_STATE b; - b = (YY_BUFFER_STATE) msyyalloc(sizeof( struct yy_buffer_state ) ); + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) ); if ( ! b ) - YY_FATAL_ERROR( "out of dynamic memory in msyy_create_buffer()" ); + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); - b->yy_buf_size = (yy_size_t)size; + b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ - b->yy_ch_buf = (char *) msyyalloc(b->yy_buf_size + 2 ); + b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) ); if ( ! b->yy_ch_buf ) - YY_FATAL_ERROR( "out of dynamic memory in msyy_create_buffer()" ); + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_is_our_buffer = 1; - msyy_init_buffer(b,file ); + yy_init_buffer( b, file ); return b; } /** Destroy the buffer. - * @param b a buffer created with msyy_create_buffer() + * @param b a buffer created with yy_create_buffer() * */ - void msyy_delete_buffer (YY_BUFFER_STATE b ) + void yy_delete_buffer (YY_BUFFER_STATE b ) { if ( ! b ) @@ -5218,27 +5428,27 @@ static void msyy_load_buffer_state (void) YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) - msyyfree((void *) b->yy_ch_buf ); + yyfree( (void *) b->yy_ch_buf ); - msyyfree((void *) b ); + yyfree( (void *) b ); } /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, - * such as during a msyyrestart() or at EOF. + * such as during a yyrestart() or at EOF. */ - static void msyy_init_buffer (YY_BUFFER_STATE b, FILE * file ) + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file ) { int oerrno = errno; - msyy_flush_buffer(b ); + yy_flush_buffer( b ); b->yy_input_file = file; b->yy_fill_buffer = 1; - /* If b is the current buffer, then msyy_init_buffer was _probably_ - * called from msyyrestart() or through yy_get_next_buffer. + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ @@ -5255,7 +5465,7 @@ static void msyy_load_buffer_state (void) * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. * */ - void msyy_flush_buffer (YY_BUFFER_STATE b ) + void yy_flush_buffer (YY_BUFFER_STATE b ) { if ( ! b ) return; @@ -5275,7 +5485,7 @@ static void msyy_load_buffer_state (void) b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) - msyy_load_buffer_state( ); + yy_load_buffer_state( ); } /** Pushes the new state onto the stack. The new state becomes @@ -5284,14 +5494,14 @@ static void msyy_load_buffer_state (void) * @param new_buffer The new state. * */ -void msyypush_buffer_state (YY_BUFFER_STATE new_buffer ) +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ) { if (new_buffer == NULL) return; - msyyensure_buffer_stack(); + yyensure_buffer_stack(); - /* This block is copied from msyy_switch_to_buffer. */ + /* This block is copied from yy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ @@ -5305,8 +5515,8 @@ void msyypush_buffer_state (YY_BUFFER_STATE new_buffer ) (yy_buffer_stack_top)++; YY_CURRENT_BUFFER_LVALUE = new_buffer; - /* copied from msyy_switch_to_buffer. */ - msyy_load_buffer_state( ); + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( ); (yy_did_buffer_switch_on_eof) = 1; } @@ -5314,18 +5524,18 @@ void msyypush_buffer_state (YY_BUFFER_STATE new_buffer ) * The next element becomes the new top. * */ -void msyypop_buffer_state (void) +void yypop_buffer_state (void) { if (!YY_CURRENT_BUFFER) return; - msyy_delete_buffer(YY_CURRENT_BUFFER ); + yy_delete_buffer(YY_CURRENT_BUFFER ); YY_CURRENT_BUFFER_LVALUE = NULL; if ((yy_buffer_stack_top) > 0) --(yy_buffer_stack_top); if (YY_CURRENT_BUFFER) { - msyy_load_buffer_state( ); + yy_load_buffer_state( ); (yy_did_buffer_switch_on_eof) = 1; } } @@ -5333,7 +5543,7 @@ void msyypop_buffer_state (void) /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ -static void msyyensure_buffer_stack (void) +static void yyensure_buffer_stack (void) { yy_size_t num_to_alloc; @@ -5343,15 +5553,15 @@ static void msyyensure_buffer_stack (void) * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ - num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ - (yy_buffer_stack) = (struct yy_buffer_state**)msyyalloc + num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ + (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc (num_to_alloc * sizeof(struct yy_buffer_state*) ); if ( ! (yy_buffer_stack) ) - YY_FATAL_ERROR( "out of dynamic memory in msyyensure_buffer_stack()" ); - + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); - + (yy_buffer_stack_max) = num_to_alloc; (yy_buffer_stack_top) = 0; return; @@ -5363,12 +5573,12 @@ static void msyyensure_buffer_stack (void) yy_size_t grow_size = 8 /* arbitrary grow size */; num_to_alloc = (yy_buffer_stack_max) + grow_size; - (yy_buffer_stack) = (struct yy_buffer_state**)msyyrealloc + (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc ((yy_buffer_stack), num_to_alloc * sizeof(struct yy_buffer_state*) ); if ( ! (yy_buffer_stack) ) - YY_FATAL_ERROR( "out of dynamic memory in msyyensure_buffer_stack()" ); + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); /* zero only the new slots.*/ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); @@ -5380,9 +5590,9 @@ static void msyyensure_buffer_stack (void) * @param base the character buffer * @param size the size in bytes of the character buffer * - * @return the newly allocated buffer state object. + * @return the newly allocated buffer state object. */ -YY_BUFFER_STATE msyy_scan_buffer (char * base, yy_size_t size ) +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size ) { YY_BUFFER_STATE b; @@ -5390,69 +5600,69 @@ YY_BUFFER_STATE msyy_scan_buffer (char * base, yy_size_t size ) base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ - return 0; + return NULL; - b = (YY_BUFFER_STATE) msyyalloc(sizeof( struct yy_buffer_state ) ); + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) ); if ( ! b ) - YY_FATAL_ERROR( "out of dynamic memory in msyy_scan_buffer()" ); + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); - b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; - b->yy_input_file = 0; + b->yy_input_file = NULL; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; - msyy_switch_to_buffer(b ); + yy_switch_to_buffer( b ); return b; } -/** Setup the input buffer state to scan a string. The next call to msyylex() will +/** Setup the input buffer state to scan a string. The next call to yylex() will * scan from a @e copy of @a str. * @param yystr a NUL-terminated string to scan * * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use - * msyy_scan_bytes() instead. + * yy_scan_bytes() instead. */ -YY_BUFFER_STATE msyy_scan_string (yyconst char * yystr ) +YY_BUFFER_STATE yy_scan_string (const char * yystr ) { - return msyy_scan_bytes(yystr,strlen(yystr) ); + return yy_scan_bytes( yystr, (int) strlen(yystr) ); } -/** Setup the input buffer state to scan the given bytes. The next call to msyylex() will +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will * scan from a @e copy of @a bytes. * @param yybytes the byte buffer to scan * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. * * @return the newly allocated buffer state object. */ -YY_BUFFER_STATE msyy_scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len ) +YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len ) { YY_BUFFER_STATE b; char *buf; yy_size_t n; - yy_size_t i; + int i; /* Get memory for full buffer, including space for trailing EOB's. */ - n = _yybytes_len + 2; - buf = (char *) msyyalloc(n ); + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n ); if ( ! buf ) - YY_FATAL_ERROR( "out of dynamic memory in msyy_scan_bytes()" ); + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); for ( i = 0; i < _yybytes_len; ++i ) buf[i] = yybytes[i]; buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; - b = msyy_scan_buffer(buf,n ); + b = yy_scan_buffer( buf, n ); if ( ! b ) - YY_FATAL_ERROR( "bad buffer in msyy_scan_bytes()" ); + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. @@ -5466,9 +5676,9 @@ YY_BUFFER_STATE msyy_scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_le #define YY_EXIT_FAILURE 2 #endif -static void yy_fatal_error (yyconst char* msg ) +static void yynoreturn yy_fatal_error (const char* msg ) { - (void) fprintf( stderr, "%s\n", msg ); + fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } @@ -5478,14 +5688,14 @@ static void yy_fatal_error (yyconst char* msg ) #define yyless(n) \ do \ { \ - /* Undo effects of setting up msyytext. */ \ + /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ - msyytext[msyyleng] = (yy_hold_char); \ - (yy_c_buf_p) = msyytext + yyless_macro_arg; \ + yytext[yyleng] = (yy_hold_char); \ + (yy_c_buf_p) = yytext + yyless_macro_arg; \ (yy_hold_char) = *(yy_c_buf_p); \ *(yy_c_buf_p) = '\0'; \ - msyyleng = yyless_macro_arg; \ + yyleng = yyless_macro_arg; \ } \ while ( 0 ) @@ -5494,126 +5704,126 @@ static void yy_fatal_error (yyconst char* msg ) /** Get the current line number. * */ -int msyyget_lineno (void) +int yyget_lineno (void) { - - return msyylineno; + + return yylineno; } /** Get the input stream. * */ -FILE *msyyget_in (void) +FILE *yyget_in (void) { - return msyyin; + return yyin; } /** Get the output stream. * */ -FILE *msyyget_out (void) +FILE *yyget_out (void) { - return msyyout; + return yyout; } /** Get the length of the current token. * */ -yy_size_t msyyget_leng (void) +int yyget_leng (void) { - return msyyleng; + return yyleng; } /** Get the current token. * */ -char *msyyget_text (void) +char *yyget_text (void) { - return msyytext; + return yytext; } /** Set the current line number. * @param _line_number line number * */ -void msyyset_lineno (int _line_number ) +void yyset_lineno (int _line_number ) { - msyylineno = _line_number; + yylineno = _line_number; } /** Set the input stream. This does not discard the current * input buffer. * @param _in_str A readable stream. * - * @see msyy_switch_to_buffer + * @see yy_switch_to_buffer */ -void msyyset_in (FILE * _in_str ) +void yyset_in (FILE * _in_str ) { - msyyin = _in_str ; + yyin = _in_str ; } -void msyyset_out (FILE * _out_str ) +void yyset_out (FILE * _out_str ) { - msyyout = _out_str ; + yyout = _out_str ; } -int msyyget_debug (void) +int yyget_debug (void) { - return msyy_flex_debug; + return yy_flex_debug; } -void msyyset_debug (int _bdebug ) +void yyset_debug (int _bdebug ) { - msyy_flex_debug = _bdebug ; + yy_flex_debug = _bdebug ; } static int yy_init_globals (void) { /* Initialization is the same as for the non-reentrant scanner. - * This function is called from msyylex_destroy(), so don't allocate here. + * This function is called from yylex_destroy(), so don't allocate here. */ - (yy_buffer_stack) = 0; + (yy_buffer_stack) = NULL; (yy_buffer_stack_top) = 0; (yy_buffer_stack_max) = 0; - (yy_c_buf_p) = (char *) 0; + (yy_c_buf_p) = NULL; (yy_init) = 0; (yy_start) = 0; /* Defined in main.c */ #ifdef YY_STDINIT - msyyin = stdin; - msyyout = stdout; + yyin = stdin; + yyout = stdout; #else - msyyin = (FILE *) 0; - msyyout = (FILE *) 0; + yyin = NULL; + yyout = NULL; #endif /* For future reference: Set errno on error, since we are called by - * msyylex_init() + * yylex_init() */ return 0; } -/* msyylex_destroy is for both reentrant and non-reentrant scanners. */ -int msyylex_destroy (void) +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (void) { /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ - msyy_delete_buffer(YY_CURRENT_BUFFER ); + yy_delete_buffer( YY_CURRENT_BUFFER ); YY_CURRENT_BUFFER_LVALUE = NULL; - msyypop_buffer_state(); + yypop_buffer_state(); } /* Destroy the stack itself. */ - msyyfree((yy_buffer_stack) ); + yyfree((yy_buffer_stack) ); (yy_buffer_stack) = NULL; /* Reset the globals. This is important in a non-reentrant scanner so the next time - * msyylex() is called, initialization will occur. */ + * yylex() is called, initialization will occur. */ yy_init_globals( ); return 0; @@ -5624,7 +5834,7 @@ int msyylex_destroy (void) */ #ifndef yytext_ptr -static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) +static void yy_flex_strncpy (char* s1, const char * s2, int n ) { int i; @@ -5634,7 +5844,7 @@ static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) #endif #ifdef YY_NEED_STRLEN -static int yy_flex_strlen (yyconst char * s ) +static int yy_flex_strlen (const char * s ) { int n; for ( n = 0; s[n]; ++n ) @@ -5644,12 +5854,12 @@ static int yy_flex_strlen (yyconst char * s ) } #endif -void *msyyalloc (yy_size_t size ) +void *yyalloc (yy_size_t size ) { - return (void *) malloc( size ); + return malloc(size); } -void *msyyrealloc (void * ptr, yy_size_t size ) +void *yyrealloc (void * ptr, yy_size_t size ) { /* The cast to (char *) in the following accommodates both @@ -5659,18 +5869,17 @@ void *msyyrealloc (void * ptr, yy_size_t size ) * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ - return (void *) realloc( (char *) ptr, size ); + return realloc(ptr, size); } -void msyyfree (void * ptr ) +void yyfree (void * ptr ) { - free( (char *) ptr ); /* see msyyrealloc() for (char *) cast */ + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" -#line 757 "maplexer.l" - +#line 752 "maplexer.l" /* diff --git a/maplexer.l b/maplexer.l index bbc8bd581c..66b3eeddbd 100644 --- a/maplexer.l +++ b/maplexer.l @@ -46,7 +46,6 @@ double msyynumber; int msyystate=MS_TOKENIZE_DEFAULT; char *msyystring=NULL; char *msyybasepath=NULL; -char *msyystring_buffer_ptr; int msyystring_buffer_size = 0; int msyystring_size; char msyystring_begin; @@ -54,22 +53,21 @@ char *msyystring_buffer = NULL; int msyystring_icase = MS_FALSE; int msyystring_return_state; int msyystring_begin_state; -int msyystring_size_tmp; int msyyreturncomments = 0; -#define MS_LEXER_STRING_REALLOC(string, string_size, max_size, string_ptr) \ - if (string_size >= max_size) { \ - msyystring_size_tmp = max_size; \ - max_size = ((max_size*2) > string_size) ? max_size*2 : string_size+1; \ - string = (char *) msSmallRealloc(string, sizeof(char *) * max_size); \ - string_ptr = string; \ - string_ptr += msyystring_size_tmp; \ - } +#define MS_LEXER_STRING_REALLOC(string, string_size, max_size) \ + do { \ + const int string_size_macro = (int)(string_size); \ + if (string_size_macro >= (int)(max_size)) { \ + max_size = (((int)(max_size)*2) > string_size_macro) ? ((int)(max_size))*2 : string_size_macro+1; \ + string = (char *) msSmallRealloc(string, sizeof(char *) * (max_size)); \ + } \ + } while(0) #define MS_LEXER_RETURN_TOKEN(token) \ MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), \ - msyystring_buffer_size, msyystring_buffer_ptr); \ + msyystring_buffer_size); \ strcpy(msyystring_buffer, msyytext); \ return(token); @@ -506,7 +504,7 @@ char path[MS_MAXPATHLEN]; msyytext++; msyytext[strlen(msyytext)-1] = '\0'; MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyystring_buffer_size); strcpy(msyystring_buffer,msyytext); return(MS_STRING); } @@ -514,7 +512,7 @@ char path[MS_MAXPATHLEN]; msyytext++; msyytext[strlen(msyytext)-1] = '\0'; MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyystring_buffer_size); strcpy(msyystring_buffer,msyytext); msyynumber = atof(msyytext); return(MS_NUMBER); @@ -524,7 +522,7 @@ char path[MS_MAXPATHLEN]; msyytext++; msyytext[strlen(msyytext)-1] = '\0'; MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyystring_buffer_size); strcpy(msyystring_buffer,msyytext); return(MS_BINDING); } @@ -546,7 +544,7 @@ char path[MS_MAXPATHLEN]; msyytext++; msyytext[strlen(msyytext)-1] = '\0'; MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_TOKEN_BINDING_DOUBLE); } @@ -555,7 +553,7 @@ char path[MS_MAXPATHLEN]; msyytext+=2; msyytext[strlen(msyytext)-2] = '\0'; MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_TOKEN_BINDING_STRING); } @@ -564,14 +562,14 @@ char path[MS_MAXPATHLEN]; msyytext+=2; msyytext[strlen(msyytext)-2] = '\0'; MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_TOKEN_BINDING_TIME); } -?[0-9]+|-?[0-9]+\.[0-9]*|-?\.[0-9]*|-?[0-9]+[eE][+-]?[0-9]+|-?[0-9]+\.[0-9]*[eE][+-]?[0-9]+|-?\.[0-9]*[eE][+-]?[0-9]+ { MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyystring_buffer_size); strcpy(msyystring_buffer,msyytext); msyynumber = atof(msyytext); return(MS_NUMBER); @@ -579,7 +577,7 @@ char path[MS_MAXPATHLEN]; -?[0-9]+|-?[0-9]+\.[0-9]*|-?\.[0-9]*|-?[0-9]+[eE][+-]?[0-9]+|-?[0-9]+\.[0-9]*[eE][+-]?[0-9]+|-?\.[0-9]*[eE][+-]?[0-9]+ { MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyystring_buffer_size); strcpy(msyystring_buffer,msyytext); msyynumber = atof(msyytext); return(MS_TOKEN_LITERAL_NUMBER); @@ -589,7 +587,7 @@ char path[MS_MAXPATHLEN]; msyytext++; msyytext[strlen(msyytext)-1] = '\0'; MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_TOKEN_LITERAL_TIME); } @@ -598,7 +596,7 @@ char path[MS_MAXPATHLEN]; msyytext++; msyytext[strlen(msyytext)-2] = '\0'; MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_IREGEX); } @@ -607,7 +605,7 @@ char path[MS_MAXPATHLEN]; msyytext++; msyytext[strlen(msyytext)-1] = '\0'; MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_REGEX); } @@ -616,7 +614,7 @@ char path[MS_MAXPATHLEN]; msyytext++; msyytext[strlen(msyytext)-1] = '\0'; MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_EXPRESSION); } @@ -625,7 +623,7 @@ char path[MS_MAXPATHLEN]; msyytext++; msyytext[strlen(msyytext)-1] = '\0'; MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_LIST); } @@ -634,18 +632,13 @@ char path[MS_MAXPATHLEN]; msyystring_return_state = MS_STRING; msyystring_begin = msyytext[0]; msyystring_size = 0; - msyystring_buffer_ptr = msyystring_buffer; + msyystring_buffer[0] = '\0'; BEGIN(MSSTRING); } \'|\"|\"i|\'i { - MS_LEXER_STRING_REALLOC(msyystring_buffer, msyystring_size, - msyystring_buffer_size, msyystring_buffer_ptr); if (msyystring_begin == msyytext[0]) { BEGIN(msyystring_begin_state); - - *msyystring_buffer_ptr = '\0'; - if (msyystring_return_state == MS_STRING) { if (msyystring_icase && strlen(msyytext)==2) { msyystring_icase = MS_FALSE; // reset @@ -657,36 +650,37 @@ char path[MS_MAXPATHLEN]; } else { - ++msyystring_size; - *msyystring_buffer_ptr++ = *msyytext; + int old_size = msyystring_size; + msyystring_size += (strlen(msyytext)==2) ? 2 : 1; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyystring_size, + msyystring_buffer_size); + msyystring_buffer[old_size] = *msyytext; if (strlen(msyytext)==2) { - MS_LEXER_STRING_REALLOC(msyystring_buffer, msyystring_size, - msyystring_buffer_size, msyystring_buffer_ptr); - ++msyystring_size; - *msyystring_buffer_ptr++ = msyytext[1]; + msyystring_buffer[old_size+1] = msyytext[1]; } + msyystring_buffer[msyystring_size] = '\0'; } } \\\'|\\\"|\\\\|\\ { - MS_LEXER_STRING_REALLOC(msyystring_buffer, msyystring_size, - msyystring_buffer_size, msyystring_buffer_ptr); - ++msyystring_size; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyystring_size, + msyystring_buffer_size); + if (strlen(msyytext) == 2) - *msyystring_buffer_ptr++ = msyytext[1]; + msyystring_buffer[msyystring_size-1] = msyytext[1]; else - *msyystring_buffer_ptr++ = msyytext[0]; + msyystring_buffer[msyystring_size-1] = msyytext[0]; + msyystring_buffer[msyystring_size] = '\0'; } [^\\\'\\\"]+ { - char *yptr = msyytext; - while ( *yptr ) { - MS_LEXER_STRING_REALLOC(msyystring_buffer, msyystring_size, - msyystring_buffer_size, msyystring_buffer_ptr); - ++msyystring_size; - *msyystring_buffer_ptr++ = *yptr++; - } + int old_size = msyystring_size; + int msyytext_len = (int)strlen(msyytext); + msyystring_size += msyytext_len; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyystring_size, + msyystring_buffer_size); + memcpy(msyystring_buffer + old_size, msyytext, msyytext_len + 1); } \"[^\"]*\"|\'[^\']*\' { @@ -719,13 +713,13 @@ char path[MS_MAXPATHLEN]; msyystring_return_state = MS_TOKEN_LITERAL_STRING; msyystring_begin = msyytext[0]; msyystring_size = 0; - msyystring_buffer_ptr = msyystring_buffer; + msyystring_buffer[0] = '\0'; BEGIN(MSSTRING); } [a-z/\.][a-z0-9/\._\-\=]* { MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_STRING); } @@ -749,7 +743,7 @@ char path[MS_MAXPATHLEN]; . { MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(0); } From a3b638d8e257e7e2773ed749fda425537917c272 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sat, 8 Oct 2022 12:59:30 +0200 Subject: [PATCH 122/160] msSHPReadShape(): avoid integer overflow Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=52209 --- mapshape.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/mapshape.c b/mapshape.c index c15b378c7c..5822c2051a 100644 --- a/mapshape.c +++ b/mapshape.c @@ -1377,11 +1377,12 @@ void msSHPReadShape( SHPHandle psSHP, int hEntity, shapeObj *shape ) const ms_int32 end = i == nParts - 1 ? nPoints : psSHP->panParts[i+1]; - shape->line[i].numpoints = end - psSHP->panParts[i]; if (psSHP->panParts[i] < 0 || end < 0 || end > nPoints || psSHP->panParts[i] >= end) { - msSetError(MS_SHPERR, "Corrupted .shp file : shape %d, shape->line[%d].numpoints=%d", "msSHPReadShape()", - hEntity, i, shape->line[i].numpoints); + msSetError(MS_SHPERR, "Corrupted .shp file : shape %d, shape->line[%d].start=%d, shape->line[%d].end=%d", "msSHPReadShape()", + hEntity, + i, psSHP->panParts[i], + i, end); while(--i >= 0) free(shape->line[i].point); free(shape->line); @@ -1391,6 +1392,7 @@ void msSHPReadShape( SHPHandle psSHP, int hEntity, shapeObj *shape ) return; } + shape->line[i].numpoints = end - psSHP->panParts[i]; if( (shape->line[i].point = (pointObj *)malloc(sizeof(pointObj)*shape->line[i].numpoints)) == NULL ) { while(--i >= 0) free(shape->line[i].point); From ee04ff31a01b57533a3f7c31b9a431fb1d865f6f Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sat, 8 Oct 2022 13:43:02 +0200 Subject: [PATCH 123/160] msOGRShapeFromWKT(): fix memleak in error code path Likely fix for https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=52223 (couldn't reproduce the leak with the reproducer and current main, but this commit does fix a leak) --- mapogr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapogr.cpp b/mapogr.cpp index 4a618b6fa5..8743707b26 100644 --- a/mapogr.cpp +++ b/mapogr.cpp @@ -5485,7 +5485,7 @@ shapeObj *msOGRShapeFromWKT(const char *string) wkbFlatten(OGR_G_GetGeometryType(hGeom)) ) == MS_FAILURE ) { free( shape ); - return NULL; + shape = NULL; } OGR_G_DestroyGeometry( hGeom ); From 907cd5d093356c8b6250e40f722a2dda1f710f97 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sun, 9 Oct 2022 13:29:03 +0200 Subject: [PATCH 124/160] loadProjection(): fix memleak in case of repeated PROJECTION block Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=52252 --- mapfile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapfile.c b/mapfile.c index ab955ce2b8..ee9f8e84b4 100644 --- a/mapfile.c +++ b/mapfile.c @@ -1142,7 +1142,7 @@ static int loadProjection(projectionObj *p) p->gt.need_geotransform = MS_FALSE; - if ( p->proj != NULL ) { + if ( p->proj != NULL || p->numargs != 0 ) { msSetError(MS_MISCERR, "Projection is already initialized. Multiple projection definitions are not allowed in this object. (line %d)", "loadProjection()", msyylineno); return(-1); From f928d003f8c3826712d6948b30cf1146a1087d7b Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sun, 9 Oct 2022 13:33:53 +0200 Subject: [PATCH 125/160] msIsAxisInverted(): avoid undefined-shift on invalid code Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=52259 --- mapproject.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mapproject.c b/mapproject.c index 1bbd90cb1c..aa1a0cff3c 100644 --- a/mapproject.c +++ b/mapproject.c @@ -975,6 +975,8 @@ int msProcessProjection(projectionObj *p) /************************************************************************/ int msIsAxisInverted(int epsg_code) { + if( epsg_code < 0 ) + return MS_FALSE; const unsigned int row = epsg_code / 8; const unsigned char index = epsg_code % 8; From c2311a0b10a854c11425a91dd4c488ea78af6cb2 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sun, 9 Oct 2022 22:44:47 +0200 Subject: [PATCH 126/160] loadSymbol(): fix potential memory leak Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=52272 --- mapsymbol.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mapsymbol.c b/mapsymbol.c index ed3bd9dd4a..be8d12645f 100644 --- a/mapsymbol.c +++ b/mapsymbol.c @@ -230,8 +230,10 @@ int loadSymbol(symbolObj *s, char *symbolpath) msSetError(MS_TYPEERR, "Parsing error near (%s):(line %d)", "loadSymbol()", msyystring_buffer, msyylineno); return(-1); } + msFree(s->full_pixmap_path); s->full_pixmap_path = msStrdup(msBuildPath(szPath, symbolpath, msyystring_buffer)); /* Set imagepath */ + msFree(s->imagepath); s->imagepath = msStrdup(msyystring_buffer); break; case(NAME): From 10e05873db3d71abdde181b469cf8739bf52f941 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 11 Oct 2022 12:49:57 +0200 Subject: [PATCH 127/160] maplexer.l: fix heap-buffer-overflow issues with NUL characters Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=52305 --- maplexer.c | 89 +++++++++++++++++++++++++++--------------------------- maplexer.l | 67 ++++++++++++++++++++-------------------- 2 files changed, 77 insertions(+), 79 deletions(-) diff --git a/maplexer.c b/maplexer.c index 7ee518baac..4904a0b035 100644 --- a/maplexer.c +++ b/maplexer.c @@ -4518,8 +4518,8 @@ YY_RULE_SETUP #line 504 "maplexer.l" { msyytext++; - msyytext[strlen(msyytext)-1] = '\0'; - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, msyystring_buffer_size); strcpy(msyystring_buffer,msyytext); return(MS_STRING); @@ -4530,8 +4530,8 @@ YY_RULE_SETUP #line 512 "maplexer.l" { msyytext++; - msyytext[strlen(msyytext)-1] = '\0'; - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, msyystring_buffer_size); strcpy(msyystring_buffer,msyytext); msyynumber = atof(msyytext); @@ -4544,8 +4544,8 @@ YY_RULE_SETUP #line 522 "maplexer.l" { msyytext++; - msyytext[strlen(msyytext)-1] = '\0'; - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, msyystring_buffer_size); strcpy(msyystring_buffer,msyytext); return(MS_BINDING); @@ -4582,8 +4582,8 @@ YY_RULE_SETUP { /* attribute binding - numeric (no quotes) */ msyytext++; - msyytext[strlen(msyytext)-1] = '\0'; - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_TOKEN_BINDING_DOUBLE); @@ -4596,8 +4596,8 @@ YY_RULE_SETUP { /* attribute binding - string (single or double quotes) */ msyytext+=2; - msyytext[strlen(msyytext)-2] = '\0'; - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), + msyytext[msyyleng-2-2] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_TOKEN_BINDING_STRING); @@ -4610,8 +4610,8 @@ YY_RULE_SETUP { /* attribute binding - time */ msyytext+=2; - msyytext[strlen(msyytext)-2] = '\0'; - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), + msyytext[msyyleng-2-2] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_TOKEN_BINDING_TIME); @@ -4621,7 +4621,7 @@ case 334: YY_RULE_SETUP #line 571 "maplexer.l" { - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, msyystring_buffer_size); strcpy(msyystring_buffer,msyytext); msyynumber = atof(msyytext); @@ -4632,7 +4632,7 @@ case 335: YY_RULE_SETUP #line 579 "maplexer.l" { - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, msyystring_buffer_size); strcpy(msyystring_buffer,msyytext); msyynumber = atof(msyytext); @@ -4645,8 +4645,8 @@ YY_RULE_SETUP #line 587 "maplexer.l" { msyytext++; - msyytext[strlen(msyytext)-1] = '\0'; - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_TOKEN_LITERAL_TIME); @@ -4658,8 +4658,8 @@ YY_RULE_SETUP #line 596 "maplexer.l" { msyytext++; - msyytext[strlen(msyytext)-2] = '\0'; - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), + msyytext[msyyleng-1-2] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_IREGEX); @@ -4671,8 +4671,8 @@ YY_RULE_SETUP #line 605 "maplexer.l" { msyytext++; - msyytext[strlen(msyytext)-1] = '\0'; - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_REGEX); @@ -4683,8 +4683,8 @@ YY_RULE_SETUP #line 614 "maplexer.l" { msyytext++; - msyytext[strlen(msyytext)-1] = '\0'; - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_EXPRESSION); @@ -4695,8 +4695,8 @@ YY_RULE_SETUP #line 623 "maplexer.l" { msyytext++; - msyytext[strlen(msyytext)-1] = '\0'; - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_LIST); @@ -4720,7 +4720,7 @@ YY_RULE_SETUP if (msyystring_begin == msyytext[0]) { BEGIN(msyystring_begin_state); if (msyystring_return_state == MS_STRING) { - if (msyystring_icase && strlen(msyytext)==2) { + if (msyystring_icase && msyyleng==2) { msyystring_icase = MS_FALSE; // reset return MS_ISTRING; } else @@ -4731,11 +4731,11 @@ YY_RULE_SETUP } else { int old_size = msyystring_size; - msyystring_size += (strlen(msyytext)==2) ? 2 : 1; + msyystring_size += (msyyleng==2) ? 2 : 1; MS_LEXER_STRING_REALLOC(msyystring_buffer, msyystring_size, msyystring_buffer_size); msyystring_buffer[old_size] = *msyytext; - if (strlen(msyytext)==2) { + if (msyyleng==2) { msyystring_buffer[old_size+1] = msyytext[1]; } msyystring_buffer[msyystring_size] = '\0'; @@ -4750,7 +4750,7 @@ YY_RULE_SETUP MS_LEXER_STRING_REALLOC(msyystring_buffer, msyystring_size, msyystring_buffer_size); - if (strlen(msyytext) == 2) + if (msyyleng == 2) msyystring_buffer[msyystring_size-1] = msyytext[1]; else msyystring_buffer[msyystring_size-1] = msyytext[0]; @@ -4763,20 +4763,19 @@ YY_RULE_SETUP #line 678 "maplexer.l" { int old_size = msyystring_size; - int msyytext_len = (int)strlen(msyytext); - msyystring_size += msyytext_len; + msyystring_size += msyyleng; MS_LEXER_STRING_REALLOC(msyystring_buffer, msyystring_size, msyystring_buffer_size); - memcpy(msyystring_buffer + old_size, msyytext, msyytext_len + 1); + memcpy(msyystring_buffer + old_size, msyytext, msyyleng + 1); } YY_BREAK case 345: /* rule 345 can match eol */ YY_RULE_SETUP -#line 687 "maplexer.l" +#line 686 "maplexer.l" { msyytext++; - msyytext[strlen(msyytext)-1] = '\0'; + msyytext[msyyleng-1-1] = '\0'; if(include_stack_ptr >= MAX_INCLUDE_DEPTH) { msSetError(MS_IOERR, "Includes nested to deeply.", "msyylex()"); @@ -4802,7 +4801,7 @@ YY_RULE_SETUP YY_BREAK case 346: YY_RULE_SETUP -#line 713 "maplexer.l" +#line 712 "maplexer.l" { msyystring_return_state = MS_TOKEN_LITERAL_STRING; msyystring_begin = msyytext[0]; @@ -4813,9 +4812,9 @@ YY_RULE_SETUP YY_BREAK case 347: YY_RULE_SETUP -#line 721 "maplexer.l" +#line 720 "maplexer.l" { - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_STRING); @@ -4824,11 +4823,11 @@ YY_RULE_SETUP case 348: /* rule 348 can match eol */ YY_RULE_SETUP -#line 728 "maplexer.l" +#line 727 "maplexer.l" { msyylineno++; } YY_BREAK case YY_STATE_EOF(INITIAL): -#line 730 "maplexer.l" +#line 729 "maplexer.l" { if( --include_stack_ptr < 0 ) return(EOF); /* end of main file */ @@ -4843,16 +4842,16 @@ case YY_STATE_EOF(INITIAL): case 349: /* rule 349 can match eol */ YY_RULE_SETUP -#line 741 "maplexer.l" +#line 740 "maplexer.l" { return(0); } YY_BREAK case 350: YY_RULE_SETUP -#line 745 "maplexer.l" +#line 744 "maplexer.l" { - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(0); @@ -4860,15 +4859,15 @@ YY_RULE_SETUP YY_BREAK case 351: YY_RULE_SETUP -#line 751 "maplexer.l" +#line 750 "maplexer.l" { return(msyytext[0]); } YY_BREAK case 352: YY_RULE_SETUP -#line 752 "maplexer.l" +#line 751 "maplexer.l" ECHO; YY_BREAK -#line 4872 "/home/even/mapserver/mapserver/maplexer.c" +#line 4871 "/home/even/mapserver/mapserver/maplexer.c" case YY_STATE_EOF(URL_VARIABLE): case YY_STATE_EOF(URL_STRING): case YY_STATE_EOF(EXPRESSION_STRING): @@ -5879,7 +5878,7 @@ void yyfree (void * ptr ) #define YYTABLES_NAME "yytables" -#line 752 "maplexer.l" +#line 751 "maplexer.l" /* diff --git a/maplexer.l b/maplexer.l index 66b3eeddbd..f502565b83 100644 --- a/maplexer.l +++ b/maplexer.l @@ -502,16 +502,16 @@ char path[MS_MAXPATHLEN]; \[[a-z/\.][a-z0-9/:\.\-\=_ ]*\] { msyytext++; - msyytext[strlen(msyytext)-1] = '\0'; - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, msyystring_buffer_size); strcpy(msyystring_buffer,msyytext); return(MS_STRING); } \[[0-9]*\] { msyytext++; - msyytext[strlen(msyytext)-1] = '\0'; - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, msyystring_buffer_size); strcpy(msyystring_buffer,msyytext); msyynumber = atof(msyytext); @@ -520,8 +520,8 @@ char path[MS_MAXPATHLEN]; \[[^\]]*\] { msyytext++; - msyytext[strlen(msyytext)-1] = '\0'; - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, msyystring_buffer_size); strcpy(msyystring_buffer,msyytext); return(MS_BINDING); @@ -542,8 +542,8 @@ char path[MS_MAXPATHLEN]; \[[^\]]*\] { /* attribute binding - numeric (no quotes) */ msyytext++; - msyytext[strlen(msyytext)-1] = '\0'; - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_TOKEN_BINDING_DOUBLE); @@ -551,8 +551,8 @@ char path[MS_MAXPATHLEN]; \"\[[^\"]*\]\"|\'\[[^\']*\]\' { /* attribute binding - string (single or double quotes) */ msyytext+=2; - msyytext[strlen(msyytext)-2] = '\0'; - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), + msyytext[msyyleng-2-2] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_TOKEN_BINDING_STRING); @@ -560,15 +560,15 @@ char path[MS_MAXPATHLEN]; \`\[[^\`]*\]\` { /* attribute binding - time */ msyytext+=2; - msyytext[strlen(msyytext)-2] = '\0'; - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), + msyytext[msyyleng-2-2] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_TOKEN_BINDING_TIME); } -?[0-9]+|-?[0-9]+\.[0-9]*|-?\.[0-9]*|-?[0-9]+[eE][+-]?[0-9]+|-?[0-9]+\.[0-9]*[eE][+-]?[0-9]+|-?\.[0-9]*[eE][+-]?[0-9]+ { - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, msyystring_buffer_size); strcpy(msyystring_buffer,msyytext); msyynumber = atof(msyytext); @@ -576,7 +576,7 @@ char path[MS_MAXPATHLEN]; } -?[0-9]+|-?[0-9]+\.[0-9]*|-?\.[0-9]*|-?[0-9]+[eE][+-]?[0-9]+|-?[0-9]+\.[0-9]*[eE][+-]?[0-9]+|-?\.[0-9]*[eE][+-]?[0-9]+ { - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, msyystring_buffer_size); strcpy(msyystring_buffer,msyytext); msyynumber = atof(msyytext); @@ -585,8 +585,8 @@ char path[MS_MAXPATHLEN]; \`[^\`]*\` { msyytext++; - msyytext[strlen(msyytext)-1] = '\0'; - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_TOKEN_LITERAL_TIME); @@ -594,8 +594,8 @@ char path[MS_MAXPATHLEN]; \/[^*]{1}[^\/]*\/i { msyytext++; - msyytext[strlen(msyytext)-2] = '\0'; - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), + msyytext[msyyleng-1-2] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_IREGEX); @@ -603,8 +603,8 @@ char path[MS_MAXPATHLEN]; \/[^*]{1}[^\/]*\/ { msyytext++; - msyytext[strlen(msyytext)-1] = '\0'; - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_REGEX); @@ -612,8 +612,8 @@ char path[MS_MAXPATHLEN]; \(.*\) { msyytext++; - msyytext[strlen(msyytext)-1] = '\0'; - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_EXPRESSION); @@ -621,8 +621,8 @@ char path[MS_MAXPATHLEN]; \{.*\} { msyytext++; - msyytext[strlen(msyytext)-1] = '\0'; - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_LIST); @@ -640,7 +640,7 @@ char path[MS_MAXPATHLEN]; if (msyystring_begin == msyytext[0]) { BEGIN(msyystring_begin_state); if (msyystring_return_state == MS_STRING) { - if (msyystring_icase && strlen(msyytext)==2) { + if (msyystring_icase && msyyleng==2) { msyystring_icase = MS_FALSE; // reset return MS_ISTRING; } else @@ -651,11 +651,11 @@ char path[MS_MAXPATHLEN]; } else { int old_size = msyystring_size; - msyystring_size += (strlen(msyytext)==2) ? 2 : 1; + msyystring_size += (msyyleng==2) ? 2 : 1; MS_LEXER_STRING_REALLOC(msyystring_buffer, msyystring_size, msyystring_buffer_size); msyystring_buffer[old_size] = *msyytext; - if (strlen(msyytext)==2) { + if (msyyleng==2) { msyystring_buffer[old_size+1] = msyytext[1]; } msyystring_buffer[msyystring_size] = '\0'; @@ -667,7 +667,7 @@ char path[MS_MAXPATHLEN]; MS_LEXER_STRING_REALLOC(msyystring_buffer, msyystring_size, msyystring_buffer_size); - if (strlen(msyytext) == 2) + if (msyyleng == 2) msyystring_buffer[msyystring_size-1] = msyytext[1]; else msyystring_buffer[msyystring_size-1] = msyytext[0]; @@ -676,16 +676,15 @@ char path[MS_MAXPATHLEN]; [^\\\'\\\"]+ { int old_size = msyystring_size; - int msyytext_len = (int)strlen(msyytext); - msyystring_size += msyytext_len; + msyystring_size += msyyleng; MS_LEXER_STRING_REALLOC(msyystring_buffer, msyystring_size, msyystring_buffer_size); - memcpy(msyystring_buffer + old_size, msyytext, msyytext_len + 1); + memcpy(msyystring_buffer + old_size, msyytext, msyyleng + 1); } \"[^\"]*\"|\'[^\']*\' { msyytext++; - msyytext[strlen(msyytext)-1] = '\0'; + msyytext[msyyleng-1-1] = '\0'; if(include_stack_ptr >= MAX_INCLUDE_DEPTH) { msSetError(MS_IOERR, "Includes nested to deeply.", "msyylex()"); @@ -718,7 +717,7 @@ char path[MS_MAXPATHLEN]; } [a-z/\.][a-z0-9/\._\-\=]* { - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_STRING); @@ -742,7 +741,7 @@ char path[MS_MAXPATHLEN]; } . { - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(0); From a23fee703960ab5469e791b19ce4bf82c1ac7cb1 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Thu, 13 Oct 2022 15:00:36 +0200 Subject: [PATCH 128/160] msOGRShapeFromWKT(): fix memory leak in error code path Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=52363 --- mapogr.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/mapogr.cpp b/mapogr.cpp index 8743707b26..686bad4723 100644 --- a/mapogr.cpp +++ b/mapogr.cpp @@ -5484,6 +5484,7 @@ shapeObj *msOGRShapeFromWKT(const char *string) if( msOGRGeometryToShape( hGeom, shape, wkbFlatten(OGR_G_GetGeometryType(hGeom)) ) == MS_FAILURE ) { + msFreeShape(shape); free( shape ); shape = NULL; } From 8f0e2b71bef50242a28fd41c175c0715482b1a05 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Thu, 13 Oct 2022 20:17:32 +0200 Subject: [PATCH 129/160] loadLeader(): fix memleak in error code path Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=52376 --- mapfile.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mapfile.c b/mapfile.c index ee9f8e84b4..baea83efe0 100644 --- a/mapfile.c +++ b/mapfile.c @@ -1529,7 +1529,13 @@ static int loadLeader(labelLeaderObj *leader) if(msGrowLeaderStyles(leader) == NULL) return(-1); initStyle(leader->styles[leader->numstyles]); - if(loadStyle(leader->styles[leader->numstyles]) != MS_SUCCESS) return(-1); + if(loadStyle(leader->styles[leader->numstyles]) != MS_SUCCESS) + { + freeStyle(leader->styles[leader->numstyles]); + free(leader->styles[leader->numstyles]); + leader->styles[leader->numstyles] = NULL; + return -1; + } leader->numstyles++; break; default: From 3ce5407b4475c3bddb9323addf553b8f6e7687fa Mon Sep 17 00:00:00 2001 From: Steve Lime Date: Fri, 14 Oct 2022 23:09:49 -0500 Subject: [PATCH 130/160] Make sure a POINT block doesn't contain too many points. --- mapsymbol.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mapsymbol.c b/mapsymbol.c index be8d12645f..a13e928461 100644 --- a/mapsymbol.c +++ b/mapsymbol.c @@ -249,6 +249,10 @@ int loadSymbol(symbolObj *s, char *symbolpath) done = MS_TRUE; break; case(MS_NUMBER): + if(s->numpoints == MS_MAXVECTORPOINTS) { + msSetError(MS_SYMERR, "POINT block contains too many points.", "loadSymbol()"); + return(-1); + } s->points[s->numpoints].x = atof(msyystring_buffer); /* grab the x */ if(getDouble(&(s->points[s->numpoints].y)) == -1) return(-1); /* grab the y */ if(s->points[s->numpoints].x!=-99) { From 00a3cbc526fa84cce16115bebdd43460ae43d284 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sun, 16 Oct 2022 21:56:11 +0200 Subject: [PATCH 131/160] loadProjection(): fix memleak in error code path Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=52472 --- mapfile.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/mapfile.c b/mapfile.c index baea83efe0..ea080aae71 100644 --- a/mapfile.c +++ b/mapfile.c @@ -1138,8 +1138,6 @@ static void writeGrid(FILE *stream, int indent, graticuleObj *pGraticule) static int loadProjection(projectionObj *p) { - int i=0; - p->gt.need_geotransform = MS_FALSE; if ( p->proj != NULL || p->numargs != 0 ) { @@ -1154,16 +1152,16 @@ static int loadProjection(projectionObj *p) msSetError(MS_EOFERR, NULL, "loadProjection()"); return(-1); case(END): - if( i == 1 && strstr(p->args[0],"+") != NULL ) { + if( p->numargs == 1 && strstr(p->args[0],"+") != NULL ) { char *one_line_def = p->args[0]; int result; p->args[0] = NULL; + p->numargs = 0; result = msLoadProjectionString( p, one_line_def ); free( one_line_def ); return result; } else { - p->numargs = i; if(p->numargs != 0) return msProcessProjection(p); else @@ -1172,15 +1170,14 @@ static int loadProjection(projectionObj *p) break; case(MS_STRING): case(MS_AUTO): - if( i == MS_MAXPROJARGS ) { + if( p->numargs == MS_MAXPROJARGS ) { msSetError(MS_MISCERR, "Parsing error near (%s):(line %d): Too many arguments in projection string", "loadProjection()", msyystring_buffer, msyylineno); - p->numargs = i; return -1; } - p->args[i] = msStrdup(msyystring_buffer); + p->args[p->numargs] = msStrdup(msyystring_buffer); p->automatic = MS_TRUE; - i++; + p->numargs++; break; default: msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)", "loadProjection()", From faa697ba55bc75254cbf044248b3e64d4e515102 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sun, 23 Oct 2022 21:38:18 +0200 Subject: [PATCH 132/160] loadGrid(): fix memleak on LABELFORMAT DD Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=52670 --- mapfile.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mapfile.c b/mapfile.c index ea080aae71..0c9f5897b8 100644 --- a/mapfile.c +++ b/mapfile.c @@ -1083,8 +1083,10 @@ static int loadGrid( layerObj *pLayer ) break; /* for string loads */ case( LABELFORMAT ): if(getString(&(pLayer->grid->labelformat)) == MS_FAILURE) { - if(strcasecmp(msyystring_buffer, "DD") == 0) /* DD triggers a symbol to be returned instead of a string so check for this special case */ + if(strcasecmp(msyystring_buffer, "DD") == 0) /* DD triggers a symbol to be returned instead of a string so check for this special case */ { + msFree(pLayer->grid->labelformat); pLayer->grid->labelformat = msStrdup("DD"); + } else return(-1); } From 8a6f06720082aecf3111577a16c986ab0fd9973b Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 25 Oct 2022 20:28:17 +0200 Subject: [PATCH 133/160] loadLayer(): fix memory leak in error code path Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=52702 --- mapfile.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mapfile.c b/mapfile.c index 0c9f5897b8..00d2e4c35c 100644 --- a/mapfile.c +++ b/mapfile.c @@ -4322,7 +4322,10 @@ int loadLayer(layerObj *layer, mapObj *map) return(-1); } - if(loadJoin(&(layer->joins[layer->numjoins])) == -1) return(-1); + if(loadJoin(&(layer->joins[layer->numjoins])) == -1) { + freeJoin(&(layer->joins[layer->numjoins])); + return(-1); + } layer->numjoins++; break; case(LABELCACHE): From 65210a5a963996c9526d0cd20ce4012b7fa51519 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 2 Nov 2022 10:29:38 +0100 Subject: [PATCH 134/160] freeLabelLeader(): fix memleak (including in nominal code path) Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=52947 --- mapfile.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mapfile.c b/mapfile.c index 00d2e4c35c..29afbaf586 100644 --- a/mapfile.c +++ b/mapfile.c @@ -1465,7 +1465,9 @@ int freeLabelLeader(labelLeaderObj *leader) { int i; for(i=0; inumstyles; i++) { - msFree(leader->styles[i]); + if(freeStyle(leader->styles[i]) == MS_SUCCESS) { + msFree(leader->styles[i]); + } } msFree(leader->styles); From 57b02009e664f119c83b67b993637bef6e5704c2 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Fri, 18 Nov 2022 00:49:00 +0100 Subject: [PATCH 135/160] loadLayer(): fix memory leak in case of repeated CLUSTER Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=53471 --- mapfile.c | 1 - 1 file changed, 1 deletion(-) diff --git a/mapfile.c b/mapfile.c index 29afbaf586..d4e6f5d4d9 100644 --- a/mapfile.c +++ b/mapfile.c @@ -4126,7 +4126,6 @@ int loadLayer(layerObj *layer, mapObj *map) layer->numclasses++; break; case(CLUSTER): - initCluster(&layer->cluster); if(loadCluster(&layer->cluster) == -1) return(-1); break; case(CLASSGROUP): From c1ec025b3dbcfde6228fd972c8f50a13cb48bd61 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Thu, 15 Dec 2022 18:18:58 +0100 Subject: [PATCH 136/160] Fix usage of FILE* and VSILFILE* In GDAL 3.7dev, VSILFILE* is no longer an alias for FILE* (see https://github.com/OSGeo/gdal/pull/6883), so we have to adjust our usage to be correct. This MapServer change is backward compatible with GDAL < 3.7 too --- mapcontext.c | 2 +- mapgdal.c | 2 +- mapogroutput.cpp | 6 +++--- mapwcs11.c | 2 +- mapwcs20.c | 2 +- mapwmslayer.c | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/mapcontext.c b/mapcontext.c index aadc922d2a..774c6ba43e 100644 --- a/mapcontext.c +++ b/mapcontext.c @@ -1277,7 +1277,7 @@ int msLoadMapContext(mapObj *map, char *filename, int unique_layer_names) int msSaveMapContext(mapObj *map, char *filename) { #if defined(USE_WMS_LYR) - VSILFILE *stream; + FILE *stream; char szPath[MS_MAXPATHLEN]; int nStatus; diff --git a/mapgdal.c b/mapgdal.c index a4641ac075..53e526f7d6 100644 --- a/mapgdal.c +++ b/mapgdal.c @@ -482,7 +482,7 @@ int msSaveImageGDAL( mapObj *map, imageObj *image, const char *filenameIn ) /* stdout and delete the file. */ /* -------------------------------------------------------------------- */ if( bFileIsTemporary ) { - FILE *fp; + VSILFILE *fp; unsigned char block[4000]; int bytes_read; diff --git a/mapogroutput.cpp b/mapogroutput.cpp index 11b977c24f..117cb6db89 100644 --- a/mapogroutput.cpp +++ b/mapogroutput.cpp @@ -1222,7 +1222,7 @@ int msOGRWriteFromQuery( mapObj *map, outputFormatObj *format, int sendheaders ) else if( EQUAL(form,"simple") ) { char buffer[1024]; int bytes_read; - FILE *fp; + VSILFILE *fp; const char *jsonp; jsonp = msGetOutputFormatOption( format, "JSONP", NULL ); @@ -1270,7 +1270,7 @@ int msOGRWriteFromQuery( mapObj *map, outputFormatObj *format, int sendheaders ) CSLDestroy(papszAdditionalFiles); for( i = 0; file_list != NULL && file_list[i] != NULL; i++ ) { - FILE *fp; + VSILFILE *fp; int bytes_read; char buffer[1024]; @@ -1307,7 +1307,7 @@ int msOGRWriteFromQuery( mapObj *map, outputFormatObj *format, int sendheaders ) /* Handle the case of a zip file result. */ /* -------------------------------------------------------------------- */ else if( EQUAL(form,"zip") ) { - FILE *fp; + VSILFILE *fp; char *zip_filename = msTmpFile(map, NULL, "/vsimem/ogrzip/", "zip" ); void *hZip; int bytes_read; diff --git a/mapwcs11.c b/mapwcs11.c index 2bfa73118e..9c5943cfcd 100644 --- a/mapwcs11.c +++ b/mapwcs11.c @@ -1303,7 +1303,7 @@ int msWCSReturnCoverage11( wcsParamsObj *params, mapObj *map, for( i = 0; i < count; i++ ) { const char *mimetype = NULL; - FILE *fp; + VSILFILE *fp; unsigned char block[4000]; int bytes_read; diff --git a/mapwcs20.c b/mapwcs20.c index 2732a744f6..1106e6a28e 100644 --- a/mapwcs20.c +++ b/mapwcs20.c @@ -2446,7 +2446,7 @@ static int msWCSWriteFile20(mapObj* map, imageObj* image, wcs20ParamsObjPtr para for( i = 0; i < count; i++ ) { const char *mimetype = NULL; - FILE *fp; + VSILFILE *fp; unsigned char block[4000]; int bytes_read; diff --git a/mapwmslayer.c b/mapwmslayer.c index c39f807578..b3fb6288f4 100755 --- a/mapwmslayer.c +++ b/mapwmslayer.c @@ -1468,7 +1468,7 @@ int msDrawWMSLayerLow(int nLayerId, httpRequestObj *pasReqInfo, if (msDrawLayer(map, lp, img) != 0) status = MS_FAILURE; } else { - FILE *fp; + VSILFILE *fp; char *wldfile; /* OK, we have to resample the raster to map projection... */ lp->transform = MS_TRUE; From 1ab19e72b05aa768d81159c086655660c14830e2 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Fri, 30 Dec 2022 15:48:54 +0100 Subject: [PATCH 137/160] msCGILoadMap(): do not load file pointed by CONTEXT= unless it validates new MS_CONTEXT_PATTERN configuration option (and doesn't validate MS_CONTEXT_BAD_PATTERN) (fixes #6779) --- mapcontext.c | 6 +++--- mapows.h | 2 +- mapservutil.c | 26 ++++++++++++++++++++++++-- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/mapcontext.c b/mapcontext.c index 774c6ba43e..144156fc41 100644 --- a/mapcontext.c +++ b/mapcontext.c @@ -664,7 +664,7 @@ int msLoadMapContextLayerDimension(CPLXMLNode *psDimension, layerObj *layer) */ int msLoadMapContextGeneral(mapObj *map, CPLXMLNode *psGeneral, CPLXMLNode *psMapContext, int nVersion, - char *filename) + const char *filename) { char *pszProj=NULL; @@ -809,7 +809,7 @@ int msLoadMapContextGeneral(mapObj *map, CPLXMLNode *psGeneral, ** Load a Layer block from a MapContext document */ int msLoadMapContextLayer(mapObj *map, CPLXMLNode *psLayer, int nVersion, - char *filename, int unique_layer_names) + const char *filename, int unique_layer_names) { char *pszValue; const char *pszHash; @@ -1106,7 +1106,7 @@ int msLoadMapContextURL(mapObj *map, char *urlfilename, int unique_layer_names) ** (eg l:1:park. l:2:road ...). If It is set to MS_FALSE, the layer name ** would be the same name as the layer name in the context */ -int msLoadMapContext(mapObj *map, char *filename, int unique_layer_names) +int msLoadMapContext(mapObj *map, const char *filename, int unique_layer_names) { #if defined(USE_WMS_LYR) char *pszWholeText, *pszValue; diff --git a/mapows.h b/mapows.h index 6c5bdc8d17..632a9082f6 100644 --- a/mapows.h +++ b/mapows.h @@ -551,7 +551,7 @@ MS_DLL_EXPORT char *msWFSExecuteGetFeature(layerObj *lp); MS_DLL_EXPORT int msWriteMapContext(mapObj *map, FILE *stream); MS_DLL_EXPORT int msSaveMapContext(mapObj *map, char *filename); -MS_DLL_EXPORT int msLoadMapContext(mapObj *map, char *filename, int unique_layer_names); +MS_DLL_EXPORT int msLoadMapContext(mapObj *map, const char *filename, int unique_layer_names); MS_DLL_EXPORT int msLoadMapContextURL(mapObj *map, char *urlfilename, int unique_layer_names); diff --git a/mapservutil.c b/mapservutil.c index bee4250297..0246aebf35 100644 --- a/mapservutil.c +++ b/mapservutil.c @@ -303,8 +303,30 @@ mapObj *msCGILoadMap(mapservObj *mapserv) if(strncasecmp(mapserv->request->ParamValues[i],"http",4) == 0) { if(msGetConfigOption(map, "CGI_CONTEXT_URL")) msLoadMapContextURL(map, mapserv->request->ParamValues[i], MS_FALSE); - } else - msLoadMapContext(map, mapserv->request->ParamValues[i], MS_FALSE); + } else { + const char *map_context_filename = mapserv->request->ParamValues[i]; + const char *ms_context_pattern = CPLGetConfigOption("MS_CONTEXT_PATTERN", NULL); + const char *ms_context_bad_pattern = CPLGetConfigOption("MS_CONTEXT_BAD_PATTERN", NULL); + if(ms_context_bad_pattern == NULL) ms_context_bad_pattern = ms_map_bad_pattern_default; + + if(ms_context_pattern == NULL) { // can't go any further, bail + msSetError(MS_WEBERR, "Required configuration value MS_CONTEXT_PATTERN not set.", "msCGILoadMap()"); + msFreeMap(map); + return NULL; + } + if(msIsValidRegex(ms_context_bad_pattern) == MS_FALSE || + msEvalRegex(ms_context_bad_pattern, map_context_filename) == MS_TRUE) { + msSetError(MS_WEBERR, "CGI variable \"context\" fails to validate.", "msCGILoadMap()"); + msFreeMap(map); + return NULL; + } + if(msEvalRegex(ms_context_pattern, map_context_filename) != MS_TRUE) { + msSetError(MS_WEBERR, "CGI variable \"context\" fails to validate.", "msCGILoadMap()"); + msFreeMap(map); + return NULL; + } + msLoadMapContext(map, map_context_filename, MS_FALSE); + } } } } From 1c2b6f8ac9f5a30ed5307c466d19c7b3c682284b Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Fri, 30 Dec 2022 15:49:24 +0100 Subject: [PATCH 138/160] msautotest: add a test for CONTEXT= loading (refs #6779) --- msautotest/wxs/expected/ows_context_caps.xml | 128 +++++++++++++++++++ msautotest/wxs/ows_context.map | 72 +++++++++++ msautotest/wxs/ows_context.xml | 13 ++ 3 files changed, 213 insertions(+) create mode 100644 msautotest/wxs/expected/ows_context_caps.xml create mode 100644 msautotest/wxs/ows_context.map create mode 100644 msautotest/wxs/ows_context.xml diff --git a/msautotest/wxs/expected/ows_context_caps.xml b/msautotest/wxs/expected/ows_context_caps.xml new file mode 100644 index 0000000000..ae7f9df53e --- /dev/null +++ b/msautotest/wxs/expected/ows_context_caps.xml @@ -0,0 +1,128 @@ +Content-Type: application/vnd.ogc.wms_xml; charset=UTF-8 + + + + ]> + + + + + OGC:WMS + Map Context demo + Demo for map context document + + + + + + + + + application/vnd.ogc.wms_xml + + + + + + + + + image/png + image/jpeg + image/png; mode=8bit + image/vnd.jpeg-png + image/vnd.jpeg-png8 + application/x-pdf + image/svg+xml + image/tiff + application/vnd.google-earth.kml+xml + application/vnd.google-earth.kmz + application/vnd.mapbox-vector-tile + application/x-protobuf + application/json + + + + + + + + + text/plain + application/vnd.ogc.gml + + + + + + + + + text/xml + + + + + + + + + image/png + image/jpeg + image/png; mode=8bit + image/vnd.jpeg-png + image/vnd.jpeg-png8 + + + + + + + + + text/xml + + + + + + + + + + application/vnd.ogc.se_xml + application/vnd.ogc.se_inimage + application/vnd.ogc.se_blank + + + + + mapcontext + Map Context demo + Demo for map context document + EPSG:4326 + + + + province + province + + + text/xml + + + + + + + diff --git a/msautotest/wxs/ows_context.map b/msautotest/wxs/ows_context.map new file mode 100644 index 0000000000..1dbbc78832 --- /dev/null +++ b/msautotest/wxs/ows_context.map @@ -0,0 +1,72 @@ +# +# Test OWS Context loading +# +# REQUIRES: INPUT=GDAL OUTPUT=PNG SUPPORTS=WMS + +# Capabilities updatesequence (less than) +# RUN_PARMS: ows_context_caps.xml [MAPSERV] QUERY_STRING="map=[MAPFILE]&CONTEXT=ows_context.xml&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetCapabilities" > [RESULT_DEVERSION] + +MAP + +NAME OWS_CONTEXT_TEST +STATUS ON +SIZE 400 300 +#EXTENT 2018000 -73300 3410396 647400 +EXTENT -67.5725 42 -58.9275 48.5 +UNITS METERS +IMAGECOLOR 255 255 255 +SHAPEPATH ./data +SYMBOLSET etc/symbols.sym +FONTSET etc/fonts.txt + +# +# Start of web interface definition +# +WEB + + IMAGEPATH "/tmp/ms_tmp/" + IMAGEURL "/ms_tmp/" + + METADATA + "wms_onlineresource" "http://localhost/path/to/wfs_simple?myparam=something&" + "ows_enable_request" "*" + END +END + +PROJECTION + "init=epsg:4326" +# "init=./data/epsg2:42304" +# "init=epsg:42304" +END + + +# +# Start of layer definitions +# + +LAYER + NAME province + DATA province + METADATA + "wms_title" "province" + END + TYPE POINT + STATUS ON + PROJECTION + "init=./data/epsg2:42304" +# "init=epsg:42304" + END + + CLASSITEM "Name_e" + + CLASS + NAME "Province" + STYLE + COLOR 200 255 0 + OUTLINECOLOR 120 120 120 + END + END +END # Layer + + +END # Map File diff --git a/msautotest/wxs/ows_context.xml b/msautotest/wxs/ows_context.xml new file mode 100644 index 0000000000..8d3a4719f2 --- /dev/null +++ b/msautotest/wxs/ows_context.xml @@ -0,0 +1,13 @@ + + + + + + + + Map Context demo + Demo for map context document + + + + From 7589699509251dc0cf880daaff144c7967d3b562 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Fri, 30 Dec 2022 15:51:51 +0100 Subject: [PATCH 139/160] msGetMapContextFileText(): add sanity check on file size (refs #6779) --- mapcontext.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/mapcontext.c b/mapcontext.c index 144156fc41..354bf8a823 100644 --- a/mapcontext.c +++ b/mapcontext.c @@ -46,11 +46,11 @@ ** Take the filename in argument ** Return value must be freed by caller */ -char * msGetMapContextFileText(char *filename) +static char * msGetMapContextFileText(const char *filename) { char *pszBuffer; VSILFILE *stream; - int nLength; + vsi_l_offset nLength; /* open file */ if(filename != NULL && strlen(filename) > 0) { @@ -65,10 +65,16 @@ char * msGetMapContextFileText(char *filename) } VSIFSeekL( stream, 0, SEEK_END ); - nLength = (int) VSIFTellL( stream ); + nLength = VSIFTellL( stream ); VSIFSeekL( stream, 0, SEEK_SET ); + if( nLength > 100 * 1024 * 1024U ) + { + msSetError(MS_MEMERR, "(%s): too big file", "msGetMapContextFileText()", filename); + VSIFCloseL( stream ); + return NULL; + } - pszBuffer = (char *) malloc(nLength+1); + pszBuffer = (char *) malloc((size_t)nLength+1); if( pszBuffer == NULL ) { msSetError(MS_MEMERR, "(%s)", "msGetMapContextFileText()", filename); VSIFCloseL( stream ); From ffb4c723bebf3cede4f0f59f9fdab5cf79f2ac74 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Fri, 30 Dec 2022 16:58:12 +0100 Subject: [PATCH 140/160] msLoadMapContextGeneral(): fix memory leaks --- mapcontext.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/mapcontext.c b/mapcontext.c index 354bf8a823..ba5bf9de13 100644 --- a/mapcontext.c +++ b/mapcontext.c @@ -688,6 +688,7 @@ int msLoadMapContextGeneral(mapObj *map, CPLXMLNode *psGeneral, sprintf(pszProj, "init=epsg:%s", pszValue+5); } + msFreeProjection(&map->projection); msInitProjection(&map->projection); map->projection.args[map->projection.numargs] = msStrdup(pszProj); map->projection.numargs++; @@ -752,12 +753,20 @@ int msLoadMapContextGeneral(mapObj *map, CPLXMLNode *psGeneral, pszValue = (char*)CPLGetXMLValue(psMapContext, "id", NULL); if (pszValue) + { + msFree(map->name); map->name = msStrdup(pszValue); + } } else { + char* pszMapName = NULL; if(msGetMapContextXMLStringValue(psGeneral, "Name", - &(map->name)) == MS_FAILURE) { + &pszMapName) == MS_FAILURE) { msGetMapContextXMLStringValue(psGeneral, "gml:name", - &(map->name)); + &pszMapName); + } + if( pszMapName ) { + msFree(map->name); + map->name = pszMapName; } } /* Keyword */ From f49a01db3e11b33df705e9139058574b43e833b4 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Fri, 30 Dec 2022 20:18:03 +0100 Subject: [PATCH 141/160] msLoadMapContext(): add validation of filename against MS_CONTEXTFILE_PATTERN, which defaults to .xml extension --- mapcontext.c | 7 +++++++ mapserver.h | 1 + 2 files changed, 8 insertions(+) diff --git a/mapcontext.c b/mapcontext.c index ba5bf9de13..55929f25db 100644 --- a/mapcontext.c +++ b/mapcontext.c @@ -30,6 +30,7 @@ #include "mapows.h" #include "cpl_vsi.h" +#include "cpl_conv.h" #if defined(USE_WMS_LYR) @@ -1130,6 +1131,12 @@ int msLoadMapContext(mapObj *map, const char *filename, int unique_layer_names) int nVersion=-1; char szVersionBuf[OWS_VERSION_MAXLEN]; + const char *ms_contextfile_pattern = CPLGetConfigOption("MS_CONTEXTFILE_PATTERN", MS_DEFAULT_CONTEXTFILE_PATTERN); + if(msEvalRegex(ms_contextfile_pattern, filename) != MS_TRUE) { + msSetError(MS_REGEXERR, "Filename validation failed." , "msLoadMapContext()"); + return MS_FAILURE; + } + /* */ /* Load the raw XML file */ /* */ diff --git a/mapserver.h b/mapserver.h index 6fb124de14..86e48b12f7 100644 --- a/mapserver.h +++ b/mapserver.h @@ -231,6 +231,7 @@ extern "C" { #else #define MS_DEFAULT_MAPFILE_PATTERN "\\.map$" #endif +#define MS_DEFAULT_CONTEXTFILE_PATTERN "\\.[Xx][Mm][Ll]$" #define MS_TEMPLATE_MAGIC_STRING "MapServer Template" #define MS_TEMPLATE_EXPR "\\.(xml|wml|html|htm|svg|kml|gml|js|tmpl)$" From 38911252a196c54b1e724c1162902056f4fefd75 Mon Sep 17 00:00:00 2001 From: Steve Lime Date: Sun, 5 Feb 2023 07:07:58 -0600 Subject: [PATCH 142/160] Collection of backports from the 8.0 development work. (#6818) Co-authored-by: Landry Breuil --- mapagg.cpp | 87 ++++----- mapdraw.c | 4 +- maperror.c | 1 + mapfile.c | 491 ++++++++++++++++++++++++++++++-------------------- mapquery.c | 3 + mapserver.h | 52 +++++- mapservutil.c | 4 +- mapsymbol.c | 15 +- 8 files changed, 399 insertions(+), 258 deletions(-) diff --git a/mapagg.cpp b/mapagg.cpp index cccdc2ecc0..3a6139f2ad 100644 --- a/mapagg.cpp +++ b/mapagg.cpp @@ -812,52 +812,57 @@ imageObj *agg2CreateImage(int width, int height, outputFormatObj *format, colorO "AGG2 driver only supports RGB or RGBA pixel models.", "agg2CreateImage()"); return image; } - image = (imageObj *) calloc(1, sizeof (imageObj)); - MS_CHECK_ALLOC(image, sizeof (imageObj), NULL); - AGG2Renderer *r = new AGG2Renderer(); + if (width > 0 && height > 0) { + image = (imageObj *) calloc(1, sizeof (imageObj)); + MS_CHECK_ALLOC(image, sizeof (imageObj), NULL); + AGG2Renderer *r = new AGG2Renderer(); - /* Compute size on 64bit and check that it is compatible of the platform size_t */ - AGG_INT64U bufSize64 = (AGG_INT64U)width * height * 4 * sizeof(band_type); - size_t bufSize = (size_t)bufSize64; - if( (AGG_INT64U)bufSize != bufSize64 ) { - msSetError(MS_MEMERR, "%s: %d: Out of memory allocating " AGG_INT64U_FRMT " bytes.\n", "agg2CreateImage()", - __FILE__, __LINE__, bufSize64); - free(image); - delete r; - return NULL; - } + /* Compute size on 64bit and check that it is compatible of the platform size_t */ + AGG_INT64U bufSize64 = (AGG_INT64U)width * height * 4 * sizeof(band_type); + size_t bufSize = (size_t)bufSize64; + if( (AGG_INT64U)bufSize != bufSize64 ) { + msSetError(MS_MEMERR, "%s: %d: Out of memory allocating " AGG_INT64U_FRMT " bytes.\n", "agg2CreateImage()", + __FILE__, __LINE__, bufSize64); + free(image); + delete r; + return NULL; + } - r->buffer = (band_type*)malloc(bufSize); - if (r->buffer == NULL) { - msSetError(MS_MEMERR, "%s: %d: Out of memory allocating " AGG_INT64U_FRMT " bytes.\n", "agg2CreateImage()", - __FILE__, __LINE__, bufSize64); - free(image); - delete r; - return NULL; - } - r->m_rendering_buffer.attach(r->buffer, width, height, width * 4); - r->m_pixel_format.attach(r->m_rendering_buffer); - r->m_compop_pixel_format.attach(r->m_rendering_buffer); - r->m_renderer_base.attach(r->m_pixel_format); - r->m_compop_renderer_base.attach(r->m_compop_pixel_format); - r->m_renderer_scanline.attach(r->m_renderer_base); - r->default_gamma = atof(msGetOutputFormatOption( format, "GAMMA", "0.75" )); - if(r->default_gamma <= 0.0 || r->default_gamma >= 1.0) { - r->default_gamma = 0.75; - } - r->gamma_function.set(0,r->default_gamma); - r->m_rasterizer_aa_gamma.gamma(r->gamma_function); - if( bg && !format->transparent ) - r->m_renderer_base.clear(aggColor(bg)); - else - r->m_renderer_base.clear(AGG_NO_COLOR); + r->buffer = (band_type*)malloc(bufSize); + if (r->buffer == NULL) { + msSetError(MS_MEMERR, "%s: %d: Out of memory allocating " AGG_INT64U_FRMT " bytes.\n", "agg2CreateImage()", + __FILE__, __LINE__, bufSize64); + free(image); + delete r; + return NULL; + } + r->m_rendering_buffer.attach(r->buffer, width, height, width * 4); + r->m_pixel_format.attach(r->m_rendering_buffer); + r->m_compop_pixel_format.attach(r->m_rendering_buffer); + r->m_renderer_base.attach(r->m_pixel_format); + r->m_compop_renderer_base.attach(r->m_compop_pixel_format); + r->m_renderer_scanline.attach(r->m_renderer_base); + r->default_gamma = atof(msGetOutputFormatOption( format, "GAMMA", "0.75" )); + if(r->default_gamma <= 0.0 || r->default_gamma >= 1.0) { + r->default_gamma = 0.75; + } + r->gamma_function.set(0,r->default_gamma); + r->m_rasterizer_aa_gamma.gamma(r->gamma_function); + if( bg && !format->transparent ) + r->m_renderer_base.clear(aggColor(bg)); + else + r->m_renderer_base.clear(AGG_NO_COLOR); - if (!bg || format->transparent || format->imagemode == MS_IMAGEMODE_RGBA ) { - r->use_alpha = true; + if (!bg || format->transparent || format->imagemode == MS_IMAGEMODE_RGBA ) { + r->use_alpha = true; + } else { + r->use_alpha = false; + } + image->img.plugin = (void*) r; } else { - r->use_alpha = false; + msSetError(MS_RENDERERERR, "Cannot create AGG2 image of size %dx%d.", + "agg2CreateImage()", width, height); } - image->img.plugin = (void*) r; return image; } diff --git a/mapdraw.c b/mapdraw.c index 9018edca56..6fb9dfcefe 100644 --- a/mapdraw.c +++ b/mapdraw.c @@ -237,8 +237,8 @@ imageObj *msDrawMap(mapObj *map, int querymap) if(map->debug >= MS_DEBUGLEVEL_TUNING) msGettimeofday(&mapstarttime, NULL); if(querymap) { /* use queryMapObj image dimensions */ - if(map->querymap.width != -1) map->width = map->querymap.width; - if(map->querymap.height != -1) map->height = map->querymap.height; + if(map->querymap.width > 0 && map->querymap.width <= map->maxsize) map->width = map->querymap.width; + if(map->querymap.height > 0 && map->querymap.height <= map->maxsize) map->height = map->querymap.height; } msApplyMapConfigOptions(map); diff --git a/maperror.c b/maperror.c index 8204d61b09..2931d4d1c8 100644 --- a/maperror.c +++ b/maperror.c @@ -230,6 +230,7 @@ void msResetErrorList() ms_error->next = NULL; ms_error->code = MS_NOERR; + ms_error->isreported = MS_FALSE; ms_error->routine[0] = '\0'; ms_error->message[0] = '\0'; ms_error->errorcount = 0; diff --git a/mapfile.c b/mapfile.c index d4e6f5d4d9..ea8b348991 100644 --- a/mapfile.c +++ b/mapfile.c @@ -246,14 +246,31 @@ int getString(char **s) return(MS_FAILURE); } +int msCheckNumber(double number, int num_check_type, double value1, double value2) +{ + if(num_check_type == MS_NUM_CHECK_NONE) { + return MS_SUCCESS; + } else if(num_check_type == MS_NUM_CHECK_RANGE && number >= value1 && number <= value2) { + return MS_SUCCESS; + } else if(num_check_type == MS_NUM_CHECK_GT && number > value1) { + return MS_SUCCESS; + } else if(num_check_type == MS_NUM_CHECK_GTE && number >= value1) { + return MS_SUCCESS; + } + + return MS_FAILURE; +} + /* ** Load a floating point number from the map file. (see lexer.l) */ -int getDouble(double *d) +int getDouble(double *d, int num_check_type, double value1, double value2) { if(msyylex() == MS_NUMBER) { - *d = msyynumber; - return(0); /* success */ + if(msCheckNumber(msyynumber, num_check_type, value1, value2) == MS_SUCCESS) { + *d = msyynumber; + return(0); + } } msSetError(MS_SYMERR, "Parsing error near (%s):(line %d)", "getDouble()", msyystring_buffer, msyylineno); @@ -263,11 +280,13 @@ int getDouble(double *d) /* ** Load a integer from the map file. (see lexer.l) */ -int getInteger(int *i) +int getInteger(int *i, int num_check_type, int value1, int value2) { if(msyylex() == MS_NUMBER) { - *i = (int)msyynumber; - return(0); /* success */ + if(msCheckNumber(msyynumber, num_check_type, value1, value2) == MS_SUCCESS) { + *i = (int)msyynumber; + return(0); + } } msSetError(MS_SYMERR, "Parsing error near (%s):(line %d)", "getInteger()", msyystring_buffer, msyylineno); @@ -415,6 +434,11 @@ int loadColor(colorObj *color, attributeBindingObj *binding) int symbol; char hex[2]; + /* + ** Note that negative color values can be used to suppress or change behavior. For example, referenceObj uses + ** a negative color component to suppress rectangle fills. + */ + if(binding) { if((symbol = getSymbol(3, MS_NUMBER, MS_BINDING, MS_STRING)) == -1) return MS_FAILURE; } else { @@ -423,9 +447,13 @@ int loadColor(colorObj *color, attributeBindingObj *binding) color->alpha=255; if(symbol == MS_NUMBER) { - color->red = (int) msyynumber; - if(getInteger(&(color->green)) == -1) return MS_FAILURE; - if(getInteger(&(color->blue)) == -1) return MS_FAILURE; + if(msyynumber >= -255 && msyynumber <= 255) { + color->red = (int) msyynumber; + } else { + return MS_FAILURE; + } + if(getInteger(&(color->green), MS_NUM_CHECK_RANGE, -255, 255) == -1) return MS_FAILURE; + if(getInteger(&(color->blue), MS_NUM_CHECK_RANGE, -255, 255) == -1) return MS_FAILURE; } else if(symbol == MS_STRING) { int len = strlen(msyystring_buffer); if(msyystring_buffer[0] == '#' && (len == 7 || len == 9)) { /* got a hex color w/optional alpha */ @@ -461,7 +489,13 @@ int loadColorWithAlpha(colorObj *color) { char hex[2]; - if(getInteger(&(color->red)) == -1) { + + /* + ** Note that negative color values can be used to suppress or change behavior. For example, referenceObj uses + ** a negative color component to suppress rectangle fills. + */ + + if(getInteger(&(color->red), MS_NUM_CHECK_RANGE, -255, 255) == -1) { if(msyystring_buffer[0] == '#' && strlen(msyystring_buffer) == 7) { /* got a hex color */ hex[0] = msyystring_buffer[1]; hex[1] = msyystring_buffer[2]; @@ -492,9 +526,9 @@ int loadColorWithAlpha(colorObj *color) } return(MS_FAILURE); } - if(getInteger(&(color->green)) == -1) return(MS_FAILURE); - if(getInteger(&(color->blue)) == -1) return(MS_FAILURE); - if(getInteger(&(color->alpha)) == -1) return(MS_FAILURE); + if(getInteger(&(color->green), MS_NUM_CHECK_RANGE, -255, 255) == -1) return(MS_FAILURE); + if(getInteger(&(color->blue), MS_NUM_CHECK_RANGE, -255, 255) == -1) return(MS_FAILURE); + if(getInteger(&(color->alpha), MS_NUM_CHECK_RANGE, 0, 255) == -1) return(MS_FAILURE); return(MS_SUCCESS); } @@ -911,7 +945,7 @@ static int loadFeaturePoints(lineObj *points) } points->point[points->numpoints].x = atof(msyystring_buffer); - if(getDouble(&(points->point[points->numpoints].y)) == -1) return(MS_FAILURE); + if(getDouble(&(points->point[points->numpoints].y), MS_NUM_CHECK_NONE, -1, -1) == -1) return(MS_FAILURE); points->numpoints++; break; @@ -1092,27 +1126,27 @@ static int loadGrid( layerObj *pLayer ) } break; case( MINARCS ): - if(getDouble(&(pLayer->grid->minarcs)) == -1) + if(getDouble(&(pLayer->grid->minarcs), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; case( MAXARCS ): - if(getDouble(&(pLayer->grid->maxarcs)) == -1) + if(getDouble(&(pLayer->grid->maxarcs), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; case( MININTERVAL ): - if(getDouble(&(pLayer->grid->minincrement)) == -1) + if(getDouble(&(pLayer->grid->minincrement), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; case( MAXINTERVAL ): - if(getDouble(&(pLayer->grid->maxincrement)) == -1) + if(getDouble(&(pLayer->grid->maxincrement), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; case( MINSUBDIVIDE ): - if(getDouble(&(pLayer->grid->minsubdivides)) == -1) + if(getDouble(&(pLayer->grid->minsubdivides), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; case( MAXSUBDIVIDE ): - if(getDouble(&(pLayer->grid->maxsubdivides)) == -1) + if(getDouble(&(pLayer->grid->maxsubdivides), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; default: @@ -1521,10 +1555,10 @@ static int loadLeader(labelLeaderObj *leader) msSetError(MS_EOFERR, NULL, "loadLeader()"); return(-1); case GRIDSTEP: - if(getInteger(&(leader->gridstep)) == -1) return(-1); + if(getInteger(&(leader->gridstep), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; case MAXDISTANCE: - if(getInteger(&(leader->maxdistance)) == -1) return(-1); + if(getInteger(&(leader->maxdistance), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; case STYLE: if(msGrowLeaderStyles(leader) == NULL) @@ -1560,9 +1594,13 @@ static int loadLabel(labelObj *label) if((symbol = getSymbol(5, MS_NUMBER,MS_AUTO,MS_AUTO2,MS_FOLLOW,MS_BINDING)) == -1) return(-1); - if(symbol == MS_NUMBER) - label->angle = msyynumber; - else if(symbol == MS_BINDING) { + if(symbol == MS_NUMBER) { + if(msCheckNumber(msyynumber, MS_NUM_CHECK_RANGE, -360.0, 360.0) == MS_FAILURE) { + msSetError(MS_MISCERR, "Invalid ANGLE, must be between -360 and 360 (line %d)", "loadLabel()", msyylineno); + return(MS_FAILURE); + } + label->angle = (double) msyynumber; + } else if(symbol == MS_BINDING) { if (label->bindings[MS_LABEL_BINDING_ANGLE].item != NULL) msFree(label->bindings[MS_LABEL_BINDING_ANGLE].item); label->bindings[MS_LABEL_BINDING_ANGLE].item = msStrdup(msyystring_buffer); @@ -1587,7 +1625,7 @@ static int loadLabel(labelObj *label) msyylex(); break; case(BUFFER): - if(getInteger(&(label->buffer)) == -1) return(-1); + if(getInteger(&(label->buffer), MS_NUM_CHECK_NONE, -1, -1) == -1) return(-1); break; case(COLOR): if(loadColor(&(label->color), &(label->bindings[MS_LABEL_BINDING_COLOR])) != MS_SUCCESS) return(-1); @@ -1637,56 +1675,60 @@ static int loadLabel(labelObj *label) label->force = MS_LABEL_FORCE_GROUP; break; default: - msSetError(MS_MISCERR, "Invalid FORCE, must be ON,OFF,or GROUP" , "loadLabel()"); + msSetError(MS_MISCERR, "Invalid FORCE, must be ON,OFF,or GROUP (line %d)" , "loadLabel()", msyylineno); return(-1); } break; case(LABEL): break; /* for string loads */ case(LEADER): - msSetError(MS_MISCERR, "LABEL LEADER not implemented. LEADER goes at the CLASS level." , "loadLabel()"); + msSetError(MS_MISCERR, "LABEL LEADER not implemented. LEADER goes at the CLASS level (line %d)" , "loadLabel()", msyylineno); return(-1); label->leader = msSmallMalloc(sizeof(labelLeaderObj)); if(loadLeader(label->leader) == -1) return(-1); break; case(MAXSIZE): - if(getInteger(&(label->maxsize)) == -1) return(-1); + if(getInteger(&(label->maxsize), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; case(MAXSCALEDENOM): - if(getDouble(&(label->maxscaledenom)) == -1) return(-1); + if(getDouble(&(label->maxscaledenom), MS_NUM_CHECK_GTE, 0, -1) == -1) return(-1); break; case(MAXLENGTH): - if(getInteger(&(label->maxlength)) == -1) return(-1); + if(getInteger(&(label->maxlength), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; case(MINLENGTH): - if(getInteger(&(label->minlength)) == -1) return(-1); + if(getInteger(&(label->minlength), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; case(MINDISTANCE): - if(getInteger(&(label->mindistance)) == -1) return(-1); + if(getInteger(&(label->mindistance), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; case(REPEATDISTANCE): - if(getInteger(&(label->repeatdistance)) == -1) return(-1); + if(getInteger(&(label->repeatdistance), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; case(MAXOVERLAPANGLE): - if(getDouble(&(label->maxoverlapangle)) == -1) return(-1); + if(getDouble(&(label->maxoverlapangle), MS_NUM_CHECK_RANGE, 0, 360) == -1) return(-1); break; case(MINFEATURESIZE): - if((symbol = getSymbol(2, MS_NUMBER,MS_AUTO)) == -1) return(-1); - if(symbol == MS_NUMBER) + if((symbol = getSymbol(2, MS_NUMBER,MS_AUTO)) == -1) return(-1); + if(symbol == MS_NUMBER) { + if(msCheckNumber(msyynumber, MS_NUM_CHECK_GT, 0, -1) == MS_FAILURE) { + msSetError(MS_MISCERR, "Invalid MINFEATURESIZE, must be greater than 0 (line %d)", "loadLabel()", msyylineno); + return(MS_FAILURE); + } label->minfeaturesize = (int)msyynumber; - else + } else label->autominfeaturesize = MS_TRUE; break; case(MINSCALEDENOM): - if(getDouble(&(label->minscaledenom)) == -1) return(-1); + if(getDouble(&(label->minscaledenom), MS_NUM_CHECK_GTE, 0, -1) == -1) return(-1); break; case(MINSIZE): - if(getInteger(&(label->minsize)) == -1) return(-1); + if(getInteger(&(label->minsize), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; case(OFFSET): if((symbol = getSymbol(2, MS_NUMBER,MS_BINDING)) == -1) return(MS_FAILURE); if(symbol == MS_NUMBER) - label->offsetx = (int) msyynumber; + label->offsetx = (int) msyynumber; // any integer ok else { if (label->bindings[MS_LABEL_BINDING_OFFSET_X].item != NULL) msFree(label->bindings[MS_LABEL_BINDING_OFFSET_X].item); @@ -1696,7 +1738,7 @@ static int loadLabel(labelObj *label) if((symbol = getSymbol(2, MS_NUMBER,MS_BINDING)) == -1) return(MS_FAILURE); if(symbol == MS_NUMBER) - label->offsety = (int) msyynumber; + label->offsety = (int) msyynumber; // any integer ok else { if (label->bindings[MS_LABEL_BINDING_OFFSET_Y].item != NULL) msFree(label->bindings[MS_LABEL_BINDING_OFFSET_Y].item); @@ -1709,7 +1751,7 @@ static int loadLabel(labelObj *label) if(label->bindings[MS_LABEL_BINDING_OUTLINECOLOR].item) label->numbindings++; break; case(OUTLINEWIDTH): - if(getInteger(&(label->outlinewidth)) == -1) return(-1); + if(getInteger(&(label->outlinewidth), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; case(PARTIALS): if((label->partials = getSymbol(2, MS_TRUE,MS_FALSE)) == -1) return(-1); @@ -1727,11 +1769,11 @@ static int loadLabel(labelObj *label) case(PRIORITY): if((symbol = getSymbol(2, MS_NUMBER,MS_BINDING)) == -1) return(-1); if(symbol == MS_NUMBER) { - label->priority = (int) msyynumber; - if(label->priority < 1 || label->priority > MS_MAX_LABEL_PRIORITY) { - msSetError(MS_MISCERR, "Invalid PRIORITY, must be an integer between 1 and %d." , "loadLabel()", MS_MAX_LABEL_PRIORITY); + if(msCheckNumber(msyynumber, MS_NUM_CHECK_RANGE, 1, MS_MAX_LABEL_PRIORITY) == MS_FAILURE) { + msSetError(MS_MISCERR, "Invalid PRIORITY, must be an integer between 1 and %d (line %d)" , "loadLabel()", MS_MAX_LABEL_PRIORITY, msyylineno); return(-1); } + label->priority = (int) msyynumber; } else { if (label->bindings[MS_LABEL_BINDING_PRIORITY].item != NULL) msFree(label->bindings[MS_LABEL_BINDING_PRIORITY].item); @@ -1743,10 +1785,9 @@ static int loadLabel(labelObj *label) if(loadColor(&(label->shadowcolor), NULL) != MS_SUCCESS) return(-1); break; case(SHADOWSIZE): - /* if(getInteger(&(label->shadowsizex)) == -1) return(-1); */ if((symbol = getSymbol(2, MS_NUMBER,MS_BINDING)) == -1) return(-1); if(symbol == MS_NUMBER) { - label->shadowsizex = (int) msyynumber; + label->shadowsizex = (int) msyynumber; // x offset, any int ok } else { if (label->bindings[MS_LABEL_BINDING_SHADOWSIZEX].item != NULL) msFree(label->bindings[MS_LABEL_BINDING_SHADOWSIZEX].item); @@ -1754,10 +1795,9 @@ static int loadLabel(labelObj *label) label->numbindings++; } - /* if(getInteger(&(label->shadowsizey)) == -1) return(-1); */ if((symbol = getSymbol(2, MS_NUMBER,MS_BINDING)) == -1) return(-1); if(symbol == MS_NUMBER) { - label->shadowsizey = (int) msyynumber; + label->shadowsizey = (int) msyynumber; // y offset, any int ok } else { if (label->bindings[MS_LABEL_BINDING_SHADOWSIZEY].item != NULL) msFree(label->bindings[MS_LABEL_BINDING_SHADOWSIZEY].item); @@ -1780,6 +1820,10 @@ static int loadLabel(labelObj *label) return(-1); if(symbol == MS_NUMBER) { + if(msCheckNumber(msyynumber, MS_NUM_CHECK_GT, 0, -1) == MS_FAILURE) { + msSetError(MS_MISCERR, "Invalid SIZE, must be greater than 0 (line %d)" , "loadLabel()", msyylineno); + return(-1); + } label->size = (double) msyynumber; } else if(symbol == MS_BINDING) { label->bindings[MS_LABEL_BINDING_SIZE].item = msStrdup(msyystring_buffer); @@ -2282,10 +2326,10 @@ int loadCluster(clusterObj *cluster) case(CLUSTER): break; /* for string loads */ case(MAXDISTANCE): - if(getDouble(&(cluster->maxdistance)) == -1) return(-1); + if(getDouble(&(cluster->maxdistance), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; case(BUFFER): - if(getDouble(&(cluster->buffer)) == -1) return(-1); + if(getDouble(&(cluster->buffer), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; case(REGION): if(getString(&cluster->region) == MS_FAILURE) return(-1); @@ -2446,19 +2490,23 @@ int loadStyle(styleObj *style) break; case(DATARANGE): /*These are both in one line now*/ - if(getDouble(&(style->minvalue)) == -1) return(-1); - if(getDouble(&(style->maxvalue)) == -1) return(-1); + if(getDouble(&(style->minvalue), MS_NUM_CHECK_NONE, -1, -1) == -1) return(MS_FAILURE); + if(getDouble(&(style->maxvalue), MS_NUM_CHECK_NONE, -1, -1) == -1) return(MS_FAILURE); break; case(RANGEITEM): - if(getString(&style->rangeitem) == MS_FAILURE) return(-1); + if(getString(&style->rangeitem) == MS_FAILURE) return(MS_FAILURE); break; /* End Range fields*/ case(ANGLE): if((symbol = getSymbol(3, MS_NUMBER,MS_BINDING,MS_AUTO)) == -1) return(MS_FAILURE); - if(symbol == MS_NUMBER) + if(symbol == MS_NUMBER) { + if(msCheckNumber(msyynumber, MS_NUM_CHECK_RANGE, 0.0, 360.0) == MS_FAILURE) { + msSetError(MS_MISCERR, "Invalid ANGLE, must be between 0 and 360 (line %d)", "loadStyle()", msyylineno); + return(MS_FAILURE); + } style->angle = (double) msyynumber; - else if(symbol==MS_BINDING) { + } else if(symbol==MS_BINDING) { if (style->bindings[MS_STYLE_BINDING_ANGLE].item != NULL) msFree(style->bindings[MS_STYLE_BINDING_ANGLE].item); style->bindings[MS_STYLE_BINDING_ANGLE].item = msStrdup(msyystring_buffer); @@ -2467,12 +2515,11 @@ int loadStyle(styleObj *style) style->autoangle=MS_TRUE; } break; - case(ANTIALIAS): /*ignore*/ + case(ANTIALIAS): /* ignore */ msyylex(); break; case(BACKGROUNDCOLOR): if(loadColor(&(style->backgroundcolor), NULL) != MS_SUCCESS) return(MS_FAILURE); - break; case(COLOR): if(loadColor(&(style->color), &(style->bindings[MS_STYLE_BINDING_COLOR])) != MS_SUCCESS) return(MS_FAILURE); if(style->bindings[MS_STYLE_BINDING_COLOR].item) style->numbindings++; @@ -2499,20 +2546,19 @@ int loadStyle(styleObj *style) } break; case(GAP): - if((getDouble(&style->gap)) == -1) return(MS_FAILURE); + if(getDouble(&(style->gap), MS_NUM_CHECK_NONE, -1, -1) == -1) return(MS_FAILURE); break; case(INITIALGAP): - if((getDouble(&style->initialgap)) == -1) return(MS_FAILURE); - if(style->initialgap < 0) { - msSetError(MS_MISCERR, "INITIALGAP requires a positive values", "loadStyle()"); + if(getDouble(&(style->initialgap), MS_NUM_CHECK_GTE, 0, -1) == -1) { // zero is ok + msSetError(MS_MISCERR, "INITIALGAP requires a positive values (line %d)", "loadStyle()", msyylineno); return(MS_FAILURE); } break; case(MAXSCALEDENOM): - if(getDouble(&(style->maxscaledenom)) == -1) return(MS_FAILURE); + if(getDouble(&(style->maxscaledenom), MS_NUM_CHECK_GTE, 0, -1) == -1) return(MS_FAILURE); break; case(MINSCALEDENOM): - if(getDouble(&(style->minscaledenom)) == -1) return(MS_FAILURE); + if(getDouble(&(style->minscaledenom), MS_NUM_CHECK_GTE, 0, -1) == -1) return(MS_FAILURE); break; case(GEOMTRANSFORM): { int s; @@ -2534,24 +2580,24 @@ int loadStyle(styleObj *style) if((style->linejoin = getSymbol(4,MS_CJC_NONE, MS_CJC_ROUND, MS_CJC_MITER, MS_CJC_BEVEL)) == -1) return(MS_FAILURE); break; case(LINEJOINMAXSIZE): - if((getDouble(&style->linejoinmaxsize)) == -1) return(MS_FAILURE); + if(getDouble(&(style->linejoinmaxsize), MS_NUM_CHECK_GT, 0, -1) == -1) return(MS_FAILURE); break; case(MAXSIZE): - if(getDouble(&(style->maxsize)) == -1) return(MS_FAILURE); + if(getDouble(&(style->maxsize), MS_NUM_CHECK_GT, 0, -1) == -1) return(MS_FAILURE); break; case(MINSIZE): - if(getDouble(&(style->minsize)) == -1) return(MS_FAILURE); + if(getDouble(&(style->minsize), MS_NUM_CHECK_GTE, 0, -1) == -1) return(MS_FAILURE); break; case(MAXWIDTH): - if(getDouble(&(style->maxwidth)) == -1) return(MS_FAILURE); + if(getDouble(&(style->maxwidth), MS_NUM_CHECK_GT, 0, -1) == -1) return(MS_FAILURE); break; case(MINWIDTH): - if(getDouble(&(style->minwidth)) == -1) return(MS_FAILURE); + if(getDouble(&(style->minwidth), MS_NUM_CHECK_GTE, 0, -1) == -1) return(MS_FAILURE); break; case(OFFSET): if((symbol = getSymbol(2, MS_NUMBER,MS_BINDING)) == -1) return(MS_FAILURE); if(symbol == MS_NUMBER) - style->offsetx = (double) msyynumber; + style->offsetx = (double) msyynumber; // any double ok else { if (style->bindings[MS_STYLE_BINDING_OFFSET_X].item != NULL) msFree(style->bindings[MS_STYLE_BINDING_OFFSET_X].item); @@ -2561,7 +2607,7 @@ int loadStyle(styleObj *style) if((symbol = getSymbol(2, MS_NUMBER,MS_BINDING)) == -1) return(MS_FAILURE); if(symbol == MS_NUMBER) - style->offsety = (double) msyynumber; + style->offsety = (double) msyynumber; // any double ok else { if (style->bindings[MS_STYLE_BINDING_OFFSET_Y].item != NULL) msFree(style->bindings[MS_STYLE_BINDING_OFFSET_Y].item); @@ -2590,7 +2636,7 @@ int loadStyle(styleObj *style) switch(msyylex()) { case(END): if(style->patternlength < 2) { - msSetError(MS_SYMERR, "Not enough pattern elements. A minimum of 2 are required", "loadStyle()"); + msSetError(MS_SYMERR, "Not enough pattern elements. A minimum of 2 are required (line %d)", "loadStyle()", msyylineno); return(MS_FAILURE); } done = MS_TRUE; @@ -2598,14 +2644,14 @@ int loadStyle(styleObj *style) case(MS_NUMBER): /* read the pattern values */ if(style->patternlength == MS_MAXPATTERNLENGTH) { msSetError(MS_SYMERR, "Pattern too long.", "loadStyle()"); - return(-1); + return(MS_FAILURE); } - style->pattern[style->patternlength] = atof(msyystring_buffer); + style->pattern[style->patternlength] = atof(msyystring_buffer); // good enough? style->patternlength++; break; default: msSetError(MS_TYPEERR, "Parsing error near (%s):(line %d)", "loadStyle()", msyystring_buffer, msyylineno); - return(-1); + return(MS_FAILURE); } if(done == MS_TRUE) break; @@ -2621,11 +2667,11 @@ int loadStyle(styleObj *style) case(OUTLINEWIDTH): if((symbol = getSymbol(2, MS_NUMBER,MS_BINDING)) == -1) return(MS_FAILURE); if(symbol == MS_NUMBER) { - style->outlinewidth = (double) msyynumber; - if(style->outlinewidth < 0) { - msSetError(MS_MISCERR, "Invalid OUTLINEWIDTH, must be greater than 0" , "loadStyle()"); + if(msCheckNumber(msyynumber, MS_NUM_CHECK_GTE, 0, -1) == MS_FAILURE) { + msSetError(MS_MISCERR, "Invalid OUTLINEWIDTH, must be greater then or equal to 0 (line %d)", "loadStyle()", msyylineno); return(MS_FAILURE); } + style->outlinewidth = (double) msyynumber; } else { if (style->bindings[MS_STYLE_BINDING_OUTLINEWIDTH].item != NULL) msFree(style->bindings[MS_STYLE_BINDING_OUTLINEWIDTH].item); @@ -2635,9 +2681,13 @@ int loadStyle(styleObj *style) break; case(SIZE): if((symbol = getSymbol(2, MS_NUMBER,MS_BINDING)) == -1) return(MS_FAILURE); - if(symbol == MS_NUMBER) + if(symbol == MS_NUMBER) { + if(msCheckNumber(msyynumber, MS_NUM_CHECK_GT, 0, -1) == MS_FAILURE) { + msSetError(MS_MISCERR, "Invalid SIZE, must be greater than 0 (line %d)", "loadStyle()", msyylineno); + return(MS_FAILURE); + } style->size = (double) msyynumber; - else { + } else { if (style->bindings[MS_STYLE_BINDING_SIZE].item != NULL) msFree(style->bindings[MS_STYLE_BINDING_SIZE].item); style->bindings[MS_STYLE_BINDING_SIZE].item = msStrdup(msyystring_buffer); @@ -2649,7 +2699,11 @@ int loadStyle(styleObj *style) case(SYMBOL): if((symbol = getSymbol(3, MS_NUMBER,MS_STRING,MS_BINDING)) == -1) return(MS_FAILURE); if(symbol == MS_NUMBER) { - if (style->symbolname != NULL) { + if(msCheckNumber(msyynumber, MS_NUM_CHECK_GTE, 0, -1) == MS_FAILURE) { + msSetError(MS_MISCERR, "Invalid SYMBOL id, must be greater than or equal to 0 (line %d)", "loadStyle()", msyylineno); + return(MS_FAILURE); + } + if(style->symbolname != NULL) { msFree(style->symbolname); style->symbolname = NULL; } @@ -2667,9 +2721,13 @@ int loadStyle(styleObj *style) break; case(WIDTH): if((symbol = getSymbol(2, MS_NUMBER,MS_BINDING)) == -1) return(MS_FAILURE); - if(symbol == MS_NUMBER) + if(symbol == MS_NUMBER) { + if(msCheckNumber(msyynumber, MS_NUM_CHECK_GTE, 0, -1) == MS_FAILURE) { + msSetError(MS_MISCERR, "Invalid WIDTH, must be greater than or equal to 0 (line %d)", "loadStyle()", msyylineno); + return(MS_FAILURE); + } style->width = (double) msyynumber; - else { + } else { if (style->bindings[MS_STYLE_BINDING_WIDTH].item != NULL) msFree(style->bindings[MS_STYLE_BINDING_WIDTH].item); style->bindings[MS_STYLE_BINDING_WIDTH].item = msStrdup(msyystring_buffer); @@ -2678,9 +2736,9 @@ int loadStyle(styleObj *style) break; case(POLAROFFSET): if((symbol = getSymbol(2, MS_NUMBER,MS_BINDING)) == -1) return(MS_FAILURE); - if(symbol == MS_NUMBER) - style->polaroffsetpixel = (double) msyynumber; - else { + if(symbol == MS_NUMBER) { + style->polaroffsetpixel = (double) msyynumber; // ok? + } else { if (style->bindings[MS_STYLE_BINDING_POLAROFFSET_PIXEL].item != NULL) msFree(style->bindings[MS_STYLE_BINDING_POLAROFFSET_PIXEL].item); style->bindings[MS_STYLE_BINDING_POLAROFFSET_PIXEL].item = msStrdup(msyystring_buffer); @@ -2688,9 +2746,9 @@ int loadStyle(styleObj *style) } if((symbol = getSymbol(2, MS_NUMBER,MS_BINDING)) == -1) return(MS_FAILURE); - if(symbol == MS_NUMBER) - style->polaroffsetangle = (double) msyynumber; - else { + if(symbol == MS_NUMBER) { + style->polaroffsetangle = (double) msyynumber; // ok? + } else { if (style->bindings[MS_STYLE_BINDING_POLAROFFSET_ANGLE].item != NULL) msFree(style->bindings[MS_STYLE_BINDING_POLAROFFSET_ANGLE].item); style->bindings[MS_STYLE_BINDING_POLAROFFSET_ANGLE].item = msStrdup(msyystring_buffer); @@ -3203,7 +3261,13 @@ int loadClass(classObj *class, layerObj *layer) break; /* for string loads */ case(DEBUG): if((class->debug = getSymbol(3, MS_ON,MS_OFF, MS_NUMBER)) == -1) return(-1); - if(class->debug == MS_NUMBER) class->debug = (int) msyynumber; + if(class->debug == MS_NUMBER) { + if(msCheckNumber(msyynumber, MS_NUM_CHECK_RANGE, 0, 5) == MS_FAILURE) { + msSetError(MS_MISCERR, "Invalid DEBUG level, must be between 0 and 5 (line %d)", "loadClass()", msyylineno); + return(-1); + } + class->debug = (int) msyynumber; + } break; case(EOF): msSetError(MS_EOFERR, NULL, "loadClass()"); @@ -3264,17 +3328,17 @@ int loadClass(classObj *class, layerObj *layer) break; case(MAXSCALE): case(MAXSCALEDENOM): - if(getDouble(&(class->maxscaledenom)) == -1) return(-1); + if(getDouble(&(class->maxscaledenom), MS_NUM_CHECK_GTE, 0, -1) == -1) return(-1); break; case(METADATA): if(loadHashTable(&(class->metadata)) != MS_SUCCESS) return(-1); break; case(MINSCALE): case(MINSCALEDENOM): - if(getDouble(&(class->minscaledenom)) == -1) return(-1); + if(getDouble(&(class->minscaledenom), MS_NUM_CHECK_GTE, 0, -1) == -1) return(-1); break; case(MINFEATURESIZE): - if(getInteger(&(class->minfeaturesize)) == -1) return(-1); + if(getInteger(&(class->minfeaturesize), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; case(NAME): if(getString(&class->name) == MS_FAILURE) return(-1); @@ -3335,33 +3399,33 @@ int loadClass(classObj *class, layerObj *layer) ** for backwards compatability, these are shortcuts for style 0 */ case(BACKGROUNDCOLOR): - if (msMaybeAllocateClassStyle(class, 0)) return MS_FAILURE; + if (msMaybeAllocateClassStyle(class, 0)) return(-1); if(loadColor(&(class->styles[0]->backgroundcolor), NULL) != MS_SUCCESS) return(-1); break; case(COLOR): - if (msMaybeAllocateClassStyle(class, 0)) return MS_FAILURE; + if (msMaybeAllocateClassStyle(class, 0)) return(-1); if(loadColor(&(class->styles[0]->color), NULL) != MS_SUCCESS) return(-1); class->numstyles = 1; /* must *always* set a color or outlinecolor */ break; case(MAXSIZE): - if (msMaybeAllocateClassStyle(class, 0)) return MS_FAILURE; - if(getDouble(&(class->styles[0]->maxsize)) == -1) return(-1); + if (msMaybeAllocateClassStyle(class, 0)) return(-1); + if(getDouble(&(class->styles[0]->maxsize), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; case(MINSIZE): - if (msMaybeAllocateClassStyle(class, 0)) return MS_FAILURE; - if(getDouble(&(class->styles[0]->minsize)) == -1) return(-1); + if (msMaybeAllocateClassStyle(class, 0)) return(-1); + if(getDouble(&(class->styles[0]->minsize), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; case(OUTLINECOLOR): - if (msMaybeAllocateClassStyle(class, 0)) return MS_FAILURE; + if (msMaybeAllocateClassStyle(class, 0)) return(-1); if(loadColor(&(class->styles[0]->outlinecolor), NULL) != MS_SUCCESS) return(-1); class->numstyles = 1; /* must *always* set a color, symbol or outlinecolor */ break; case(SIZE): - if (msMaybeAllocateClassStyle(class, 0)) return MS_FAILURE; - if(getDouble(&(class->styles[0]->size)) == -1) return(-1); + if (msMaybeAllocateClassStyle(class, 0)) return(-1); + if(getDouble(&(class->styles[0]->size), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; case(SYMBOL): - if (msMaybeAllocateClassStyle(class, 0)) return MS_FAILURE; + if (msMaybeAllocateClassStyle(class, 0)) return(-1); if((state = getSymbol(2, MS_NUMBER,MS_STRING)) == -1) return(-1); if(state == MS_NUMBER) class->styles[0]->symbol = (int) msyynumber; @@ -3377,33 +3441,33 @@ int loadClass(classObj *class, layerObj *layer) ** for backwards compatability, these are shortcuts for style 1 */ case(OVERLAYBACKGROUNDCOLOR): - if (msMaybeAllocateClassStyle(class, 1)) return MS_FAILURE; + if (msMaybeAllocateClassStyle(class, 1)) return(-1); if(loadColor(&(class->styles[1]->backgroundcolor), NULL) != MS_SUCCESS) return(-1); break; case(OVERLAYCOLOR): - if (msMaybeAllocateClassStyle(class, 1)) return MS_FAILURE; + if (msMaybeAllocateClassStyle(class, 1)) return(-1); if(loadColor(&(class->styles[1]->color), NULL) != MS_SUCCESS) return(-1); class->numstyles = 2; /* must *always* set a color, symbol or outlinecolor */ break; case(OVERLAYMAXSIZE): - if (msMaybeAllocateClassStyle(class, 1)) return MS_FAILURE; - if(getDouble(&(class->styles[1]->maxsize)) == -1) return(-1); + if (msMaybeAllocateClassStyle(class, 1)) return(-1); + if(getDouble(&(class->styles[1]->maxsize), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; case(OVERLAYMINSIZE): - if (msMaybeAllocateClassStyle(class, 1)) return MS_FAILURE; - if(getDouble(&(class->styles[1]->minsize)) == -1) return(-1); + if (msMaybeAllocateClassStyle(class, 1)) return(-1); + if(getDouble(&(class->styles[1]->minsize), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; case(OVERLAYOUTLINECOLOR): - if (msMaybeAllocateClassStyle(class, 1)) return MS_FAILURE; + if (msMaybeAllocateClassStyle(class, 1)) return(-1); if(loadColor(&(class->styles[1]->outlinecolor), NULL) != MS_SUCCESS) return(-1); class->numstyles = 2; /* must *always* set a color, symbol or outlinecolor */ break; case(OVERLAYSIZE): - if (msMaybeAllocateClassStyle(class, 1)) return MS_FAILURE; - if(getDouble(&(class->styles[1]->size)) == -1) return(-1); + if (msMaybeAllocateClassStyle(class, 1)) return(-1); + if(getDouble(&(class->styles[1]->size), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; case(OVERLAYSYMBOL): - if (msMaybeAllocateClassStyle(class, 1)) return MS_FAILURE; + if (msMaybeAllocateClassStyle(class, 1)) return(-1); if((state = getSymbol(2, MS_NUMBER,MS_STRING)) == -1) return(-1); if(state == MS_NUMBER) class->styles[1]->symbol = (int) msyynumber; @@ -4072,20 +4136,12 @@ int loadLayerCompositer(LayerCompositer *compositer) { case END: return MS_SUCCESS; case OPACITY: - if (getInteger(&(compositer->opacity)) == -1) { + if (getInteger(&(compositer->opacity), MS_NUM_CHECK_RANGE, 0, 100) == -1) { if (compositer->filter) { msFree(compositer->filter->filter); msFree(compositer->filter); compositer->filter=NULL; - } - return MS_FAILURE; } - if(compositer->opacity<0 || compositer->opacity>100) { - if (compositer->filter) { - msFree(compositer->filter->filter); - msFree(compositer->filter); - compositer->filter=NULL; - } msSetError(MS_PARSEERR,"OPACITY must be between 0 and 100 (line %d)","loadLayerCompositer()",msyylineno); return MS_FAILURE; } @@ -4194,7 +4250,13 @@ int loadLayer(layerObj *layer, mapObj *map) break; case(DEBUG): if((layer->debug = getSymbol(3, MS_ON,MS_OFF, MS_NUMBER)) == -1) return(-1); - if(layer->debug == MS_NUMBER) layer->debug = (int) msyynumber; + if(layer->debug == MS_NUMBER) { + if(msCheckNumber(msyynumber, MS_NUM_CHECK_RANGE, 0, 5) == MS_FAILURE) { + msSetError(MS_MISCERR, "Invalid DEBUG level, must be between 0 and 5 (line %d)", "loadLayer()", msyylineno); + return(-1); + } + layer->debug = (int) msyynumber; + } break; case(DUMP): if((layer->dump = getSymbol(2, MS_TRUE,MS_FALSE)) == -1) return(-1); @@ -4215,10 +4277,10 @@ int loadLayer(layerObj *layer, mapObj *map) return(0); break; case(EXTENT): { - if(getDouble(&(layer->extent.minx)) == -1) return(-1); - if(getDouble(&(layer->extent.miny)) == -1) return(-1); - if(getDouble(&(layer->extent.maxx)) == -1) return(-1); - if(getDouble(&(layer->extent.maxy)) == -1) return(-1); + if(getDouble(&(layer->extent.minx), MS_NUM_CHECK_NONE, -1, -1) == -1) return(-1); + if(getDouble(&(layer->extent.miny), MS_NUM_CHECK_NONE, -1, -1) == -1) return(-1); + if(getDouble(&(layer->extent.maxx), MS_NUM_CHECK_NONE, -1, -1) == -1) return(-1); + if(getDouble(&(layer->extent.maxy), MS_NUM_CHECK_NONE, -1, -1) == -1) return(-1); if (!MS_VALID_EXTENT(layer->extent)) { msSetError(MS_MISCERR, "Given layer extent is invalid. Check that it is in the form: minx, miny, maxx, maxy", "loadLayer()"); return(-1); @@ -4345,11 +4407,11 @@ int loadLayer(layerObj *layer, mapObj *map) break; case(LABELMAXSCALE): case(LABELMAXSCALEDENOM): - if(getDouble(&(layer->labelmaxscaledenom)) == -1) return(-1); + if(getDouble(&(layer->labelmaxscaledenom), MS_NUM_CHECK_GTE, 0, -1) == -1) return(-1); break; case(LABELMINSCALE): case(LABELMINSCALEDENOM): - if(getDouble(&(layer->labelminscaledenom)) == -1) return(-1); + if(getDouble(&(layer->labelminscaledenom), MS_NUM_CHECK_GTE, 0, -1) == -1) return(-1); break; case(LABELREQUIRES): if(getString(&layer->labelrequires) == MS_FAILURE) return(-1); /* getString() cleans up previously allocated string */ @@ -4376,27 +4438,27 @@ int loadLayer(layerObj *layer, mapObj *map) } break; case(MAXFEATURES): - if(getInteger(&(layer->maxfeatures)) == -1) return(-1); + if(getInteger(&(layer->maxfeatures), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; case(MAXSCALE): case(MAXSCALEDENOM): - if(getDouble(&(layer->maxscaledenom)) == -1) return(-1); + if(getDouble(&(layer->maxscaledenom), MS_NUM_CHECK_GTE, 0, -1) == -1) return(-1); break; case(MAXGEOWIDTH): - if(getDouble(&(layer->maxgeowidth)) == -1) return(-1); + if(getDouble(&(layer->maxgeowidth), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; case(METADATA): if(loadHashTable(&(layer->metadata)) != MS_SUCCESS) return(-1); break; case(MINSCALE): case(MINSCALEDENOM): - if(getDouble(&(layer->minscaledenom)) == -1) return(-1); + if(getDouble(&(layer->minscaledenom), MS_NUM_CHECK_GTE, 0, -1) == -1) return(-1); break; case(MINGEOWIDTH): - if(getDouble(&(layer->mingeowidth)) == -1) return(-1); + if(getDouble(&(layer->mingeowidth), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; case(MINFEATURESIZE): - if(getInteger(&(layer->minfeaturesize)) == -1) return(-1); + if(getInteger(&(layer->minfeaturesize), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; case(NAME): if(getString(&layer->name) == MS_FAILURE) return(-1); @@ -4413,7 +4475,7 @@ int loadLayer(layerObj *layer, mapObj *map) case(TRANSPARENCY): /* keyword supported for mapfile backwards compatability */ { int opacity; - if (getInteger(&opacity) == -1) return(-1); + if (getInteger(&opacity, MS_NUM_CHECK_RANGE, 0, 100) == -1) return(-1); if(opacity != 100) { if(layer->compositer) { msSetError(MS_PARSEERR, "Cannot use OPACITY and COMPOSITER simultaneously at the LAYER level (line %d)", "loadLayer()", msyylineno ); @@ -4502,7 +4564,7 @@ int loadLayer(layerObj *layer, mapObj *map) break; case(SYMBOLSCALE): case(SYMBOLSCALEDENOM): - if(getDouble(&(layer->symbolscaledenom)) == -1) return(-1); + if(getDouble(&(layer->symbolscaledenom), MS_NUM_CHECK_GTE, 1, -1) == -1) return(-1); break; case(TEMPLATE): if(getString(&layer->template) == MS_FAILURE) return(-1); /* getString() cleans up previously allocated string */ @@ -4549,7 +4611,7 @@ int loadLayer(layerObj *layer, mapObj *map) } break; case(TOLERANCE): - if(getDouble(&(layer->tolerance)) == -1) return(-1); + if(getDouble(&(layer->tolerance), MS_NUM_CHECK_GTE, 0, -1) == -1) return(-1); break; case(TOLERANCEUNITS): if((layer->toleranceunits = getSymbol(8, MS_INCHES,MS_FEET,MS_MILES,MS_METERS,MS_KILOMETERS,MS_NAUTICALMILES,MS_DD,MS_PIXELS)) == -1) return(-1); @@ -4573,7 +4635,7 @@ int loadLayer(layerObj *layer, mapObj *map) if(loadExpression(&(layer->utfdata)) == -1) return(-1); /* loadExpression() cleans up previously allocated expression */ break; case(UTFITEM): - if(getString(&layer->utfitem) == MS_FAILURE) return(-1); /* loadExpression() cleans up previously allocated expression */ + if(getString(&layer->utfitem) == MS_FAILURE) return(-1); break; case(VALIDATION): if(loadHashTable(&(layer->validation)) != MS_SUCCESS) return(-1); @@ -4891,10 +4953,10 @@ int loadReferenceMap(referenceMapObj *ref, mapObj *map) if(loadColor(&(ref->color), NULL) != MS_SUCCESS) return(-1); break; case(EXTENT): - if(getDouble(&(ref->extent.minx)) == -1) return(-1); - if(getDouble(&(ref->extent.miny)) == -1) return(-1); - if(getDouble(&(ref->extent.maxx)) == -1) return(-1); - if(getDouble(&(ref->extent.maxy)) == -1) return(-1); + if(getDouble(&(ref->extent.minx), MS_NUM_CHECK_NONE, -1, -1) == -1) return(-1); + if(getDouble(&(ref->extent.miny), MS_NUM_CHECK_NONE, -1, -1) == -1) return(-1); + if(getDouble(&(ref->extent.maxx), MS_NUM_CHECK_NONE, -1, -1) == -1) return(-1); + if(getDouble(&(ref->extent.maxy), MS_NUM_CHECK_NONE, -1, -1) == -1) return(-1); if (!MS_VALID_EXTENT(ref->extent)) { msSetError(MS_MISCERR, "Given reference extent is invalid. Check that it " \ "is in the form: minx, miny, maxx, maxy", "loadReferenceMap()"); @@ -4908,8 +4970,8 @@ int loadReferenceMap(referenceMapObj *ref, mapObj *map) if(loadColor(&(ref->outlinecolor), NULL) != MS_SUCCESS) return(-1); break; case(SIZE): - if(getInteger(&(ref->width)) == -1) return(-1); - if(getInteger(&(ref->height)) == -1) return(-1); + if(getInteger(&(ref->width), MS_NUM_CHECK_RANGE, 5, ref->map->maxsize) == -1) return(-1); // is 5 reasonable? + if(getInteger(&(ref->height), MS_NUM_CHECK_RANGE, 5, ref->map->maxsize) == -1) return(-1); break; case(STATUS): if((ref->status = getSymbol(2, MS_ON,MS_OFF)) == -1) return(-1); @@ -4917,22 +4979,26 @@ int loadReferenceMap(referenceMapObj *ref, mapObj *map) case(MARKER): if((state = getSymbol(2, MS_NUMBER,MS_STRING)) == -1) return(-1); - if(state == MS_NUMBER) + if(state == MS_NUMBER) { + if(msCheckNumber(msyynumber, MS_NUM_CHECK_GTE, 0, -1) == MS_FAILURE) { + msSetError(MS_MISCERR, "Invalid MARKER, must be greater than 0 (line %d)", "loadReferenceMap()", msyylineno); + return(-1); + } ref->marker = (int) msyynumber; - else { + } else { if (ref->markername != NULL) msFree(ref->markername); ref->markername = msStrdup(msyystring_buffer); } break; case(MARKERSIZE): - if(getInteger(&(ref->markersize)) == -1) return(-1); + if(getInteger(&(ref->markersize), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; case(MINBOXSIZE): - if(getInteger(&(ref->minboxsize)) == -1) return(-1); + if(getInteger(&(ref->minboxsize), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; case(MAXBOXSIZE): - if(getInteger(&(ref->maxboxsize)) == -1) return(-1); + if(getInteger(&(ref->maxboxsize), MS_NUM_CHECK_GT, 0, -1) == -1) return(-1); break; case(REFERENCE): break; /* for string loads */ @@ -5278,12 +5344,12 @@ int loadLegend(legendObj *legend, mapObj *map) if((legend->interlace = getSymbol(2, MS_ON,MS_OFF)) == -1) return(-1); break; case(KEYSIZE): - if(getInteger(&(legend->keysizex)) == -1) return(-1); - if(getInteger(&(legend->keysizey)) == -1) return(-1); + if(getInteger(&(legend->keysizex), MS_NUM_CHECK_RANGE, MS_LEGEND_KEYSIZE_MIN, MS_LEGEND_KEYSIZE_MAX) == -1) return(-1); + if(getInteger(&(legend->keysizey), MS_NUM_CHECK_RANGE, MS_LEGEND_KEYSIZE_MIN, MS_LEGEND_KEYSIZE_MAX) == -1) return(-1); break; case(KEYSPACING): - if(getInteger(&(legend->keyspacingx)) == -1) return(-1); - if(getInteger(&(legend->keyspacingy)) == -1) return(-1); + if(getInteger(&(legend->keyspacingx), MS_NUM_CHECK_RANGE, MS_LEGEND_KEYSPACING_MIN, MS_LEGEND_KEYSPACING_MAX) == -1) return(-1); + if(getInteger(&(legend->keyspacingy), MS_NUM_CHECK_RANGE, MS_LEGEND_KEYSPACING_MIN, MS_LEGEND_KEYSPACING_MAX) == -1) return(-1); break; case(LABEL): if(loadLabel(&(legend->label)) == -1) return(-1); @@ -5447,7 +5513,7 @@ int loadScalebar(scalebarObj *scalebar) if((scalebar->interlace = getSymbol(2, MS_ON,MS_OFF)) == -1) return(-1); break; case(INTERVALS): - if(getInteger(&(scalebar->intervals)) == -1) return(-1); + if(getInteger(&(scalebar->intervals), MS_NUM_CHECK_RANGE, MS_SCALEBAR_INTERVALS_MIN, MS_SCALEBAR_INTERVALS_MAX) == -1) return(-1); break; case(LABEL): if(loadLabel(&(scalebar->label)) == -1) return(-1); @@ -5466,14 +5532,14 @@ int loadScalebar(scalebarObj *scalebar) case(SCALEBAR): break; /* for string loads */ case(SIZE): - if(getInteger(&(scalebar->width)) == -1) return(-1); - if(getInteger(&(scalebar->height)) == -1) return(-1); + if(getInteger(&(scalebar->width), MS_NUM_CHECK_RANGE, MS_SCALEBAR_WIDTH_MIN, MS_SCALEBAR_WIDTH_MAX) == -1) return(-1); + if(getInteger(&(scalebar->height), MS_NUM_CHECK_RANGE, MS_SCALEBAR_HEIGHT_MIN, MS_SCALEBAR_HEIGHT_MAX) == -1) return(-1); break; case(STATUS): if((scalebar->status = getSymbol(3, MS_ON,MS_OFF,MS_EMBED)) == -1) return(-1); break; case(STYLE): - if(getInteger(&(scalebar->style)) == -1) return(-1); + if(getInteger(&(scalebar->style), MS_NUM_CHECK_RANGE, 0, 1) == -1) return(-1); // only 2 styles: 0 and 1 break; case(TRANSPARENT): if((scalebar->transparent = getSymbol(2, MS_ON,MS_OFF)) == -1) return(-1); @@ -5482,8 +5548,8 @@ int loadScalebar(scalebarObj *scalebar) if((scalebar->units = getSymbol(6, MS_INCHES,MS_FEET,MS_MILES,MS_METERS,MS_KILOMETERS,MS_NAUTICALMILES)) == -1) return(-1); break; case(OFFSET): - if(getInteger(&(scalebar->offsetx)) == -1) return(-1); - if(getInteger(&(scalebar->offsety)) == -1) return(-1); + if(getInteger(&(scalebar->offsetx), MS_NUM_CHECK_RANGE, MS_SCALEBAR_OFFSET_MIN, MS_SCALEBAR_OFFSET_MAX) == -1) return(-1); + if(getInteger(&(scalebar->offsety), MS_NUM_CHECK_RANGE, MS_SCALEBAR_OFFSET_MIN, MS_SCALEBAR_OFFSET_MAX) == -1) return(-1); break; default: if(strlen(msyystring_buffer) > 0) { @@ -5581,8 +5647,10 @@ void initQueryMap(queryMapObj *querymap) MS_INIT_COLOR(querymap->color, 255,255,0,255); /* yellow */ } -int loadQueryMap(queryMapObj *querymap) +int loadQueryMap(queryMapObj *querymap, mapObj *map) { + querymap->map = (mapObj *)map; + for(;;) { switch(msyylex()) { case(QUERYMAP): @@ -5597,8 +5665,17 @@ int loadQueryMap(queryMapObj *querymap) return(0); break; case(SIZE): - if(getInteger(&(querymap->width)) == -1) return(-1); - if(getInteger(&(querymap->height)) == -1) return(-1); + /* + ** we do -1 (and avoid 0) here to maintain backwards compatability as older versions write "SIZE -1 -1" when saving a mapfile + */ + if(getInteger(&(querymap->width), MS_NUM_CHECK_RANGE, -1, querymap->map->maxsize) == -1 || querymap->width == 0) { + msSetError(MS_MISCERR, "Invalid SIZE value (line %d)", "loadQueryMap()", msyylineno); + return(-1); + } + if(getInteger(&(querymap->height), MS_NUM_CHECK_RANGE, -1, querymap->map->maxsize) == -1 || querymap->height == 0) { + msSetError(MS_MISCERR, "Invalid SIZE value (line %d)", "loadQueryMap()", msyylineno); + return(-1); + } break; case(STATUS): if((querymap->status = getSymbol(2, MS_ON,MS_OFF)) == -1) return(-1); @@ -5633,7 +5710,7 @@ int msUpdateQueryMapFromString(queryMapObj *querymap, char *string, int url_stri msyylineno = 1; /* start at line 1 */ - if(loadQueryMap(querymap) == -1) { + if(loadQueryMap(querymap, querymap->map) == -1) { msReleaseLock( TLOCK_PARSER ); return MS_FAILURE; /* parse error */; } @@ -5651,7 +5728,8 @@ static void writeQueryMap(FILE *stream, int indent, queryMapObj *querymap) writeBlockBegin(stream, indent, "QUERYMAP"); MS_INIT_COLOR(c,255,255,0,255); writeColor(stream, indent, "COLOR", &c, &(querymap->color)); - writeDimension(stream, indent, "SIZE", querymap->width, querymap->height, NULL, NULL); + if(querymap->width != -1 && querymap->height != -1) // don't write SIZE if not explicitly set + writeDimension(stream, indent, "SIZE", querymap->width, querymap->height, NULL, NULL); writeKeyword(stream, indent, "STATUS", querymap->status, 2, MS_ON, "ON", MS_OFF, "OFF"); writeKeyword(stream, indent, "STYLE", querymap->style, 3, MS_NORMAL, "NORMAL", MS_HILITE, "HILITE", MS_SELECTED, "SELECTED"); writeBlockEnd(stream, indent, "QUERYMAP"); @@ -5802,10 +5880,10 @@ int loadWeb(webObj *web, mapObj *map) if(getString(&web->error) == MS_FAILURE) return(-1); break; case(EXTENT): - if(getDouble(&(web->extent.minx)) == -1) return(-1); - if(getDouble(&(web->extent.miny)) == -1) return(-1); - if(getDouble(&(web->extent.maxx)) == -1) return(-1); - if(getDouble(&(web->extent.maxy)) == -1) return(-1); + if(getDouble(&(web->extent.minx), MS_NUM_CHECK_NONE, -1, -1) == -1) return(-1); + if(getDouble(&(web->extent.miny), MS_NUM_CHECK_NONE, -1, -1) == -1) return(-1); + if(getDouble(&(web->extent.maxx), MS_NUM_CHECK_NONE, -1, -1) == -1) return(-1); + if(getDouble(&(web->extent.maxy), MS_NUM_CHECK_NONE, -1, -1) == -1) return(-1); if (!MS_VALID_EXTENT(web->extent)) { msSetError(MS_MISCERR, "Given web extent is invalid. Check that it is in the form: minx, miny, maxx, maxy", "loadWeb()"); return(-1); @@ -5852,7 +5930,7 @@ int loadWeb(webObj *web, mapObj *map) break; case(MAXSCALE): case(MAXSCALEDENOM): - if(getDouble(&web->maxscaledenom) == -1) return(-1); + if(getDouble(&web->maxscaledenom, MS_NUM_CHECK_GTE, 0, -1) == -1) return(-1); break; case(MAXTEMPLATE): if(getString(&web->maxtemplate) == MS_FAILURE) return(-1); @@ -5862,7 +5940,7 @@ int loadWeb(webObj *web, mapObj *map) break; case(MINSCALE): case(MINSCALEDENOM): - if(getDouble(&web->minscaledenom) == -1) return(-1); + if(getDouble(&web->minscaledenom, MS_NUM_CHECK_GTE, 0, -1) == -1) return(-1); break; case(MINTEMPLATE): if(getString(&web->mintemplate) == MS_FAILURE) return(-1); @@ -6329,7 +6407,13 @@ static int loadMapInternal(mapObj *map) break; case(DEBUG): if((map->debug = getSymbol(3, MS_ON,MS_OFF, MS_NUMBER)) == -1) return MS_FAILURE; - if(map->debug == MS_NUMBER) map->debug = (int) msyynumber; + if(map->debug == MS_NUMBER) { + if(msCheckNumber(msyynumber, MS_NUM_CHECK_RANGE, 0, 5) == MS_FAILURE) { + msSetError(MS_MISCERR, "Invalid DEBUG level, must be between 0 and 5 (line %d)", "msLoadMap()", msyylineno); + return(-1); + } + map->debug = (int) msyynumber; + } break; case(END): if(msyyin) { @@ -6360,10 +6444,10 @@ static int loadMapInternal(mapObj *map) msSetError(MS_EOFERR, NULL, "msLoadMap()"); return MS_FAILURE; case(EXTENT): { - if(getDouble(&(map->extent.minx)) == -1) return MS_FAILURE; - if(getDouble(&(map->extent.miny)) == -1) return MS_FAILURE; - if(getDouble(&(map->extent.maxx)) == -1) return MS_FAILURE; - if(getDouble(&(map->extent.maxy)) == -1) return MS_FAILURE; + if(getDouble(&(map->extent.minx), MS_NUM_CHECK_NONE, -1, -1) == -1) return MS_FAILURE; + if(getDouble(&(map->extent.miny), MS_NUM_CHECK_NONE, -1, -1) == -1) return MS_FAILURE; + if(getDouble(&(map->extent.maxx), MS_NUM_CHECK_NONE, -1, -1) == -1) return MS_FAILURE; + if(getDouble(&(map->extent.maxy), MS_NUM_CHECK_NONE, -1, -1) == -1) return MS_FAILURE; if (!MS_VALID_EXTENT(map->extent)) { msSetError(MS_MISCERR, "Given map extent is invalid. Check that it " \ "is in the form: minx, miny, maxx, maxy", "loadMapInternal()"); @@ -6373,7 +6457,7 @@ static int loadMapInternal(mapObj *map) break; case(ANGLE): { double rotation_angle; - if(getDouble(&(rotation_angle)) == -1) return MS_FAILURE; + if(getDouble(&(rotation_angle), MS_NUM_CHECK_RANGE, -360, 360) == -1) return MS_FAILURE; msMapSetRotation( map, rotation_angle ); } break; @@ -6387,7 +6471,7 @@ static int loadMapInternal(mapObj *map) if(loadColor(&(map->imagecolor), NULL) != MS_SUCCESS) return MS_FAILURE; break; case(IMAGEQUALITY): - if(getInteger(&(map->imagequality)) == -1) return MS_FAILURE; + if(getInteger(&(map->imagequality), MS_NUM_CHECK_RANGE, 0, 100) == -1) return MS_FAILURE; break; case(IMAGETYPE): msFree(map->imagetype); @@ -6419,7 +6503,7 @@ static int loadMapInternal(mapObj *map) foundMapToken = MS_TRUE; break; case(MAXSIZE): - if(getInteger(&(map->maxsize)) == -1) return MS_FAILURE; + if(getInteger(&(map->maxsize), MS_NUM_CHECK_GT, 0, -1) == -1) return MS_FAILURE; break; case(NAME): free(map->name); @@ -6430,20 +6514,20 @@ static int loadMapInternal(mapObj *map) if(loadProjection(&map->projection) == -1) return MS_FAILURE; break; case(QUERYMAP): - if(loadQueryMap(&(map->querymap)) == -1) return MS_FAILURE; + if(loadQueryMap(&(map->querymap), map) == -1) return MS_FAILURE; break; case(REFERENCE): if(loadReferenceMap(&(map->reference), map) == -1) return MS_FAILURE; break; case(RESOLUTION): - if(getDouble(&(map->resolution)) == -1) return MS_FAILURE; + if(getDouble(&(map->resolution), MS_NUM_CHECK_RANGE, MS_RESOLUTION_MIN, MS_RESOLUTION_MAX) == -1) return MS_FAILURE; break; case(DEFRESOLUTION): - if(getDouble(&(map->defresolution)) == -1) return MS_FAILURE; + if(getDouble(&(map->defresolution), MS_NUM_CHECK_RANGE, MS_RESOLUTION_MIN, MS_RESOLUTION_MAX) == -1) return MS_FAILURE; break; case(SCALE): case(SCALEDENOM): - if(getDouble(&(map->scaledenom)) == -1) return MS_FAILURE; + if(getDouble(&(map->scaledenom), MS_NUM_CHECK_GTE, 1, -1) == -1) return MS_FAILURE; break; case(SCALEBAR): if(loadScalebar(&(map->scalebar)) == -1) return MS_FAILURE; @@ -6452,8 +6536,8 @@ static int loadMapInternal(mapObj *map) if(getString(&map->shapepath) == MS_FAILURE) return MS_FAILURE; break; case(SIZE): - if(getInteger(&(map->width)) == -1) return MS_FAILURE; - if(getInteger(&(map->height)) == -1) return MS_FAILURE; + if(getInteger(&(map->width), MS_NUM_CHECK_RANGE, 1, map->maxsize) == -1) return MS_FAILURE; + if(getInteger(&(map->height), MS_NUM_CHECK_RANGE, 1, map->maxsize) == -1) return MS_FAILURE; break; case(STATUS): if((map->status = getSymbol(2, MS_ON,MS_OFF)) == -1) return MS_FAILURE; @@ -6626,10 +6710,13 @@ mapObj *msLoadMap(const char *filename, const char *new_mappath) if (msyyin == NULL) { msSetError(MS_IOERR, "tmpfile() failed to create temporary file", "msLoadMap()"); msReleaseLock( TLOCK_PARSER ); + msFreeMap(map); + return NULL; } if (msTransformXmlMapfile(getenv("MS_XMLMAPFILE_XSLT"), filename, msyyin) != MS_SUCCESS) { fclose(msyyin); + msFreeMap(map) return NULL; } fseek ( msyyin , 0 , SEEK_SET ); @@ -6638,6 +6725,7 @@ mapObj *msLoadMap(const char *filename, const char *new_mappath) if((msyyin = fopen(filename,"r")) == NULL) { msSetError(MS_IOERR, "(%s)", "msLoadMap()", filename); msReleaseLock( TLOCK_PARSER ); + msFreeMap(map); return NULL; } #ifdef USE_XMLMAPFILE @@ -6654,8 +6742,9 @@ mapObj *msLoadMap(const char *filename, const char *new_mappath) /* of the mapfile as the default path */ if(NULL == getcwd(szCWDPath, MS_MAXPATHLEN)) { msSetError(MS_MISCERR, "getcwd() returned a too long path", "msLoadMap()"); - msFreeMap(map); msReleaseLock( TLOCK_PARSER ); + msFreeMap(map); + return NULL; } if (new_mappath) @@ -6717,10 +6806,10 @@ int msUpdateMapFromURL(mapObj *map, char *variable, char *string) msyystring = string; msyylex(); - if(getDouble(&(map->extent.minx)) == -1) break; - if(getDouble(&(map->extent.miny)) == -1) break; - if(getDouble(&(map->extent.maxx)) == -1) break; - if(getDouble(&(map->extent.maxy)) == -1) break; + if(getDouble(&(map->extent.minx), MS_NUM_CHECK_NONE, -1, -1) == -1) break; + if(getDouble(&(map->extent.miny), MS_NUM_CHECK_NONE, -1, -1) == -1) break; + if(getDouble(&(map->extent.maxx), MS_NUM_CHECK_NONE, -1, -1) == -1) break; + if(getDouble(&(map->extent.maxy), MS_NUM_CHECK_NONE, -1, -1) == -1) break; if (!MS_VALID_EXTENT(map->extent)) { msSetError(MS_MISCERR, "Given map extent is invalid. Check that it is in the form: minx, miny, maxx, maxy", "msLoadMapParameterFromUrl()"); break; @@ -6733,7 +6822,7 @@ int msUpdateMapFromURL(mapObj *map, char *variable, char *string) msyystring = string; msyylex(); - if(getDouble(&(rotation_angle)) == -1) break; + if(getDouble(&(rotation_angle), MS_NUM_CHECK_RANGE, -360, 360) == -1) break; msMapSetRotation( map, rotation_angle ); } break; @@ -6791,7 +6880,7 @@ int msUpdateMapFromURL(mapObj *map, char *variable, char *string) switch(msyylex()) { case STYLE: - if(getInteger(&k) == -1) return MS_FAILURE; + if(getInteger(&k, MS_NUM_CHECK_NONE, -1, -1) == -1) return MS_FAILURE; if(k>=GET_LAYER(map, i)->class[j]->numstyles || k<0) { msSetError(MS_MISCERR, "Style to be modified not valid.", "msUpdateMapFromURL()"); return MS_FAILURE; @@ -6799,7 +6888,7 @@ int msUpdateMapFromURL(mapObj *map, char *variable, char *string) if(msUpdateStyleFromString((GET_LAYER(map, i))->class[j]->styles[k], string, MS_TRUE) != MS_SUCCESS) return MS_FAILURE; break; case LABEL: - if(getInteger(&k) == -1) return MS_FAILURE; + if(getInteger(&k, MS_NUM_CHECK_NONE, -1, -1) == -1) return MS_FAILURE; if(k>=GET_LAYER(map, i)->class[j]->numlabels || k<0) { msSetError(MS_MISCERR, "Label to be modified not valid.", "msUpdateMapFromURL()"); return MS_FAILURE; @@ -6835,14 +6924,14 @@ int msUpdateMapFromURL(mapObj *map, char *variable, char *string) msyystring = string; msyylex(); - if(getDouble(&(map->resolution)) == -1) break; + if(getDouble(&(map->resolution), MS_NUM_CHECK_RANGE, MS_RESOLUTION_MIN, MS_RESOLUTION_MAX) == -1) break; break; case(DEFRESOLUTION): msyystate = MS_TOKENIZE_URL_STRING; msyystring = string; msyylex(); - if(getDouble(&(map->defresolution)) == -1) break; + if(getDouble(&(map->defresolution), MS_NUM_CHECK_RANGE, MS_RESOLUTION_MIN, MS_RESOLUTION_MAX) == -1) break; break; case(SCALEBAR): return msUpdateScalebarFromString(&(map->scalebar), string, MS_TRUE); @@ -6851,13 +6940,15 @@ int msUpdateMapFromURL(mapObj *map, char *variable, char *string) msyystring = string; msyylex(); - if(getInteger(&(map->width)) == -1) break; - if(getInteger(&(map->height)) == -1) break; - - if(map->width > map->maxsize || map->height > map->maxsize || map->width < 0 || map->height < 0) { + if(getInteger(&(map->width), MS_NUM_CHECK_RANGE, 1, map->maxsize) == -1) { + msSetError(MS_WEBERR, "Image size out of range.", "msUpdateMapFromURL()"); + break; + } + if(getInteger(&(map->height), MS_NUM_CHECK_RANGE, 1, map->maxsize) == -1) { msSetError(MS_WEBERR, "Image size out of range.", "msUpdateMapFromURL()"); break; } + msMapComputeGeotransform( map ); break; case(TRANSPARENT): diff --git a/mapquery.c b/mapquery.c index 98292a251a..a33128f510 100644 --- a/mapquery.c +++ b/mapquery.c @@ -902,6 +902,9 @@ int msQueryByFilter(mapObj *map) status = msLayerWhichShapes(lp, search_rect, MS_TRUE); if(status == MS_DONE) { /* no overlap */ + lp->filteritem = old_filteritem; /* point back to original value */ + msCopyExpression(&lp->filter, &old_filter); /* restore old filter */ + msFreeExpression(&old_filter); msLayerClose(lp); continue; } else if(status != MS_SUCCESS) goto query_error; diff --git a/mapserver.h b/mapserver.h index 86e48b12f7..9950da8225 100644 --- a/mapserver.h +++ b/mapserver.h @@ -484,6 +484,8 @@ extern "C" { #endif + enum MS_NUM_CHECK_TYPES { MS_NUM_CHECK_NONE=0, MS_NUM_CHECK_RANGE, MS_NUM_CHECK_GT, MS_NUM_CHECK_GTE }; + /* General enumerated types - needed by scripts */ enum MS_FILE_TYPE {MS_FILE_MAP, MS_FILE_SYMBOL}; enum MS_UNITS {MS_INCHES, MS_FEET, MS_MILES, MS_METERS, MS_KILOMETERS, MS_DD, MS_PIXELS, MS_PERCENTAGES, MS_NAUTICALMILES}; @@ -899,10 +901,18 @@ extern "C" { /* used to visualize query results */ /************************************************************************/ typedef struct { - int height, width; - int status; - int style; /* HILITE, SELECTED or NORMAL */ - colorObj color; +#ifdef SWIG + %immutable; +#endif /* SWIG */ + struct mapObj *map; ///< Reference to parent :class:`mapObj` +#ifdef SWIG + %mutable; +#endif /* SWIG */ + int height; ///< See :ref:`SIZE ` + int width; ///< See :ref:`SIZE ` + int status; ///< See :ref:`STATUS ` + int style; ///< ``HILITE``, ``SELECTED`` or ``NORMAL`` - see :ref:`STYLE ` + colorObj color; ///< See :ref:`COLOR ` } queryMapObj; /************************************************************************/ @@ -1412,6 +1422,21 @@ typedef struct labelObj labelObj; /************************************************************************/ /* scalebarObj */ /************************************************************************/ + + #define MS_SCALEBAR_INTERVALS_MIN 1 + #define MS_SCALEBAR_INTERVALS_MAX 100 + + #define MS_SCALEBAR_WIDTH_MIN 5 + #define MS_SCALEBAR_WIDTH_MAX 1000 + #define MS_SCALEBAR_HEIGHT_MIN 2 + #define MS_SCALEBAR_HEIGHT_MAX 100 + + #define MS_SCALEBAR_OFFSET_MIN -50 + #define MS_SCALEBAR_OFFSET_MAX 50 + + /** + The :ref:`SCALEBAR ` object + */ typedef struct { colorObj imagecolor; int height, width; @@ -1438,6 +1463,14 @@ typedef struct labelObj labelObj; /* legendObj */ /************************************************************************/ + #define MS_LEGEND_KEYSIZE_MIN 5 + #define MS_LEGEND_KEYSIZE_MAX 200 + #define MS_LEGEND_KEYSPACING_MIN 0 + #define MS_LEGEND_KEYSPACING_MAX 50 + + /** + The :ref:`LEGEND ` object + */ typedef struct { colorObj imagecolor; #ifdef SWIG @@ -1857,7 +1890,12 @@ void msPopulateTextSymbolForLabelAndString(textSymbolObj *ts, labelObj *l, char /* application. */ /************************************************************************/ - /* MAP OBJECT - */ + #define MS_RESOLUTION_MAX 1000 /* applies to resolution and defresolution */ + #define MS_RESOLUTION_MIN 10 + + /** + The :ref:`MAP ` object + */ struct mapObj { /* structure for a map */ char *name; /* small identifier for naming etc. */ int status; /* is map creation on or off */ @@ -2030,8 +2068,8 @@ void msPopulateTextSymbolForLabelAndString(textSymbolObj *ts, labelObj *l, char ** a few other places (like mapscript)... found in mapfile.c */ int getString(char **s); - int getDouble(double *d); - int getInteger(int *i); + int getDouble(double *d, int num_check_type, double value1, double value2); // getDouble(double *d); + int getInteger(int *i, int num_check_type, int value1, int value2); // getInteger(int *i); int getSymbol(int n, ...); int getCharacter(char *c); diff --git a/mapservutil.c b/mapservutil.c index 0246aebf35..d3b9e71886 100644 --- a/mapservutil.c +++ b/mapservutil.c @@ -1520,8 +1520,8 @@ int msCGIDispatchQueryRequest(mapservObj *mapserv) if((status = msExecuteQuery(mapserv->map)) != MS_SUCCESS) return MS_FAILURE; } - if(mapserv->map->querymap.width != -1) mapserv->map->width = mapserv->map->querymap.width; /* make sure we use the right size */ - if(mapserv->map->querymap.height != -1) mapserv->map->height = mapserv->map->querymap.height; + if(mapserv->map->querymap.width > 0 && mapserv->map->querymap.width <= mapserv->map->maxsize) mapserv->map->width = mapserv->map->querymap.width; /* make sure we use the right size */ + if(mapserv->map->querymap.height > 0 && mapserv->map->querymap.height <= mapserv->map->maxsize) mapserv->map->height = mapserv->map->querymap.height; if(mapserv->UseShapes) if(MS_SUCCESS != setExtentFromShapes(mapserv)) diff --git a/mapsymbol.c b/mapsymbol.c index a13e928461..27f766a8a9 100644 --- a/mapsymbol.c +++ b/mapsymbol.c @@ -162,13 +162,16 @@ int loadSymbol(symbolObj *s, char *symbolpath) for(;;) { switch(msyylex()) { case(ANCHORPOINT): - if(getDouble(&(s->anchorpoint_x)) == -1) return MS_FAILURE; - if(getDouble(&(s->anchorpoint_y)) == -1) return MS_FAILURE; - if(s->anchorpoint_x<0 || s->anchorpoint_x>1 || s->anchorpoint_y<0 || s->anchorpoint_y>1) { + if(getDouble(&(s->anchorpoint_x), MS_NUM_CHECK_RANGE, 0, 1) == -1) { + msSetError(MS_SYMERR, "ANCHORPOINT must be between 0 and 1", "loadSymbol()"); + return -1; + } + if(getDouble(&(s->anchorpoint_y), MS_NUM_CHECK_RANGE, 0, 1) == -1) { msSetError(MS_SYMERR, "ANCHORPOINT must be between 0 and 1", "loadSymbol()"); return(-1); - } + } break; + case(ANTIALIAS): /*ignore*/ msyylex(); break; @@ -254,7 +257,7 @@ int loadSymbol(symbolObj *s, char *symbolpath) return(-1); } s->points[s->numpoints].x = atof(msyystring_buffer); /* grab the x */ - if(getDouble(&(s->points[s->numpoints].y)) == -1) return(-1); /* grab the y */ + if(getDouble(&(s->points[s->numpoints].y), MS_NUM_CHECK_NONE, -1, -1) == -1) return(-1); /* grab the y */ if(s->points[s->numpoints].x!=-99) { s->sizex = MS_MAX(s->sizex, s->points[s->numpoints].x); s->sizey = MS_MAX(s->sizey, s->points[s->numpoints].y); @@ -272,7 +275,7 @@ int loadSymbol(symbolObj *s, char *symbolpath) break; case(TRANSPARENT): s->transparent = MS_TRUE; - if(getInteger(&(s->transparentcolor)) == -1) return(-1); + if(getInteger(&(s->transparentcolor), MS_NUM_CHECK_RANGE, 0, 255) == -1) return(-1); break; case(TYPE): if((s->type = getSymbol(8,MS_SYMBOL_VECTOR,MS_SYMBOL_ELLIPSE,MS_SYMBOL_PIXMAP,MS_SYMBOL_SIMPLE,MS_TRUETYPE,MS_SYMBOL_HATCH,MS_SYMBOL_SVG)) == -1) From 9e7578d391d656d78429fe9f345af4dfc8b31aba Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Thu, 9 Feb 2023 15:54:14 +0100 Subject: [PATCH 143/160] Fix build error --- mapfile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapfile.c b/mapfile.c index ea8b348991..c65679cfa2 100644 --- a/mapfile.c +++ b/mapfile.c @@ -6716,7 +6716,7 @@ mapObj *msLoadMap(const char *filename, const char *new_mappath) if (msTransformXmlMapfile(getenv("MS_XMLMAPFILE_XSLT"), filename, msyyin) != MS_SUCCESS) { fclose(msyyin); - msFreeMap(map) + msFreeMap(map); return NULL; } fseek ( msyyin , 0 , SEEK_SET ); From 7643fb863eeefbbd7b685f42febe64e0ee847da2 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sat, 8 Apr 2023 13:18:50 +0200 Subject: [PATCH 144/160] mapfile parser: fix double-free when included file doesn't exist Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=57788 --- maplexer.c | 8 ++++---- maplexer.l | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/maplexer.c b/maplexer.c index 4904a0b035..74a4992043 100644 --- a/maplexer.c +++ b/maplexer.c @@ -4782,10 +4782,6 @@ YY_RULE_SETUP return(-1); } - include_stack[include_stack_ptr] = YY_CURRENT_BUFFER; /* save state */ - include_lineno[include_stack_ptr] = msyylineno; - include_stack_ptr++; - msyyin = fopen(msBuildPath(path, msyybasepath, msyytext), "r"); if(!msyyin) { msSetError(MS_IOERR, "Error opening included file \"%s\".", "msyylex()", msyytext); @@ -4793,6 +4789,10 @@ YY_RULE_SETUP return(-1); } + include_stack[include_stack_ptr] = YY_CURRENT_BUFFER; /* save state */ + include_lineno[include_stack_ptr] = msyylineno; + include_stack_ptr++; + msyy_switch_to_buffer( msyy_create_buffer(msyyin, YY_BUF_SIZE) ); msyylineno = 1; diff --git a/maplexer.l b/maplexer.l index f502565b83..d1122fc4e4 100644 --- a/maplexer.l +++ b/maplexer.l @@ -691,10 +691,6 @@ char path[MS_MAXPATHLEN]; return(-1); } - include_stack[include_stack_ptr] = YY_CURRENT_BUFFER; /* save state */ - include_lineno[include_stack_ptr] = msyylineno; - include_stack_ptr++; - msyyin = fopen(msBuildPath(path, msyybasepath, msyytext), "r"); if(!msyyin) { msSetError(MS_IOERR, "Error opening included file \"%s\".", "msyylex()", msyytext); @@ -702,6 +698,10 @@ char path[MS_MAXPATHLEN]; return(-1); } + include_stack[include_stack_ptr] = YY_CURRENT_BUFFER; /* save state */ + include_lineno[include_stack_ptr] = msyylineno; + include_stack_ptr++; + msyy_switch_to_buffer( msyy_create_buffer(msyyin, YY_BUF_SIZE) ); msyylineno = 1; From 39235b4bcb718a8c1092cfcbf77c642391c4eac0 Mon Sep 17 00:00:00 2001 From: Jeff McKenna Date: Mon, 17 Apr 2023 14:49:30 -0300 Subject: [PATCH 145/160] update for 7.6.5 release --- CMakeLists.txt | 2 +- HISTORY.TXT | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 97bfeade2b..3c02f274ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ include(CheckCSourceCompiles) set (MapServer_VERSION_MAJOR 7) set (MapServer_VERSION_MINOR 6) -set (MapServer_VERSION_REVISION 4) +set (MapServer_VERSION_REVISION 5) set (MapServer_VERSION_SUFFIX "") # Set C++ version diff --git a/HISTORY.TXT b/HISTORY.TXT index 6a0f7378a1..b94aa0ce6d 100644 --- a/HISTORY.TXT +++ b/HISTORY.TXT @@ -12,6 +12,15 @@ For a complete change history, please see the Git log comments. For more details about recent point releases, please see the online changelog at: https://mapserver.org/development/changelog/ +7.6.5 release (2023-04-17) +-------------------------- + +- remove password content from logs (#6621) + +- increase security and stability (#6818) + +see detailed changelog for other fixes + 7.6.4 release (2021-07-12) -------------------------- From 50e4536d4340958d65e5b3f3a6e1e36e63a0fb21 Mon Sep 17 00:00:00 2001 From: Jeff McKenna Date: Thu, 20 Apr 2023 10:07:38 -0300 Subject: [PATCH 146/160] upgrade Travis to Focal --- .travis.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 75ed6d733e..29a0d4cc60 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,19 +2,21 @@ branches: except: - /^(cherry-pick-)?backport-\d+-to-/ -dist: bionic +dist: focal language: php matrix: include: - - php: 7.3 + - php: 8.1 env: - - BUILD_NAME=PHP_7.3_WITH_ASAN + - BUILD_NAME=PHP_8.1_WITH_ASAN + - PYTHON_VERSION=3.8 - - php: 7.4 + - php: 8.2.0 env: - - BUILD_NAME=PHP_7.4_WITH_PROJ6 + - BUILD_NAME=PHP_8.2 + - PYTHON_VERSION=3.9 cache: apt: true From a22ec51adea68ea8cd02c41509839250cc34b192 Mon Sep 17 00:00:00 2001 From: Jeff McKenna Date: Thu, 20 Apr 2023 10:11:06 -0300 Subject: [PATCH 147/160] upgrade Travis to Focal --- ci/travis/after_success.sh | 8 ++-- ci/travis/before_install.sh | 77 +++++++++++++++++++++++++++++++++---- ci/travis/script.sh | 50 +++++++++++++++--------- 3 files changed, 104 insertions(+), 31 deletions(-) diff --git a/ci/travis/after_success.sh b/ci/travis/after_success.sh index e04e6c3e80..66768e38d0 100755 --- a/ci/travis/after_success.sh +++ b/ci/travis/after_success.sh @@ -1,12 +1,12 @@ #!/bin/sh set -eu -if [ "$BUILD_NAME" != "PHP_7.3_WITH_ASAN" ]; then - # Only run coverage when it is safe to do so (not on pull requests), and only on main branch +if [ "$BUILD_NAME" != "PHP_8.1_WITH_ASAN" ]; then + # Only run coverage when it is safe to do so (not on pull requests), and only on master branch echo "$TRAVIS_SECURE_ENV_VARS" echo "$TRAVIS_BRANCH" - sh -c 'if test "$TRAVIS_SECURE_ENV_VARS" = "true" -a "$TRAVIS_BRANCH" = "main"; then echo "run coverage"; ./run_code_coverage_upload.sh; fi' + sh -c 'if test "$TRAVIS_SECURE_ENV_VARS" = "true" -a "$TRAVIS_BRANCH" = "master"; then echo "run coverage"; ./run_code_coverage_upload.sh; fi' ln -s ../../../mapparser.y build/CMakeFiles/mapserver.dir/ ln -s ../../../maplexer.l build/CMakeFiles/mapserver.dir/ - coveralls --exclude renderers --exclude mapscript --exclude apache --exclude build/mapscript/mapscriptJAVA_wrap.c --exclude build/mapscript/mapscriptPYTHON_wrap.c --exclude shp2img.c --exclude legend.c --exclude scalebar.c --exclude msencrypt.c --exclude sortshp.c --exclude shptreevis.c --exclude shptree.c --exclude testexpr.c --exclude sym2img.c --exclude testcopy.c --exclude shptreetst.c --exclude tile4ms.c --exclude proj --exclude swig-3.0.12 --extension .c --extension .cpp + coveralls --exclude renderers --exclude mapscript --exclude apache --exclude build/mapscript/mapscriptJAVA_wrap.c --exclude build/mapscript/mapscriptPYTHON_wrap.c --exclude map2img.c --exclude legend.c --exclude scalebar.c --exclude msencrypt.c --exclude sortshp.c --exclude shptreevis.c --exclude shptree.c --exclude testexpr.c --exclude sym2img.c --exclude testcopy.c --exclude shptreetst.c --exclude tile4ms.c --extension .c --extension .cpp fi diff --git a/ci/travis/before_install.sh b/ci/travis/before_install.sh index 9bc708f505..cd21b55297 100755 --- a/ci/travis/before_install.sh +++ b/ci/travis/before_install.sh @@ -5,33 +5,94 @@ set -eu if ls /etc/apt/sources.list.d/pgdg* 2>/dev/null >/dev/null; then sudo mv /etc/apt/sources.list.d/pgdg* /tmp; fi dpkg -l | grep postgresql || /bin/true dpkg -l | grep postgis || /bin/true -sudo apt-get remove --purge postgresql* libpq-dev libpq5 || /bin/true +sudo apt-get remove --purge postgresql* libpq-dev libpq5 cmake || /bin/true sudo add-apt-repository -y ppa:ubuntugis/ubuntugis-unstable sudo apt-get update -sudo apt-get install -y --allow-unauthenticated protobuf-c-compiler libprotobuf-c0-dev bison flex libfribidi-dev cmake librsvg2-dev colordiff libpq-dev libpng-dev libjpeg-dev libgif-dev libgeos-dev libfreetype6-dev libfcgi-dev libcurl4-gnutls-dev libcairo2-dev libgdal-dev libproj-dev libxml2-dev libexempi-dev lcov lftp postgis libharfbuzz-dev gdal-bin ccache curl postgresql-server-dev-10 postgresql-10-postgis-3 postgresql-10-postgis-3-scripts swig g++ +sudo apt-get install -y --allow-unauthenticated build-essential protobuf-c-compiler libprotobuf-c-dev bison flex libfribidi-dev librsvg2-dev colordiff libpq-dev libpng-dev libjpeg-dev libgif-dev libgeos-dev libfreetype6-dev libfcgi-dev libcurl4-gnutls-dev libcairo2-dev libgdal-dev libproj-dev libxml2-dev libexempi-dev lcov lftp postgis libharfbuzz-dev gdal-bin proj-bin ccache curl postgresql-server-dev-12 postgresql-12-postgis-3 postgresql-12-postgis-3-scripts g++ ca-certificates # following are already installed on Travis CI #sudo apt-get install --allow-unauthenticated php-dev python-dev python3-dev sudo apt-get install -y --allow-unauthenticated libmono-system-drawing4.0-cil mono-mcs sudo apt-get install -y --allow-unauthenticated libperl-dev sudo apt-get install -y --allow-unauthenticated openjdk-8-jdk +sudo apt-get install -y --allow-unauthenticated libonig5 +#install recent cmake on GH build action +if [ -z ${TRAVIS+x} ]; then + sudo apt-get install -y --allow-unauthenticated cmake + #sudo apt-get install -y --allow-unauthenticated php-xdebug + export MSBUILD_ENV="NOT_TRAVIS" +else + # install recent CMake on Travis + DEPS_DIR="${TRAVIS_BUILD_DIR}/deps" + mkdir ${DEPS_DIR} && cd ${DEPS_DIR} + wget --no-check-certificate https://github.com/Kitware/CMake/releases/download/v3.26.3/cmake-3.26.3-linux-x86_64.tar.gz + tar -xvf cmake-3.26.3-linux-x86_64.tar.gz > /dev/null + mv cmake-3.26.3-linux-x86_64 cmake-install + export PATH=${DEPS_DIR}/cmake-install:${DEPS_DIR}/cmake-install/bin:${PATH} + cd ${TRAVIS_BUILD_DIR} + export MSBUILD_ENV="TRAVIS" + # check CMake version installed + cmake --version +fi + +#upgrade to recent SWIG +git clone https://github.com/swig/swig.git swig-git-master +cd swig-git-master +wget https://github.com/PhilipHazel/pcre2/releases/download/pcre2-10.39/pcre2-10.39.tar.gz +./Tools/pcre-build.sh +./autogen.sh +./configure --prefix=/usr +make +sudo make install +sudo ldconfig +cd ../ +#check SWIG version +swig -version + +eval "$(pyenv init --path)" +eval "$(pyenv init -)" + +# list installed and available Python/PHP versions +pyenv versions +# echo $(pyenv root) +# phpenv versions + +# set the global Python version +pyenv global $PYTHON_VERSION + +# check we are using the correct versions +pyenv which pip +pyenv which python + +pip install --upgrade pip +pip install cryptography==3.4.6 # avoid requiring rust compiler for the cryptography dependency pip install cpp-coveralls pyflakes lxml -sudo pip install -U -r msautotest/requirements.txt +pip install -r msautotest/requirements.txt export CC="ccache gcc" export CXX="ccache g++" -sudo sed -i 's/md5/trust/' /etc/postgresql/10/main/pg_hba.conf -sudo sed -i 's/peer/trust/' /etc/postgresql/10/main/pg_hba.conf -sudo service postgresql restart 10 +sudo sed -i 's/md5/trust/' /etc/postgresql/12/main/pg_hba.conf +sudo sed -i 's/peer/trust/' /etc/postgresql/12/main/pg_hba.conf +sudo service postgresql restart 12 cd msautotest +#upgrade to recent PHPUnit +cd php && curl -LO https://phar.phpunit.de/phpunit-10.phar +cd .. python -m pyflakes . ./create_postgis_test_data.sh -# py3 -python -m http.server &> /dev/null & +# copy custom projection to the PROJ_DATA folder +sudo cp ./wxs/data/epsg2 /usr/share/proj/ + +if [ $PYTHON_VERSION = "2.7" ]; then + python -m SimpleHTTPServer &> /dev/null & +else + # py3 + python -m http.server &> /dev/null & +fi cd .. touch maplexer.l diff --git a/ci/travis/script.sh b/ci/travis/script.sh index 3c756cb23a..3e67ac6ae0 100755 --- a/ci/travis/script.sh +++ b/ci/travis/script.sh @@ -1,7 +1,10 @@ #!/bin/sh set -eu -#if [ "$BUILD_NAME" = "PHP_7.3_WITH_ASAN" ]; then +eval "$(pyenv init --path)" +eval "$(pyenv init -)" + +#if [ "$BUILD_NAME" = "PHP_7.2_WITH_ASAN" ]; then # export CC="ccache clang" # export CXX="ccache clang++" #else @@ -9,31 +12,40 @@ set -eu export CXX="ccache g++" #fi -curl http://download.osgeo.org/proj/proj-6.1.1.tar.gz > proj-6.1.1.tar.gz -tar xzf proj-6.1.1.tar.gz -mv proj-6.1.1 proj -(cd proj/data && curl http://download.osgeo.org/proj/proj-datumgrid-1.8.tar.gz > proj-datumgrid-1.8.tar.gz && tar xvzf proj-datumgrid-1.8.tar.gz) -(cd proj; CFLAGS='-O2 -DPROJ_RENAME_SYMBOLS' CXXFLAGS='-O2 -DPROJ_RENAME_SYMBOLS' ./configure --disable-static --prefix=/usr/local && CCACHE_CPP2=yes make -j2 && sudo make -j3 install) -sudo rm -f /usr/include/proj_api.h +#make sure to use recent CMake, and the pyenv Python instance +export PYTHONPREFIX="$(dirname $(realpath $(pyenv which python)))/.." +if [ -z ${TRAVIS+x} ]; then + #not travis + export PATH=${PYTHONPREFIX}/bin:${PATH} +else + #travis + export PATH=${TRAVIS_BUILD_DIR}/deps/cmake-install:${TRAVIS_BUILD_DIR}/deps/cmake-install/bin:${PYTHONPREFIX}/bin:${PATH} +fi +cmake --version + +# check we are using the correct versions +pyenv which pip +pyenv which python +#pyenv which python-config +# check for phpunit & xdebug +php msautotest/php/phpunit-10.phar --version +php -v -if [ "$BUILD_NAME" = "PHP_7.3_WITH_ASAN" ]; then - # Force use of PROJ 4 API - sudo rm /usr/local/include/proj.h +if [ "$BUILD_NAME" = "PHP_8.1_WITH_ASAN" ]; then # -DNDEBUG to avoid issues with cairo cleanup - make cmakebuild MFLAGS="-j2" CMAKE_C_FLAGS="-g -fsanitize=address -DNDEBUG -DPROJ_RENAME_SYMBOLS -DACCEPT_USE_OF_DEPRECATED_PROJ_API_H" CMAKE_CXX_FLAGS="-g -fsanitize=address -DNDEBUG -DPROJ_RENAME_SYMBOLS -DACCEPT_USE_OF_DEPRECATED_PROJ_API_H" EXTRA_CMAKEFLAGS="-DCMAKE_BUILD_TYPE=None -DCMAKE_EXE_LINKER_FLAGS=-fsanitize=address -DPROJ_INCLUDE_DIR=/usr/local/include -DPROJ_LIBRARY=/usr/local/lib/libproj.so.15" + make cmakebuild MFLAGS="-j2" CMAKE_C_FLAGS="-g -fsanitize=address -DNDEBUG" CMAKE_CXX_FLAGS="-g -fsanitize=address -DNDEBUG" EXTRA_CMAKEFLAGS="-DCMAKE_BUILD_TYPE=None -DCMAKE_EXE_LINKER_FLAGS=-fsanitize=address" export AUTOTEST_OPTS="--strict --run_under_asan" - # Only run tests that only involve mapserv/shp2img binaries. mspython, etc would require LD_PREOLOAD'ing the asan shared object + # Only run tests that only involve mapserv/map2img binaries. mspython, etc would require LD_PREOLOAD'ing the asan shared object make -j4 asan_compatible_tests -elif [ "$BUILD_NAME" = "PHP_7.4_WITH_PROJ6" ]; then - # Avoid any use of PROJ 4 API - sudo rm -f /usr/include/proj_api.h - make cmakebuild MFLAGS="-j2" CMAKE_C_FLAGS="-O2 -DPROJ_RENAME_SYMBOLS" CMAKE_CXX_FLAGS="-O2 -DPROJ_RENAME_SYMBOLS" EXTRA_CMAKEFLAGS="-DPROJ_INCLUDE_DIR=/usr/local/include -DPROJ_LIBRARY=/usr/local/lib/libproj.so.15" +elif [ "$BUILD_NAME" = "PHP_8.1_WITH_PROJ8" ]; then + #runs through GitHub action + make cmakebuild MFLAGS="-j2" CMAKE_C_FLAGS="-O2" CMAKE_CXX_FLAGS="-O2" LIBMAPSERVER_EXTRA_FLAGS="-Wall -Werror -Wextra" make mspython-wheel + make phpng-build make -j4 test else - # Force use of PROJ 4 API - sudo rm /usr/local/include/proj.h - make cmakebuild MFLAGS="-j2" CMAKE_C_FLAGS="-DPROJ_RENAME_SYMBOLS -DACCEPT_USE_OF_DEPRECATED_PROJ_API_H" CMAKE_CXX_FLAGS="-DPROJ_RENAME_SYMBOLS -DACCEPT_USE_OF_DEPRECATED_PROJ_API_H" EXTRA_CMAKEFLAGS="-DPROJ_INCLUDE_DIR=/usr/local/include -DPROJ_LIBRARY=/usr/local/lib/libproj.so.15" + make cmakebuild MFLAGS="-j2" CMAKE_C_FLAGS="-O2" CMAKE_CXX_FLAGS="-O2" LIBMAPSERVER_EXTRA_FLAGS="-Wall -Werror -Wextra" make mspython-wheel + make phpng-build make -j4 test fi From 227a11c36cbc1e164e7aaad4d0a9cdc6a6d60488 Mon Sep 17 00:00:00 2001 From: Jeff McKenna Date: Thu, 20 Apr 2023 10:59:00 -0300 Subject: [PATCH 148/160] minor update for Travis --- Makefile | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index cc272602df..07bd3620a8 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ AUTOTEST_OPTS?=--strict_mode -PHP_MAPSCRIPT=build/mapscript/php/php_mapscript.so +PHP_MAPSCRIPT?=build/mapscript/phpng/php_mapscriptng.so PYTHON_MAPSCRIPT_PATH=build/mapscript/python JAVA_MAPSCRIPT_PATH=build/mapscript/java CSHARP_MAPSCRIPT_PATH=build/mapscript/csharp @@ -9,7 +9,7 @@ FLEX=flex YACC=yacc CMAKEFLAGS=-DCMAKE_C_FLAGS="--coverage ${CMAKE_C_FLAGS}" -DCMAKE_CXX_FLAGS="--coverage ${CMAKE_CXX_FLAGS}" \ -DCMAKE_SHARED_LINKER_FLAGS="-lgcov" -DWITH_CLIENT_WMS=1 \ - -DWITH_CLIENT_WFS=1 -DWITH_KML=1 -DWITH_SOS=1 -DWITH_CSHARP=1 -DWITH_PHP=1 -DWITH_PERL=1 \ + -DWITH_CLIENT_WFS=1 -DWITH_KML=1 -DWITH_SOS=1 -DWITH_CSHARP=1 -DWITH_PHPNG=1 -DWITH_PERL=1 \ -DWITH_PYTHON=1 -DWITH_JAVA=1 -DWITH_THREAD_SAFETY=1 -DWITH_FRIBIDI=1 -DWITH_FCGI=0 -DWITH_EXEMPI=1 \ -DCMAKE_BUILD_TYPE=Release -DWITH_RSVG=1 -DWITH_CURL=1 -DWITH_HARFBUZZ=1 -DWITH_POINT_Z_M=1 -DWITH_MSSQL2008=ON ${EXTRA_CMAKEFLAGS} all: cmakebuild @@ -48,6 +48,9 @@ mspython-wheel: php-testcase: test -f "$(PHP_MAPSCRIPT)" && (export PHP_MAPSCRIPT_SO="../../$(PHP_MAPSCRIPT)" && cd msautotest/php && ./run_test.sh) +phpng-build: + cd build && cmake --build . --config Release + java-testcase: test -d "$(JAVA_MAPSCRIPT_PATH)" && (export JAVA_MAPSCRIPT_SO="../../$(JAVA_MAPSCRIPT_PATH)" && cd mapscript/java && ./run_test.sh) From 3bfaddbde45dc67d005bea9ae061c1f411a1457e Mon Sep 17 00:00:00 2001 From: Jeff McKenna Date: Wed, 10 Jan 2024 09:50:31 -0400 Subject: [PATCH 149/160] update license year --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index bac791e4e2..138e3d6d76 100644 --- a/README.rst +++ b/README.rst @@ -69,7 +69,7 @@ License :: - Copyright (c) 2008-2021 Open Source Geospatial Foundation. + Copyright (c) 2008-2024 Open Source Geospatial Foundation. Copyright (c) 1996-2008 Regents of the University of Minnesota. Permission is hereby granted, free of charge, to any person obtaining a copy From 63ad0ab801cf70c1ca0a8aca69e0987f4382dcdb Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 10 Jun 2024 15:20:44 +0200 Subject: [PATCH 150/160] OGR output format: avoid potential stack buffer overflow on too long filename --- mapogroutput.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/mapogroutput.cpp b/mapogroutput.cpp index 11b977c24f..8f79fb352e 100644 --- a/mapogroutput.cpp +++ b/mapogroutput.cpp @@ -829,9 +829,13 @@ int msOGRWriteFromQuery( mapObj *map, outputFormatObj *format, int sendheaders ) return MS_FAILURE; } - if( !EQUAL(storage,"stream") ) - { - msBuildPath( datasource_name, request_dir, fo_filename ); + if (!EQUAL(storage, "stream")) { + if (!msBuildPath(datasource_name, request_dir, fo_filename)) { + msFree(request_dir); + CSLDestroy(layer_options); + CSLDestroy(ds_options); + return MS_FAILURE; + } if( EQUAL(form,"zip") ) { @@ -842,8 +846,8 @@ int msOGRWriteFromQuery( mapObj *map, outputFormatObj *format, int sendheaders ) } /* and add .dat extension if user didn't provide another extension */ - if( EQUAL(CPLGetExtension(datasource_name), "") ) { - strcat(datasource_name, ".dat"); + if (EQUAL(CPLGetExtension(datasource_name), "")) { + strlcat(datasource_name, ".dat", sizeof(datasource_name)); } } From 7b4f1b0df83ea1a225217226d741fa26e8654b8a Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sat, 8 Jun 2024 17:16:17 +0200 Subject: [PATCH 151/160] mapregex.c: fix invalid mapping of MS_REG_NOSUB and MS_REG_NEWLINE with GNU regex --- mapregex.c | 20 +++++++++++++++++--- mapregex.h | 11 +++++------ msautotest/query/expected/rfc62_test003.txt | 9 +++------ 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/mapregex.c b/mapregex.c index 6a8a74fa6b..db4b48e895 100644 --- a/mapregex.c +++ b/mapregex.c @@ -56,9 +56,23 @@ MS_API_EXPORT(int) ms_regcomp(ms_regex_t *regex, const char *expr, int cflags) { /* Must free in regfree() */ - regex_t* sys_regex = (regex_t*) msSmallMalloc(sizeof(regex_t)); - regex->sys_regex = (void*) sys_regex; - return regcomp(sys_regex, expr, cflags); + int reg_cflags = 0; + regex_t *sys_regex = (regex_t *)msSmallMalloc(sizeof(regex_t)); + regex->sys_regex = (void *)sys_regex; + if (cflags & MS_REG_EXTENDED) + reg_cflags |= REG_EXTENDED; + if (cflags & MS_REG_ICASE) + reg_cflags |= REG_ICASE; + if (cflags & MS_REG_NOSUB) + reg_cflags |= REG_NOSUB; + if (cflags & MS_REG_NEWLINE) + reg_cflags |= REG_NEWLINE; + int ret = regcomp(sys_regex, expr, reg_cflags); + if (ret != 0) { + free(regex->sys_regex); + regex->sys_regex = NULL; + } + return ret; } MS_API_EXPORT(size_t) ms_regerror(int errcode, const ms_regex_t *regex, char *errbuf, size_t errbuf_size) diff --git a/mapregex.h b/mapregex.h index 77a358f0c1..b9d8144e8a 100644 --- a/mapregex.h +++ b/mapregex.h @@ -64,12 +64,11 @@ extern "C" { /* === regcomp.c === */ #define MS_REG_BASIC 0000 #define MS_REG_EXTENDED 0001 -#define MS_REG_ICASE 0002 -#define MS_REG_NOSUB 0004 -#define MS_REG_NEWLINE 0010 -#define MS_REG_NOSPEC 0020 -#define MS_REG_PEND 0040 -#define MS_REG_DUMP 0200 +#define MS_REG_ICASE 0002 +// WARNING: GNU regex has REG_NOSUB = (1 << 3) = 8 +#define MS_REG_NOSUB 0004 +// WARNING: GNU regex has REG_NEWLINE = (1 << 2) = 4 +#define MS_REG_NEWLINE 0010 /* === regerror.c === */ diff --git a/msautotest/query/expected/rfc62_test003.txt b/msautotest/query/expected/rfc62_test003.txt index 49831211f0..584be7c10f 100644 --- a/msautotest/query/expected/rfc62_test003.txt +++ b/msautotest/query/expected/rfc62_test003.txt @@ -1,14 +1,12 @@ --xxOGRBoundaryxx -Content-Disposition: attachment; filename=foo -bar.gml +Content-Disposition: attachment; filename=some_default_value.gml Content-Type: application/binary Content-Transfer-Encoding: binary @@ -56,8 +54,7 @@ bar.xsd" --xxOGRBoundaryxx -Content-Disposition: attachment; filename=foo -bar.xsd +Content-Disposition: attachment; filename=some_default_value.xsd Content-Type: application/binary Content-Transfer-Encoding: binary From 70f7a3719757a58b1fd94f94f6b7e2742f0d557b Mon Sep 17 00:00:00 2001 From: Jeff McKenna Date: Tue, 11 Jun 2024 17:47:19 -0300 Subject: [PATCH 152/160] update for 7.6.6 release --- CMakeLists.txt | 2 +- HISTORY.TXT | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c02f274ba..e762df676b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ include(CheckCSourceCompiles) set (MapServer_VERSION_MAJOR 7) set (MapServer_VERSION_MINOR 6) -set (MapServer_VERSION_REVISION 5) +set (MapServer_VERSION_REVISION 6) set (MapServer_VERSION_SUFFIX "") # Set C++ version diff --git a/HISTORY.TXT b/HISTORY.TXT index b94aa0ce6d..8d9bf2cd2a 100644 --- a/HISTORY.TXT +++ b/HISTORY.TXT @@ -12,6 +12,11 @@ For a complete change history, please see the Git log comments. For more details about recent point releases, please see the online changelog at: https://mapserver.org/development/changelog/ +7.6.6 release (2024-06-11) +-------------------------- + +- security fix to prevent SQL injections through regex validation (#7075) + 7.6.5 release (2023-04-17) -------------------------- From 3c3c2258456a9c74ec72d90f5eae018c327e97e5 Mon Sep 17 00:00:00 2001 From: Jeff McKenna Date: Thu, 13 Jun 2024 09:05:05 -0300 Subject: [PATCH 153/160] update workflow --- .github/workflows/check-crlf.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check-crlf.yml b/.github/workflows/check-crlf.yml index 0d2c314785..33bb6d09be 100644 --- a/.github/workflows/check-crlf.yml +++ b/.github/workflows/check-crlf.yml @@ -8,13 +8,13 @@ on: [push, pull_request] jobs: Check-CRLF: name: verify that only LF linefeeds are used - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest steps: - name: Checkout repository contents - uses: actions/checkout@v1 + uses: actions/checkout@v4 - name: Use action to check for CRLF endings - uses: erclu/check-crlf@v1.1.2 + uses: erclu/check-crlf@v1.2.0 with: # ignore directories containing *.pdf and *.tab exclude: msautotest/misc/data/ /msautotest/renderers/expected/ \ No newline at end of file From 07dcf014f3173a3949258983480c9e2a6444475a Mon Sep 17 00:00:00 2001 From: Jeff McKenna Date: Thu, 13 Jun 2024 09:07:07 -0300 Subject: [PATCH 154/160] update workflow --- .github/workflows/check-crlf.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-crlf.yml b/.github/workflows/check-crlf.yml index 33bb6d09be..2dbedf19d3 100644 --- a/.github/workflows/check-crlf.yml +++ b/.github/workflows/check-crlf.yml @@ -17,4 +17,4 @@ jobs: - name: Use action to check for CRLF endings uses: erclu/check-crlf@v1.2.0 with: # ignore directories containing *.pdf and *.tab - exclude: msautotest/misc/data/ /msautotest/renderers/expected/ \ No newline at end of file + exclude: msautotest/misc/data/ /msautotest/renderers/expected/ tests/ \ No newline at end of file From 31bf2825dfdd5323f9691bfd7861e97960d15da8 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Fri, 19 Jul 2024 18:47:00 +0200 Subject: [PATCH 155/160] tostring() expression function: validate format string, and make sure buffer is large enough --- mapparser.c | 219 +++++++++++++++++++++++++++------------------------- mapparser.y | 9 ++- mapserver.h | 1 + mapstring.c | 64 +++++++++++++++ 4 files changed, 184 insertions(+), 109 deletions(-) diff --git a/mapparser.c b/mapparser.c index 99314f4866..ad0cac6745 100644 --- a/mapparser.c +++ b/mapparser.c @@ -80,7 +80,7 @@ int yylex(YYSTYPE *, parseObj *); /* prototype functions, defined after the grammar */ int yyerror(parseObj *, const char *); -#line 84 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:339 */ +#line 84 "/vagrant/mapparser.c" /* yacc.c:339 */ # ifndef YY_NULLPTR # if defined __cplusplus && 201103L <= __cplusplus @@ -100,8 +100,8 @@ int yyerror(parseObj *, const char *); /* In a future release of Bison, this section will be replaced by #include "mapparser.h". */ -#ifndef YY_YY_HOME_EVEN_MAPSERVER_MAPSERVER_MAPPARSER_H_INCLUDED -# define YY_YY_HOME_EVEN_MAPSERVER_MAPSERVER_MAPPARSER_H_INCLUDED +#ifndef YY_YY_VAGRANT_MAPPARSER_H_INCLUDED +# define YY_YY_VAGRANT_MAPPARSER_H_INCLUDED /* Debug traces. */ #ifndef YYDEBUG # define YYDEBUG 0 @@ -222,7 +222,7 @@ union YYSTYPE struct tm tmval; shapeObj *shpval; -#line 226 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:355 */ +#line 226 "/vagrant/mapparser.c" /* yacc.c:355 */ }; typedef union YYSTYPE YYSTYPE; @@ -234,11 +234,11 @@ typedef union YYSTYPE YYSTYPE; int yyparse (parseObj *p); -#endif /* !YY_YY_HOME_EVEN_MAPSERVER_MAPSERVER_MAPPARSER_H_INCLUDED */ +#endif /* !YY_YY_VAGRANT_MAPPARSER_H_INCLUDED */ /* Copy the second part of user declarations. */ -#line 242 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:358 */ +#line 242 "/vagrant/mapparser.c" /* yacc.c:358 */ #ifdef short # undef short @@ -549,7 +549,7 @@ static const yytype_uint16 yyrline[] = 581, 592, 602, 614, 615, 616, 617, 618, 619, 620, 627, 628, 629, 630, 638, 641, 642, 643, 653, 663, 673, 683, 693, 703, 713, 723, 734, 752, 753, 754, - 758, 763, 767, 771, 775, 779, 785, 786 + 758, 768, 772, 776, 780, 784, 790, 791 }; #endif @@ -1551,7 +1551,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); break; } } -#line 1555 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1555 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 4: @@ -1570,7 +1570,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); break; } } -#line 1574 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1574 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 5: @@ -1588,7 +1588,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); break; } } -#line 1592 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1592 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 6: @@ -1601,13 +1601,13 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); break; } } -#line 1605 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1605 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 8: #line 120 "mapparser.y" /* yacc.c:1646 */ { (yyval.intval) = (yyvsp[-1].intval); } -#line 1611 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1611 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 9: @@ -1616,7 +1616,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); (yyval.intval) = MS_FALSE; if((yyvsp[-2].intval) == (yyvsp[0].intval)) (yyval.intval) = MS_TRUE; } -#line 1620 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1620 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 10: @@ -1629,7 +1629,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); else (yyval.intval) = MS_FALSE; } -#line 1633 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1633 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 11: @@ -1643,7 +1643,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } else (yyval.intval) = MS_FALSE; } -#line 1647 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1647 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 12: @@ -1656,7 +1656,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); else (yyval.intval) = MS_FALSE; } -#line 1660 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1660 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 13: @@ -1670,7 +1670,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } else (yyval.intval) = MS_FALSE; } -#line 1674 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1674 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 14: @@ -1683,7 +1683,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); else (yyval.intval) = MS_FALSE; } -#line 1687 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1687 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 15: @@ -1697,7 +1697,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } else (yyval.intval) = MS_FALSE; } -#line 1701 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1701 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 16: @@ -1710,7 +1710,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); else (yyval.intval) = MS_FALSE; } -#line 1714 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1714 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 17: @@ -1724,19 +1724,19 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } else (yyval.intval) = MS_FALSE; } -#line 1728 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1728 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 18: #line 193 "mapparser.y" /* yacc.c:1646 */ { (yyval.intval) = !(yyvsp[0].intval); } -#line 1734 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1734 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 19: #line 194 "mapparser.y" /* yacc.c:1646 */ { (yyval.intval) = !(yyvsp[0].dblval); } -#line 1740 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1740 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 20: @@ -1761,7 +1761,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); free((yyvsp[-2].strval)); free((yyvsp[0].strval)); } -#line 1765 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1765 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 21: @@ -1786,7 +1786,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); free((yyvsp[-2].strval)); free((yyvsp[0].strval)); } -#line 1790 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1790 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 22: @@ -1797,7 +1797,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); else (yyval.intval) = MS_FALSE; } -#line 1801 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1801 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 23: @@ -1808,7 +1808,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); else (yyval.intval) = MS_FALSE; } -#line 1812 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1812 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 24: @@ -1819,7 +1819,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); else (yyval.intval) = MS_FALSE; } -#line 1823 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1823 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 25: @@ -1830,7 +1830,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); else (yyval.intval) = MS_FALSE; } -#line 1834 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1834 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 26: @@ -1841,7 +1841,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); else (yyval.intval) = MS_FALSE; } -#line 1845 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1845 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 27: @@ -1852,7 +1852,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); else (yyval.intval) = MS_FALSE; } -#line 1856 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1856 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 28: @@ -1865,7 +1865,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); free((yyvsp[-2].strval)); free((yyvsp[0].strval)); } -#line 1869 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1869 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 29: @@ -1878,7 +1878,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); free((yyvsp[-2].strval)); free((yyvsp[0].strval)); } -#line 1882 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1882 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 30: @@ -1891,7 +1891,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); free((yyvsp[-2].strval)); free((yyvsp[0].strval)); } -#line 1895 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1895 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 31: @@ -1904,7 +1904,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); free((yyvsp[-2].strval)); free((yyvsp[0].strval)); } -#line 1908 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1908 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 32: @@ -1917,7 +1917,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); free((yyvsp[-2].strval)); free((yyvsp[0].strval)); } -#line 1921 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1921 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 33: @@ -1930,7 +1930,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); free((yyvsp[-2].strval)); free((yyvsp[0].strval)); } -#line 1934 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1934 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 34: @@ -1941,7 +1941,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); else (yyval.intval) = MS_FALSE; } -#line 1945 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1945 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 35: @@ -1952,7 +1952,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); else (yyval.intval) = MS_FALSE; } -#line 1956 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1956 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 36: @@ -1963,7 +1963,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); else (yyval.intval) = MS_FALSE; } -#line 1967 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1967 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 37: @@ -1974,7 +1974,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); else (yyval.intval) = MS_FALSE; } -#line 1978 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1978 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 38: @@ -1985,7 +1985,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); else (yyval.intval) = MS_FALSE; } -#line 1989 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 1989 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 39: @@ -1996,7 +1996,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); else (yyval.intval) = MS_FALSE; } -#line 2000 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2000 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 40: @@ -2022,7 +2022,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); free((yyvsp[-2].strval)); free((yyvsp[0].strval)); } -#line 2026 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2026 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 41: @@ -2047,7 +2047,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); (yyval.intval) = MS_TRUE; free((yyvsp[0].strval)); } -#line 2051 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2051 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 42: @@ -2058,7 +2058,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); else (yyval.intval) = MS_FALSE; } -#line 2062 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2062 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 43: @@ -2071,7 +2071,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); free((yyvsp[-2].strval)); free((yyvsp[0].strval)); } -#line 2075 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2075 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 44: @@ -2082,7 +2082,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); else (yyval.intval) = MS_FALSE; } -#line 2086 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2086 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 45: @@ -2098,7 +2098,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } else (yyval.intval) = rval; } -#line 2102 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2102 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 46: @@ -2114,7 +2114,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } else (yyval.intval) = rval; } -#line 2118 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2118 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 47: @@ -2130,7 +2130,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } else (yyval.intval) = rval; } -#line 2134 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2134 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 48: @@ -2146,7 +2146,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } else (yyval.intval) = rval; } -#line 2150 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2150 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 49: @@ -2162,7 +2162,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } else (yyval.intval) = rval; } -#line 2166 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2166 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 50: @@ -2178,7 +2178,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } else (yyval.intval) = rval; } -#line 2182 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2182 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 51: @@ -2194,7 +2194,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } else (yyval.intval) = rval; } -#line 2198 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2198 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 52: @@ -2210,7 +2210,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } else (yyval.intval) = rval; } -#line 2214 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2214 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 53: @@ -2226,7 +2226,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } else (yyval.intval) = rval; } -#line 2230 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2230 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 54: @@ -2242,7 +2242,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } else (yyval.intval) = rval; } -#line 2246 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2246 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 55: @@ -2258,7 +2258,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } else (yyval.intval) = rval; } -#line 2262 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2262 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 56: @@ -2274,7 +2274,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } else (yyval.intval) = rval; } -#line 2278 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2278 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 57: @@ -2290,7 +2290,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } else (yyval.intval) = rval; } -#line 2294 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2294 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 58: @@ -2306,7 +2306,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } else (yyval.intval) = rval; } -#line 2310 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2310 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 59: @@ -2322,7 +2322,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } else (yyval.intval) = rval; } -#line 2326 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2326 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 60: @@ -2338,7 +2338,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } else (yyval.intval) = rval; } -#line 2342 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2342 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 61: @@ -2353,7 +2353,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); else (yyval.intval) = MS_FALSE; } -#line 2357 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2357 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 62: @@ -2368,37 +2368,37 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); else (yyval.intval) = MS_FALSE; } -#line 2372 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2372 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 64: #line 615 "mapparser.y" /* yacc.c:1646 */ { (yyval.dblval) = (yyvsp[-1].dblval); } -#line 2378 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2378 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 65: #line 616 "mapparser.y" /* yacc.c:1646 */ { (yyval.dblval) = (yyvsp[-2].dblval) + (yyvsp[0].dblval); } -#line 2384 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2384 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 66: #line 617 "mapparser.y" /* yacc.c:1646 */ { (yyval.dblval) = (yyvsp[-2].dblval) - (yyvsp[0].dblval); } -#line 2390 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2390 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 67: #line 618 "mapparser.y" /* yacc.c:1646 */ { (yyval.dblval) = (yyvsp[-2].dblval) * (yyvsp[0].dblval); } -#line 2396 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2396 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 68: #line 619 "mapparser.y" /* yacc.c:1646 */ { (yyval.dblval) = (int)(yyvsp[-2].dblval) % (int)(yyvsp[0].dblval); } -#line 2402 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2402 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 69: @@ -2410,25 +2410,25 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } else (yyval.dblval) = (yyvsp[-2].dblval) / (yyvsp[0].dblval); } -#line 2414 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2414 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 70: #line 627 "mapparser.y" /* yacc.c:1646 */ { (yyval.dblval) = (yyvsp[0].dblval); } -#line 2420 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2420 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 71: #line 628 "mapparser.y" /* yacc.c:1646 */ { (yyval.dblval) = pow((yyvsp[-2].dblval), (yyvsp[0].dblval)); } -#line 2426 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2426 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 72: #line 629 "mapparser.y" /* yacc.c:1646 */ { (yyval.dblval) = strlen((yyvsp[-1].strval)); } -#line 2432 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2432 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 73: @@ -2441,19 +2441,19 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); (yyval.dblval) = msGetPolygonArea((yyvsp[-1].shpval)); if((yyvsp[-1].shpval)->scratch == MS_TRUE) msFreeShape((yyvsp[-1].shpval)); } -#line 2445 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2445 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 74: #line 638 "mapparser.y" /* yacc.c:1646 */ { (yyval.dblval) = (MS_NINT((yyvsp[-3].dblval)/(yyvsp[-1].dblval)))*(yyvsp[-1].dblval); } -#line 2451 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2451 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 76: #line 642 "mapparser.y" /* yacc.c:1646 */ { (yyval.shpval) = (yyvsp[-1].shpval); } -#line 2457 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2457 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 77: @@ -2468,7 +2468,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); s->scratch = MS_TRUE; (yyval.shpval) = s; } -#line 2472 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2472 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 78: @@ -2483,7 +2483,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); s->scratch = MS_TRUE; (yyval.shpval) = s; } -#line 2487 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2487 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 79: @@ -2498,7 +2498,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); s->scratch = MS_TRUE; (yyval.shpval) = s; } -#line 2502 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2502 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 80: @@ -2513,7 +2513,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); s->scratch = MS_TRUE; (yyval.shpval) = s; } -#line 2517 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2517 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 81: @@ -2528,7 +2528,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); s->scratch = MS_TRUE; (yyval.shpval) = s; } -#line 2532 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2532 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 82: @@ -2543,7 +2543,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); s->scratch = MS_TRUE; (yyval.shpval) = s; } -#line 2547 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2547 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 83: @@ -2558,7 +2558,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); s->scratch = MS_TRUE; (yyval.shpval) = s; } -#line 2562 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2562 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 84: @@ -2573,7 +2573,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); s->scratch = MS_TRUE; (yyval.shpval) = s; } -#line 2577 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2577 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 85: @@ -2589,7 +2589,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); s->scratch = MS_TRUE; (yyval.shpval) = s; } -#line 2593 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2593 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 86: @@ -2610,13 +2610,13 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); return(-1); #endif } -#line 2614 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2614 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 88: #line 753 "mapparser.y" /* yacc.c:1646 */ { (yyval.strval) = (yyvsp[-1].strval); } -#line 2620 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2620 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 89: @@ -2625,72 +2625,77 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); (yyval.strval) = (char *)malloc(strlen((yyvsp[-2].strval)) + strlen((yyvsp[0].strval)) + 1); sprintf((yyval.strval), "%s%s", (yyvsp[-2].strval), (yyvsp[0].strval)); free((yyvsp[-2].strval)); free((yyvsp[0].strval)); } -#line 2629 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2629 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 90: #line 758 "mapparser.y" /* yacc.c:1646 */ { - (yyval.strval) = (char *) malloc(strlen((yyvsp[-1].strval)) + 64); /* Plenty big? Should use snprintf below... */ - sprintf((yyval.strval), (yyvsp[-1].strval), (yyvsp[-3].dblval)); + char* ret = msToString((yyvsp[-1].strval), (yyvsp[-3].dblval)); free((yyvsp[-1].strval)); + (yyvsp[-1].strval) = NULL; + if(!ret) { + yyerror(p, "tostring() failed."); + return(-1); + } + (yyval.strval) = ret; } -#line 2639 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2644 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 91: -#line 763 "mapparser.y" /* yacc.c:1646 */ +#line 768 "mapparser.y" /* yacc.c:1646 */ { (yyvsp[-1].strval) = msCommifyString((yyvsp[-1].strval)); (yyval.strval) = (yyvsp[-1].strval); } -#line 2648 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2653 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 92: -#line 767 "mapparser.y" /* yacc.c:1646 */ +#line 772 "mapparser.y" /* yacc.c:1646 */ { msStringToUpper((yyvsp[-1].strval)); (yyval.strval) = (yyvsp[-1].strval); } -#line 2657 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2662 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 93: -#line 771 "mapparser.y" /* yacc.c:1646 */ +#line 776 "mapparser.y" /* yacc.c:1646 */ { msStringToLower((yyvsp[-1].strval)); (yyval.strval) = (yyvsp[-1].strval); } -#line 2666 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2671 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 94: -#line 775 "mapparser.y" /* yacc.c:1646 */ +#line 780 "mapparser.y" /* yacc.c:1646 */ { msStringInitCap((yyvsp[-1].strval)); (yyval.strval) = (yyvsp[-1].strval); } -#line 2675 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2680 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 95: -#line 779 "mapparser.y" /* yacc.c:1646 */ +#line 784 "mapparser.y" /* yacc.c:1646 */ { msStringFirstCap((yyvsp[-1].strval)); (yyval.strval) = (yyvsp[-1].strval); } -#line 2684 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2689 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; case 97: -#line 786 "mapparser.y" /* yacc.c:1646 */ +#line 791 "mapparser.y" /* yacc.c:1646 */ { (yyval.tmval) = (yyvsp[-1].tmval); } -#line 2690 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2695 "/vagrant/mapparser.c" /* yacc.c:1646 */ break; -#line 2694 "/home/even/mapserver/mapserver/mapparser.c" /* yacc.c:1646 */ +#line 2699 "/vagrant/mapparser.c" /* yacc.c:1646 */ default: break; } /* User semantic actions sometimes alter yychar, and that requires @@ -2918,7 +2923,7 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); #endif return yyresult; } -#line 789 "mapparser.y" /* yacc.c:1906 */ +#line 794 "mapparser.y" /* yacc.c:1906 */ /* diff --git a/mapparser.y b/mapparser.y index 198142aa4a..62bb5c62c0 100644 --- a/mapparser.y +++ b/mapparser.y @@ -756,9 +756,14 @@ string_exp: STRING sprintf($$, "%s%s", $1, $3); free($1); free($3); } | TOSTRING '(' math_exp ',' string_exp ')' { - $$ = (char *) malloc(strlen($5) + 64); /* Plenty big? Should use snprintf below... */ - sprintf($$, $5, $3); + char* ret = msToString($5, $3); free($5); + $5 = NULL; + if(!ret) { + yyerror(p, "tostring() failed."); + return(-1); + } + $$ = ret; } | COMMIFY '(' string_exp ')' { $3 = msCommifyString($3); diff --git a/mapserver.h b/mapserver.h index 9950da8225..418ea8fdc1 100644 --- a/mapserver.h +++ b/mapserver.h @@ -2312,6 +2312,7 @@ void msPopulateTextSymbolForLabelAndString(textSymbolObj *ts, labelObj *l, char MS_DLL_EXPORT char *msJoinStrings(char **array, int arrayLength, const char *delimeter); MS_DLL_EXPORT char *msHashString(const char *pszStr); MS_DLL_EXPORT char *msCommifyString(char *str); + MS_DLL_EXPORT char *msToString(const char *format, double value); MS_DLL_EXPORT int msHexToInt(char *hex); MS_DLL_EXPORT char *msGetEncodedString(const char *string, const char *encoding); MS_DLL_EXPORT char *msConvertWideStringToUTF8 (const wchar_t* string, const char* encoding); diff --git a/mapstring.c b/mapstring.c index 17df5dc5cc..6a419cd01f 100644 --- a/mapstring.c +++ b/mapstring.c @@ -37,6 +37,7 @@ #include "cpl_vsi.h" #include +#include #include #include @@ -1492,6 +1493,69 @@ char *msCommifyString(char *str) return str; } +/************************************************************************/ +/* msToString() */ +/************************************************************************/ + +char *msToString(const char *format, double value) { + int pctAlreadyFound = FALSE; + // Validate that the formatting string is OK for a single input double value + int extra_size = 0; + for (const char *ptr = format; *ptr; ++ptr) { + if (*ptr == '%' && ptr[1] == '%') { + ++ptr; + } else if (*ptr == '%') { + if (pctAlreadyFound) { + msSetError(MS_MISCERR, "More than one conversion specifier", + "msToString()"); + return NULL; + } + pctAlreadyFound = TRUE; + ++ptr; + // Skip flag characters + while (*ptr == '+' || *ptr == '-' || *ptr == ' ' || *ptr == '\'' || + *ptr == '0') { + ++ptr; + } + // Skip width + if (*ptr >= '1' && *ptr <= '9') { + extra_size = atoi(ptr); + do { + ++ptr; + } while (*ptr >= '0' && *ptr <= '9'); + if (extra_size > 1024) { + // To avoid arbitrary memory allocatin + msSetError(MS_MISCERR, "Too large width", "msToString()"); + return NULL; + } + } + // maximum double value is of the order of ~1e308 + if (extra_size < DBL_MAX_10_EXP) + extra_size = DBL_MAX_10_EXP; + extra_size += 32; // extra margin + + // Skip precision + if (*ptr == '.') { + ++ptr; + while (*ptr >= '0' && *ptr <= '9') + ++ptr; + } + // Check conversion specifier + if (!(*ptr == 'e' || *ptr == 'E' || *ptr == 'f' || *ptr == 'F' || + *ptr == 'g' || *ptr == 'G')) { + msSetError(MS_MISCERR, "Invalid conversion specifier", "msToString()"); + return NULL; + } + } + } + { + // extra_size / 3 if thousands' grouping characters is used + const size_t nBufferSize = strlen(format) + extra_size + (extra_size / 3) + 1; + char *ret = (char*)(msSmallMalloc(nBufferSize)); + snprintf(ret, nBufferSize, format, value); + return ret; + } +} /* ------------------------------------------------------------------------------- */ /* Replace all occurrences of old with new in str. */ From 445e49beef58a75e57d1140c8bbc1d850c5a6867 Mon Sep 17 00:00:00 2001 From: Jeff McKenna Date: Sun, 21 Jul 2024 12:08:44 -0300 Subject: [PATCH 156/160] update security release notes --- SECURITY.md | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index ac7faa18b5..43e72602a5 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -11,25 +11,30 @@ submissions, when describing the vulnerability (see https://mapserver.org/develo ## Supported Versions The MapServer PSC (Project Steering Committee) will release patches for security vulnerabilities -for the last release branch of the **two most recent release series** (such as 8.x, 7.x. 6.x, etc...). +for the last release branch of the **two most recent release series** (such as 8.x, 7.x, 6.x, etc..., +where "x" is the most recent release in the series, such as: 7.6.6 being supported, but +not 7.6.5). For example, once 8.4 is released, support for 8.2 will be dropped. + Patches will only be provided **for a period of three years** from the release date of the current series. -For example, as 8.0 has been released, now only 8.0.x and 7.6.x will be supported/patched and 7.6.x will -only be supported for three years from the date of the 8.0 series release. +For example, as 8.2 has been released, now 8.2.x, and 7.6.x will be supported/patched, and 7.6.x will +only be supported for three years from the date of the 8.0 series release (until 2025-09-12). Currently, the following versions are supported: -| Version | Supported | -| ------- | ------------------ | -| 8.0.x | :white_check_mark: | -| 7.6.x | :white_check_mark: | -| 7.4.x | :x: | -| 7.2.x | :x: | -| 7.0.x | :x: | -| 6.4.x | :x: | -| < 6.4 | :x: | - -Note: _MapServer 8.0.0 was released on 2022-09-12._ -Note: _MapServer 7.0.0 was released on 2015-07-24._ +| Version | Supported | Support Until | +| ------- | ------------------ |-------------- | +| 8.2.x | :white_check_mark: | | +| 8.0.x | :x: | | +| 7.6.x | :white_check_mark: | 2025-09-12 | +| 7.4.x | :x: | | +| 7.2.x | :x: | | +| 7.0.x | :x: | | +| 6.4.x | :x: | | +| < 6.4 | :x: | | + +- _MapServer 8.2.0 was released on 2024-07-08_ +- _MapServer 8.0.0 was released on 2022-09-12_ +- _MapServer 7.0.0 was released on 2015-07-24_ ## Version Numbering: Explained From 0246da24e91fbeaa7b8567f5261271f933180cfa Mon Sep 17 00:00:00 2001 From: Jeff McKenna Date: Sun, 21 Jul 2024 14:30:22 -0300 Subject: [PATCH 157/160] update for 7.6.7 release --- CMakeLists.txt | 2 +- HISTORY.TXT | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e762df676b..f497682465 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ include(CheckCSourceCompiles) set (MapServer_VERSION_MAJOR 7) set (MapServer_VERSION_MINOR 6) -set (MapServer_VERSION_REVISION 6) +set (MapServer_VERSION_REVISION 7) set (MapServer_VERSION_SUFFIX "") # Set C++ version diff --git a/HISTORY.TXT b/HISTORY.TXT index 8d9bf2cd2a..94071bbf53 100644 --- a/HISTORY.TXT +++ b/HISTORY.TXT @@ -12,6 +12,11 @@ For a complete change history, please see the Git log comments. For more details about recent point releases, please see the online changelog at: https://mapserver.org/development/changelog/ +7.6.7 release (2024-07-21) +-------------------------- + +- security: validate tostring() expression function (#7123) + 7.6.6 release (2024-06-11) -------------------------- From 1043c99c2306311a330555d0d46e83feebd6bba0 Mon Sep 17 00:00:00 2001 From: Jeff McKenna Date: Mon, 6 Jan 2025 15:25:34 -0400 Subject: [PATCH 158/160] update copyright year --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 138e3d6d76..e064e0c893 100644 --- a/README.rst +++ b/README.rst @@ -69,7 +69,7 @@ License :: - Copyright (c) 2008-2024 Open Source Geospatial Foundation. + Copyright (c) 2008-2025 Open Source Geospatial Foundation. Copyright (c) 1996-2008 Regents of the University of Minnesota. Permission is hereby granted, free of charge, to any person obtaining a copy From 918b54d4cbabbe65e2134e4782c2ab4cbd3894fd Mon Sep 17 00:00:00 2001 From: Jeff McKenna Date: Tue, 22 Jul 2025 09:47:18 -0300 Subject: [PATCH 159/160] use master for CRLF check --- .github/workflows/check-crlf.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-crlf.yml b/.github/workflows/check-crlf.yml index 2dbedf19d3..f7276b8280 100644 --- a/.github/workflows/check-crlf.yml +++ b/.github/workflows/check-crlf.yml @@ -15,6 +15,6 @@ jobs: uses: actions/checkout@v4 - name: Use action to check for CRLF endings - uses: erclu/check-crlf@v1.2.0 + uses: erclu/check-crlf@master with: # ignore directories containing *.pdf and *.tab exclude: msautotest/misc/data/ /msautotest/renderers/expected/ tests/ \ No newline at end of file From 2f7a4069ffd86cf5dec0680938d76d4c6bb725f4 Mon Sep 17 00:00:00 2001 From: Jeff McKenna Date: Tue, 12 Aug 2025 07:47:10 -0300 Subject: [PATCH 160/160] bump Checkout action --- .github/workflows/check-crlf.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-crlf.yml b/.github/workflows/check-crlf.yml index f7276b8280..b6db0a6f8f 100644 --- a/.github/workflows/check-crlf.yml +++ b/.github/workflows/check-crlf.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Checkout repository contents - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Use action to check for CRLF endings uses: erclu/check-crlf@master