diff --git a/.github/workflows/check-crlf.yml b/.github/workflows/check-crlf.yml new file mode 100644 index 0000000000..b6db0a6f8f --- /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-latest + + steps: + - name: Checkout repository contents + uses: actions/checkout@v5 + + - name: Use action to check for CRLF endings + 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 diff --git a/.travis.yml b/.travis.yml index 780251daef..29a0d4cc60 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,31 +2,21 @@ branches: except: - /^(cherry-pick-)?backport-\d+-to-/ -dist: trusty +dist: focal language: php matrix: include: - - php: 5.6 + - php: 8.1 env: - - BUILD_NAME=PHP_5.6 + - BUILD_NAME=PHP_8.1_WITH_ASAN + - PYTHON_VERSION=3.8 - - php: 7.0 + - php: 8.2.0 env: - - BUILD_NAME=PHP_7.0 - - - php: 7.1 - env: - - BUILD_NAME=PHP_7.1 - - - php: 7.2 - env: - - BUILD_NAME=PHP_7.2_WITH_ASAN - - - php: 7.3 - env: - - BUILD_NAME=PHP_7.3_WITH_PROJ6 + - BUILD_NAME=PHP_8.2 + - PYTHON_VERSION=3.9 cache: apt: true @@ -81,11 +71,8 @@ after_success: - ./ci/travis/after_success.sh notifications: - email: - recipients: - - thomas.bonfort@gmail.com irc: channels: - - "irc.freenode.org#mapserver" + - "irc.libera.chat#mapserver" use_notice: true diff --git a/CMakeLists.txt b/CMakeLists.txt index 65e0944b8a..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 0) +set (MapServer_VERSION_REVISION 7) set (MapServer_VERSION_SUFFIX "") # Set C++ version 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. diff --git a/HISTORY.TXT b/HISTORY.TXT index e3067cf725..94071bbf53 100644 --- a/HISTORY.TXT +++ b/HISTORY.TXT @@ -10,7 +10,58 @@ 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.7 release (2024-07-21) +-------------------------- + +- security: validate tostring() expression function (#7123) + +7.6.6 release (2024-06-11) +-------------------------- + +- security fix to prevent SQL injections through regex validation (#7075) + +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) +-------------------------- + +- 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) +------------------------- + +- 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) +------------------------- + +- No major changes, see detailed changelog for bug fixes + +7.6.1 release (2020-07-31) +------------------------- + +- No major changes, see detailed changelog for bug fixes 7.6.0 release (2020-05-08) -------------------------- 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..28956387b0 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. +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/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) diff --git a/README.rst b/README.rst index be676b4d46..e064e0c893 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: @@ -69,7 +69,7 @@ License :: - Copyright (c) 2008-2020 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 @@ -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=main + :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/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000..43e72602a5 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,63 @@ +# 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..., +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.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 | 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 + +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. 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/ci/travis/after_success.sh b/ci/travis/after_success.sh index 0958b614a3..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.2_WITH_ASAN" ]; then +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" = "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 5b9129a5b0..cd21b55297 100755 --- a/ci/travis/before_install.sh +++ b/ci/travis/before_install.sh @@ -1,31 +1,99 @@ #!/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 cmake || /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 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 +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 +pip install -r msautotest/requirements.txt + 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/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 -pyflakes . +#upgrade to recent PHPUnit +cd php && curl -LO https://phar.phpunit.de/phpunit-10.phar +cd .. +python -m pyflakes . ./create_postgis_test_data.sh -python -m SimpleHTTPServer &> /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 touch mapparser.y diff --git a/ci/travis/script.sh b/ci/travis/script.sh index e17e4175ef..3e67ac6ae0 100755 --- a/ci/travis/script.sh +++ b/ci/travis/script.sh @@ -1,39 +1,51 @@ #!/bin/sh set -eu -if [ "$BUILD_NAME" = "PHP_7.2_WITH_ASAN" ]; then - export CC="ccache clang" - export CXX="ccache clang++" -else +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 export CC="ccache gcc" export CXX="ccache g++" +#fi + +#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 -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 +# 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.2_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.3_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 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/FindOracle.cmake b/cmake/FindOracle.cmake index 2568b5e735..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) + 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 diff --git a/cmake/FindProj.cmake b/cmake/FindProj.cmake index 22dee8dffb..d71cea721e 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 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) +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/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) diff --git a/fontcache.c b/fontcache.c index f29ed46b73..bf5c81353d 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; @@ -275,9 +287,19 @@ 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 %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) + msReleaseLock(TLOCK_TTF); +#endif return NULL; } gc->metrics.minx = face->face->glyph->metrics.horiBearingX / 64.0; @@ -288,6 +310,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; } @@ -315,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 %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\"", "msGetGlyphByIndex()",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); 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/mapcontext.c b/mapcontext.c index 2eaa4d275d..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) @@ -46,11 +47,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 +66,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 ); @@ -664,7 +671,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; @@ -682,6 +689,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++; @@ -746,12 +754,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 */ @@ -809,9 +825,8 @@ 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 *pszProj=NULL; char *pszValue; const char *pszHash; char *pszName=NULL; @@ -970,7 +985,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 +1002,7 @@ int msLoadMapContextLayer(mapObj *map, CPLXMLNode *psLayer, int nVersion, pszProj); } } + msFree(pszProj); } } @@ -1106,7 +1122,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; @@ -1115,6 +1131,12 @@ int msLoadMapContext(mapObj *map, 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 */ /* */ @@ -1277,7 +1299,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/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 : 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/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/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/mapdraw.c b/mapdraw.c index 0b9873a4e3..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); @@ -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) @@ -1301,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; @@ -1350,13 +1356,16 @@ 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)); } 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); @@ -1515,7 +1524,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)) @@ -1947,7 +1956,7 @@ int polygonLayerDrawShape(mapObj *map, imageObj *image, layerObj *layer, { int c = shape->classindex; - pointObj annopnt; + pointObj annopnt = {0}; // initialize int i; if(MS_DRAW_FEATURES(drawmode)) { @@ -2831,7 +2840,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; @@ -3119,6 +3128,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/maperror.c b/maperror.c index b3d7cbbdb3..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; @@ -324,6 +325,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 +369,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/mapfile.c b/mapfile.c index cf222f4c3c..c65679cfa2 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) { @@ -216,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); @@ -233,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); @@ -385,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 { @@ -393,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 */ @@ -431,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]; @@ -462,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); } @@ -805,7 +869,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"); } @@ -881,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; @@ -1053,34 +1117,36 @@ 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); } 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: @@ -1108,11 +1174,9 @@ 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 ) { + 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); @@ -1124,16 +1188,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 @@ -1142,9 +1206,14 @@ static int loadProjection(projectionObj *p) break; case(MS_STRING): case(MS_AUTO): - p->args[i] = msStrdup(msyystring_buffer); + if( p->numargs == MS_MAXPROJARGS ) { + msSetError(MS_MISCERR, "Parsing error near (%s):(line %d): Too many arguments in projection string", "loadProjection()", + msyystring_buffer, msyylineno); + return -1; + } + 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()", @@ -1430,7 +1499,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); @@ -1484,16 +1555,22 @@ 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) 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: @@ -1517,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); @@ -1544,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); @@ -1558,7 +1639,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 */ @@ -1595,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); @@ -1654,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); @@ -1667,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); @@ -1685,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); @@ -1701,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); @@ -1712,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); @@ -1738,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); @@ -1754,7 +1840,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++; @@ -1933,12 +2024,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) @@ -1993,7 +2080,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 +2089,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; @@ -2138,7 +2226,6 @@ static void writeExpression(FILE *stream, int indent, const char *name, expressi int loadHashTable(hashTableObj *ptable) { - char *key=NULL, *data=NULL; assert(ptable); for(;;) { @@ -2149,14 +2236,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); @@ -2234,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); @@ -2398,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); @@ -2419,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++; @@ -2451,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; @@ -2486,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); @@ -2513,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); @@ -2542,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; @@ -2550,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; @@ -2573,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); @@ -2587,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); @@ -2601,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; } @@ -2619,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); @@ -2630,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); @@ -2640,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); @@ -2729,7 +2835,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\"", @@ -3062,6 +3168,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++; @@ -3152,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()"); @@ -3197,7 +3312,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++; @@ -3211,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); @@ -3233,7 +3350,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): @@ -3277,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; @@ -3319,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; @@ -3374,12 +3496,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; } @@ -3390,7 +3515,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; } @@ -4011,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; } @@ -4055,11 +4172,16 @@ 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): - initCluster(&layer->cluster); if(loadCluster(&layer->cluster) == -1) return(-1); break; case(CLASSGROUP): @@ -4128,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); @@ -4149,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); @@ -4257,7 +4385,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): @@ -4276,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 */ @@ -4307,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); @@ -4344,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 ); @@ -4408,7 +4539,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): @@ -4430,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 */ @@ -4477,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); @@ -4501,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); @@ -4819,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()"); @@ -4836,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); @@ -4845,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 */ @@ -5206,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); @@ -5375,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); @@ -5394,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); @@ -5410,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) { @@ -5509,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): @@ -5525,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); @@ -5561,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 */; } @@ -5579,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"); @@ -5730,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); @@ -5780,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); @@ -5790,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); @@ -5890,6 +6040,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 */ @@ -6256,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) { @@ -6287,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()"); @@ -6300,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; @@ -6314,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); @@ -6324,7 +6481,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): @@ -6347,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); @@ -6358,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; @@ -6380,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; @@ -6389,7 +6545,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; @@ -6442,7 +6603,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); } @@ -6534,7 +6695,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); } @@ -6549,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 ); @@ -6561,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 @@ -6577,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) @@ -6635,26 +6801,15 @@ 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; 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; @@ -6667,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; @@ -6725,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; @@ -6733,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; @@ -6747,22 +6902,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) { @@ -6782,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); @@ -6798,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/mapgdal.c b/mapgdal.c index 630f163f55..53e526f7d6 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 ); } @@ -485,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; @@ -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/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()"); 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/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/maplabel.c b/maplabel.c index 52b84cfaa4..ce84ed32f5 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) { @@ -782,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); @@ -799,10 +810,16 @@ 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); + free(path); return(-1); } @@ -871,7 +888,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}; // initialize double x1=0, y1=0, x2=0, y2=0; double sin_a,cos_a; double w, h, x, y; diff --git a/maplegend.c b/maplegend.c index 4c2d377352..a71da95266 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->sizeunits != MS_PIXELS) { + lp->scalefactor = 1.0; + } + switch(type) { case MS_LAYER_POINT: marker.x = dstX + MS_NINT(width / 2.0); diff --git a/maplexer.c b/maplexer.c index 060c1801c2..74a4992043 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); + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, + 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); + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, + 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); + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, + 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); + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, + 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); + msyytext[msyyleng-2-2] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, + 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); + msyytext[msyyleng-2-2] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, + 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); + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, + 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); + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, + 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); + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, + 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); + msyytext[msyyleng-1-2] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_IREGEX); } @@ -4458,64 +4668,59 @@ 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); + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, + 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); + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, + 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); + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, + 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) { + if (msyystring_icase && msyyleng==2) { msyystring_icase = MS_FALSE; // reset return MS_ISTRING; } else @@ -4525,62 +4730,58 @@ YY_RULE_SETUP } else { - ++msyystring_size; - *msyystring_buffer_ptr++ = *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]; + int old_size = msyystring_size; + msyystring_size += (msyyleng==2) ? 2 : 1; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyystring_size, + msyystring_buffer_size); + msyystring_buffer[old_size] = *msyytext; + if (msyyleng==2) { + 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; - if (strlen(msyytext) == 2) - *msyystring_buffer_ptr++ = msyytext[1]; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyystring_size, + msyystring_buffer_size); + + if (msyyleng == 2) + 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; + msyystring_size += msyyleng; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyystring_size, + msyystring_buffer_size); + memcpy(msyystring_buffer + old_size, msyytext, msyyleng + 1); } YY_BREAK case 345: /* rule 345 can match eol */ YY_RULE_SETUP -#line 692 "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()"); 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); @@ -4588,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; @@ -4596,21 +4801,21 @@ YY_RULE_SETUP YY_BREAK case 346: YY_RULE_SETUP -#line 718 "maplexer.l" +#line 712 "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 720 "maplexer.l" { - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_STRING); } @@ -4618,11 +4823,11 @@ YY_RULE_SETUP case 348: /* rule 348 can match eol */ YY_RULE_SETUP -#line 733 "maplexer.l" +#line 727 "maplexer.l" { msyylineno++; } YY_BREAK case YY_STATE_EOF(INITIAL): -#line 735 "maplexer.l" +#line 729 "maplexer.l" { if( --include_stack_ptr < 0 ) return(EOF); /* end of main file */ @@ -4637,32 +4842,32 @@ case YY_STATE_EOF(INITIAL): case 349: /* rule 349 can match eol */ YY_RULE_SETUP -#line 746 "maplexer.l" +#line 740 "maplexer.l" { return(0); } YY_BREAK case 350: YY_RULE_SETUP -#line 750 "maplexer.l" +#line 744 "maplexer.l" { - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(0); } YY_BREAK case 351: YY_RULE_SETUP -#line 756 "maplexer.l" +#line 750 "maplexer.l" { return(msyytext[0]); } YY_BREAK case 352: YY_RULE_SETUP -#line 757 "maplexer.l" +#line 751 "maplexer.l" ECHO; YY_BREAK -#line 4666 "/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): @@ -4684,15 +4889,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 +4951,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 +5005,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 +5018,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 +5047,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 +5060,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 +5074,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 +5083,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 +5116,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 +5130,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 +5171,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 +5199,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 +5215,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 +5233,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 +5272,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 +5289,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 +5315,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 +5327,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 +5365,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 +5389,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 +5427,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 +5464,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 +5484,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 +5493,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 +5514,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 +5523,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 +5542,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 +5552,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 +5572,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 +5589,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 +5599,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 +5675,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 +5687,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 +5703,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 +5833,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 +5843,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 +5853,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 +5868,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 751 "maplexer.l" /* diff --git a/maplexer.l b/maplexer.l index bbc8bd581c..d1122fc4e4 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); @@ -504,17 +502,17 @@ char path[MS_MAXPATHLEN]; \[[a-z/\.][a-z0-9/:\.\-\=_ ]*\] { msyytext++; - msyytext[strlen(msyytext)-1] = '\0'; - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + 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), - msyystring_buffer_size, msyystring_buffer_ptr); + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, + msyystring_buffer_size); strcpy(msyystring_buffer,msyytext); msyynumber = atof(msyytext); return(MS_NUMBER); @@ -522,9 +520,9 @@ char path[MS_MAXPATHLEN]; \[[^\]]*\] { msyytext++; - msyytext[strlen(msyytext)-1] = '\0'; - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, + msyystring_buffer_size); strcpy(msyystring_buffer,msyytext); return(MS_BINDING); } @@ -544,42 +542,42 @@ char path[MS_MAXPATHLEN]; \[[^\]]*\] { /* 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); + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_TOKEN_BINDING_DOUBLE); } \"\[[^\"]*\]\"|\'\[[^\']*\]\' { /* 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); + msyytext[msyyleng-2-2] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_TOKEN_BINDING_STRING); } \`\[[^\`]*\]\` { /* attribute binding - time */ msyytext+=2; - msyytext[strlen(msyytext)-2] = '\0'; - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + 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), - msyystring_buffer_size, msyystring_buffer_ptr); + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, + msyystring_buffer_size); strcpy(msyystring_buffer,msyytext); msyynumber = atof(msyytext); return(MS_NUMBER); } -?[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); + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, + msyystring_buffer_size); strcpy(msyystring_buffer,msyytext); msyynumber = atof(msyytext); return(MS_TOKEN_LITERAL_NUMBER); @@ -587,45 +585,45 @@ char path[MS_MAXPATHLEN]; \`[^\`]*\` { msyytext++; - msyytext[strlen(msyytext)-1] = '\0'; - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_TOKEN_LITERAL_TIME); } \/[^*]{1}[^\/]*\/i { msyytext++; - msyytext[strlen(msyytext)-2] = '\0'; - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyytext[msyyleng-1-2] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_IREGEX); } \/[^*]{1}[^\/]*\/ { msyytext++; - msyytext[strlen(msyytext)-1] = '\0'; - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_REGEX); } \(.*\) { msyytext++; - msyytext[strlen(msyytext)-1] = '\0'; - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_EXPRESSION); } \{.*\} { msyytext++; - msyytext[strlen(msyytext)-1] = '\0'; - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + msyytext[msyyleng-1-1] = '\0'; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_LIST); } @@ -634,20 +632,15 @@ 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) { + if (msyystring_icase && msyyleng==2) { msyystring_icase = MS_FALSE; // reset return MS_ISTRING; } else @@ -657,51 +650,47 @@ char path[MS_MAXPATHLEN]; } else { - ++msyystring_size; - *msyystring_buffer_ptr++ = *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]; + int old_size = msyystring_size; + msyystring_size += (msyyleng==2) ? 2 : 1; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyystring_size, + msyystring_buffer_size); + msyystring_buffer[old_size] = *msyytext; + if (msyyleng==2) { + 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; - if (strlen(msyytext) == 2) - *msyystring_buffer_ptr++ = msyytext[1]; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyystring_size, + msyystring_buffer_size); + + if (msyyleng == 2) + 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; + msyystring_size += msyyleng; + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyystring_size, + msyystring_buffer_size); + 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()"); 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); @@ -709,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; @@ -719,13 +712,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); + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(MS_STRING); } @@ -748,8 +741,8 @@ char path[MS_MAXPATHLEN]; } . { - MS_LEXER_STRING_REALLOC(msyystring_buffer, strlen(msyytext), - msyystring_buffer_size, msyystring_buffer_ptr); + MS_LEXER_STRING_REALLOC(msyystring_buffer, msyyleng, + msyystring_buffer_size); strcpy(msyystring_buffer, msyytext); return(0); } diff --git a/mapmetadata.c b/mapmetadata.c index 1654dca584..f8d7303e83 100644 --- a/mapmetadata.c +++ b/mapmetadata.c @@ -31,6 +31,9 @@ #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 /************************************************************************/ /* _msMetadataGetCharacterString */ @@ -860,6 +863,8 @@ int msMetadataDispatch(mapObj *map, cgiRequestObj *cgi_request, owsRequestObj *o return status; } +#endif /* USE_LIBXML2 */ + /************************************************************************/ /* msMetadataCreateParamsObj */ /* */ @@ -946,3 +951,4 @@ void msMetadataSetGetMetadataURL(layerObj *lp, const char *url) msFree(pszMetadataURL); } +#endif diff --git a/mapmssql2008.c b/mapmssql2008.c index 8fefacef9d..58daa3e6fd 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); @@ -1324,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; @@ -1382,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, @@ -1508,18 +1546,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 +2610,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 */ @@ -3011,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/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); 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/mapogcfilter.c b/mapogcfilter.c index 838a28d804..1b77ffa612 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"))) @@ -2174,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; @@ -2216,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 ) @@ -2267,7 +2267,7 @@ char *FLTGetSQLExpression(FilterEncodingNode *psFilterNode, layerObj *lp) #else msSetError(MS_MISCERR, "OWS support is not available.", "FLTGetSQLExpression()"); - return(MS_FAILURE); + return NULL; #endif } @@ -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 */ /* */ @@ -3126,19 +3132,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 */ @@ -3150,8 +3151,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; isldurl = (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); @@ -122,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. */ @@ -162,6 +166,8 @@ static void msSLDApplySLD_DuplicateLayers(mapObj *map, int nSLDLayers, layerObj } } +#endif + /************************************************************************/ /* msSLDApplySLD */ /* */ @@ -2255,10 +2261,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 +2316,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); @@ -4669,8 +4720,8 @@ char *msSLDGetAttributeNameOrValue(char *pszExpression, } else pszAttributeName[iValue++] = pszExpression[i]; } - pszAttributeName[iValue] = '\0'; } + pszAttributeName[iValue] = '\0'; } msFreeCharArray(aszValues, nTokens); } else if (bOneCharCompare == 0) { @@ -4697,8 +4748,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 1c25b3c523..686bad4723 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") || @@ -2368,16 +2308,102 @@ 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 ) { + if( (EQUAL(psInfo->dialect, "Spatialite") && psInfo->bHasSpatialIndex) + || EQUAL(psInfo->dialect, "GPKG") ) + { + if (filter) filter = msStringConcatenate(filter, " AND "); + char* pszEscapedMainTableName = msLayerEscapePropertyName( + layer, psInfo->pszMainTableName); + filter = msStringConcatenate(filter, "\""); + filter = msStringConcatenate(filter, pszEscapedMainTableName); + msFree(pszEscapedMainTableName); + filter = msStringConcatenate(filter, "\"."); + 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, "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?? - 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); @@ -3080,7 +3106,10 @@ int msOGRFileReadTile( layerObj *layer, msOGRFileInfo *psInfo, #ifndef IGNORE_MISSING_DATA if( psTileInfo == NULL && targetTile == -1 ) + { + msFree(pszSRS); goto NextFile; + } #endif if( psTileInfo == NULL ) @@ -3447,7 +3476,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 ) { @@ -5351,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); @@ -5368,7 +5406,12 @@ static int msOGRGetPaging(layerObj *layer) } if(!msOGRLayerIsOpen(layer)) - msOGRLayerOpenVT(layer); + { + if(msOGRLayerOpenVT(layer) != MS_SUCCESS) + { + return FALSE; + } + } assert( layer->layerinfo != NULL); @@ -5441,8 +5484,9 @@ shapeObj *msOGRShapeFromWKT(const char *string) if( msOGRGeometryToShape( hGeom, shape, wkbFlatten(OGR_G_GetGeometryType(hGeom)) ) == MS_FAILURE ) { + msFreeShape(shape); free( shape ); - return NULL; + shape = NULL; } OGR_G_DestroyGeometry( hGeom ); diff --git a/mapogroutput.cpp b/mapogroutput.cpp index 11b977c24f..f416a1c6f2 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)); } } @@ -1222,7 +1226,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 +1274,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 +1311,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/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/mapows.c b/mapows.c index fbd98e48c6..f81d1f9ce7 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 ); @@ -2585,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(""); @@ -2645,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/mapows.h b/mapows.h index ed0402ec95..632a9082f6 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); @@ -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/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/mappostgis.c b/mappostgis.c index 0f1bdfcfa5..fee3d8c494 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}; // initialize double dx21, dy21, dx31, dy31, h21, h31, d, r; /* Circle is closed, so p2 must be opposite p1 & p3. */ @@ -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; } @@ -2055,7 +2062,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 +2072,53 @@ 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 + 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 + { + 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 +2153,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); @@ -2566,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; } @@ -3953,7 +3973,12 @@ void msPostGISEnablePaging(layerObj *layer, int value) } if(!msPostGISLayerIsOpen(layer)) - msPostGISLayerOpen(layer); + { + if(msPostGISLayerOpen(layer) != MS_SUCCESS) + { + return; + } + } assert( layer->layerinfo != NULL); diff --git a/mapprimitive.c b/mapprimitive.c index eabfdbc903..76f92ee7f6 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}; // initialize double r; double denominator, numerator; @@ -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)); diff --git a/mapproject.c b/mapproject.c index 5659c59fea..aa1a0cff3c 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; @@ -110,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() */ /************************************************************************/ @@ -117,17 +141,31 @@ 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; 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 ) { @@ -135,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 ) @@ -151,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; } } @@ -211,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 ) { @@ -241,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; } @@ -251,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; } @@ -307,7 +362,11 @@ static void msProjErrorLogger(void * user_data, int level, const char * message) { (void)user_data; +#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 ); } @@ -672,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; } @@ -851,6 +911,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) { @@ -910,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; @@ -1197,7 +1264,7 @@ static int msProjectShapeShouldDoLineCutting(reprojectionObj* reprojector) return MS_FALSE; } - pointObj p; + 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; @@ -1999,7 +2066,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); @@ -2011,7 +2097,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); @@ -2399,6 +2485,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 ); } diff --git a/mapquery.c b/mapquery.c index 3272ed9f9e..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; @@ -1617,7 +1620,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? */ 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; } 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 166c8d4fd8..b9d8144e8a 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 @@ -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/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 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/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 () 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/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()); 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/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: 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) 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 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); diff --git a/mapserv.c b/mapserv.c index 00603a6d55..9e912721a5 100644 --- a/mapserv.c +++ b/mapserv.c @@ -43,7 +43,9 @@ #include "mapio.h" #include "maptime.h" -#ifndef WIN32 +#include "cpl_conv.h" + +#ifndef _WIN32 #include #endif @@ -51,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 */ @@ -66,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 */ @@ -162,6 +164,16 @@ 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", "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]); + 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 */ @@ -234,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/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/mapserver.h b/mapserver.h index d607aa92b0..418ea8fdc1 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) @@ -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)$" @@ -483,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}; @@ -898,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; /************************************************************************/ @@ -1411,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; @@ -1437,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 @@ -1843,6 +1877,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 */ @@ -1855,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 */ @@ -1902,6 +1942,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 */ @@ -2024,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); @@ -2154,7 +2198,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 @@ -2266,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/mapservutil.c b/mapservutil.c index 0ef4feca71..d3b9e71886 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,38 +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) { - char *ms_mapfile = getenv("MS_MAPFILE"); - if(ms_mapfile) { - 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(getenv("MS_MAP_NO_PATH")) { - 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(getenv("MS_MAP_PATTERN") && msEvalRegex(getenv("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")) { @@ -272,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); + } } } } @@ -1467,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)) @@ -1620,7 +1673,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; diff --git a/mapshape.c b/mapshape.c index a70d858232..5822c2051a 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 ) @@ -1018,20 +1016,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 < 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); + } + 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) { @@ -1372,13 +1374,15 @@ 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) { - msSetError(MS_SHPERR, "Corrupted .shp file : shape %d, shape->line[%d].numpoints=%d", "msSHPReadShape()", - hEntity, i, shape->line[i].numpoints); + const ms_int32 end = i == nParts - 1 + ? nPoints + : psSHP->panParts[i+1]; + if (psSHP->panParts[i] < 0 || end < 0 || end > nPoints || + psSHP->panParts[i] >= end) { + 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); @@ -1388,10 +1392,12 @@ 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); free(shape->line); + shape->line = NULL; shape->numlines = 0; shape->type = MS_SHAPE_NULL; msSetError(MS_MEMERR, "Out of memory", "msSHPReadShape()"); @@ -1487,6 +1493,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); @@ -1498,6 +1506,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); @@ -1509,6 +1519,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()"); @@ -1726,11 +1737,21 @@ int msShapefileOpen(shapefileObj *shpfile, const char *mode, const char *filenam /* load some information about this shapefile */ msSHPGetInfo( shpfile->hSHP, &shpfile->numshapes, &shpfile->type); - msSHPReadBounds( shpfile->hSHP, -1, &(shpfile->bounds)); + + if( shpfile->numshapes < 0 || shpfile->numshapes > 256000000 ) { + msSetError(MS_SHPERR, "Corrupted .shp file : numshapes = %d.", + "msShapefileOpen()", shpfile->numshapes); + msSHPClose(shpfile->hSHP); + return -1; + } + + if( msSHPReadBounds( shpfile->hSHP, -1, &(shpfile->bounds)) != MS_SUCCESS ) { + msSHPClose(shpfile->hSHP); + return -1; + } bufferSize = strlen(filename)+5; dbfFilename = (char *)msSmallMalloc(bufferSize); - dbfFilename[0] = '\0'; strcpy(dbfFilename, filename); /* clean off any extention the filename might have */ @@ -1749,6 +1770,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); @@ -1855,8 +1877,14 @@ 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) { + if (msSHXReadSize(shpfile->hSHP, i) == 4) { /* handle NULL shape */ + continue; + } + return(MS_FAILURE); + } + + if(msRectOverlap(&shaperect, &rect) == MS_TRUE) msSetBit(shpfile->status, i, 1); } } } @@ -2759,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 */ diff --git a/mapsmoothing.c b/mapsmoothing.c index 12e32c24fe..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; + pointObj point = {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}; // initialize + int k = 0; if (res == MS_FALSE) { /* invalid window */ msAddPointToLine(&newShape->line[j], diff --git a/mapstring.c b/mapstring.c index 2401520817..6a419cd01f 100644 --- a/mapstring.c +++ b/mapstring.c @@ -37,6 +37,7 @@ #include "cpl_vsi.h" #include +#include #include #include @@ -377,25 +378,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); } } @@ -1500,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. */ @@ -2184,7 +2240,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 +2254,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 +2267,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/mapsymbol.c b/mapsymbol.c index 481da5faa1..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; @@ -230,8 +233,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): @@ -247,8 +252,12 @@ 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(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); @@ -266,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) @@ -399,7 +408,7 @@ int msAddImageSymbol(symbolSetObj *symbolset, char *filename) unlink(tmpfullfilename); msFree(tmpfilename); msFree(tmppath); - return MS_FAILURE; + return -1; } } msFree(tmpfilename); diff --git a/maptemplate.c b/maptemplate.c index 896929814f..e085e041fa 100644 --- a/maptemplate.c +++ b/maptemplate.c @@ -47,8 +47,10 @@ static char *olUrl = "//www.mapserver.org/lib/OpenLayers-ms60.js"; static char *olTemplate = \ "\n" "\n" + "\n" " MapServer Simple Viewer\n" " \n" + " \n" " \n" " \n" "
\n" @@ -1726,8 +1728,8 @@ static int processShplabelTag(layerObj *layer, char **line, shapeObj *origshape) } if(labelposvalid == MS_TRUE) { - pointObj p1; - pointObj p2; + pointObj p1 = {0}; // initialize + pointObj p2 = {0}; int label_offset_x, label_offset_y; labelObj *label=NULL; label_bounds lbounds; @@ -3634,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); } @@ -4217,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)) { @@ -4610,6 +4617,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) */ @@ -4649,6 +4657,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/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/mapwcs11.c b/mapwcs11.c index f5fe2b37cb..9c5943cfcd 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, @@ -1304,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 a258fdcaa6..1106e6a28e 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, @@ -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; @@ -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..92bbccf7ea 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*/ @@ -5182,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; + } } } } diff --git a/mapwms.c b/mapwms.c index 20ddfcc0b3..65f9b18528 100644 --- a/mapwms.c +++ b/mapwms.c @@ -48,7 +48,7 @@ #include #include -#ifdef WIN32 +#ifdef _WIN32 #include #endif @@ -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/mapwmslayer.c b/mapwmslayer.c index 899db5e91f..b3fb6288f4 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); } @@ -734,8 +740,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) { @@ -1414,7 +1420,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( @@ -1463,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; @@ -1496,7 +1501,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 +1513,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 */ diff --git a/mapxbase.c b/mapxbase.c index c283259693..dd813671cd 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 ); @@ -203,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); @@ -217,6 +225,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 ); 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'). 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/gdal/expected/wmsclient_3543.png b/msautotest/gdal/expected/wmsclient_3543.png index 407f15a590..45a3942da0 100644 Binary files a/msautotest/gdal/expected/wmsclient_3543.png and b/msautotest/gdal/expected/wmsclient_3543.png differ 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/misc/data/union/layer1.dbf b/msautotest/misc/data/union/layer1.dbf new file mode 100644 index 0000000000..6076a2abab Binary files /dev/null and b/msautotest/misc/data/union/layer1.dbf differ diff --git a/msautotest/misc/data/union/layer1.shp b/msautotest/misc/data/union/layer1.shp new file mode 100644 index 0000000000..aec5a87608 Binary files /dev/null and b/msautotest/misc/data/union/layer1.shp differ diff --git a/msautotest/misc/data/union/layer1.shx b/msautotest/misc/data/union/layer1.shx new file mode 100644 index 0000000000..98b0d5ff60 Binary files /dev/null and b/msautotest/misc/data/union/layer1.shx differ diff --git a/msautotest/misc/data/union/layer2.dbf b/msautotest/misc/data/union/layer2.dbf new file mode 100644 index 0000000000..34e534d3f9 Binary files /dev/null and b/msautotest/misc/data/union/layer2.dbf differ diff --git a/msautotest/misc/data/union/layer2.shp b/msautotest/misc/data/union/layer2.shp new file mode 100644 index 0000000000..9b8c267370 Binary files /dev/null and b/msautotest/misc/data/union/layer2.shp differ diff --git a/msautotest/misc/data/union/layer2.shx b/msautotest/misc/data/union/layer2.shx new file mode 100644 index 0000000000..3866ae926e Binary files /dev/null and b/msautotest/misc/data/union/layer2.shx differ diff --git a/msautotest/misc/expected/mode_tile_output.png b/msautotest/misc/expected/mode_tile_output.png new file mode 100644 index 0000000000..82c5c1e887 Binary files /dev/null and b/msautotest/misc/expected/mode_tile_output.png differ diff --git a/msautotest/misc/expected/union.png b/msautotest/misc/expected/union.png new file mode 100644 index 0000000000..7cb9408719 Binary files /dev/null and b/msautotest/misc/expected/union.png differ 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 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/misc/union.map b/msautotest/misc/union.map new file mode 100644 index 0000000000..2361e09ad1 --- /dev/null +++ b/msautotest/misc/union.map @@ -0,0 +1,57 @@ +# +# Test UNION LAYER +# +# REQUIRES: OUTPUT=PNG +# +MAP + +STATUS ON +EXTENT 478300 4762880 481650 4765610 +SIZE 400 300 + +IMAGETYPE png +SHAPEPATH ./data/union + +LAYER + STATUS default + NAME 'shppoly' + TYPE POLYGON + CONNECTIONTYPE UNION + CONNECTION "layer1,layer2" # reference to the source layers + STYLEITEM "AUTO" + # Define an empty class that will be filled at runtime from the color and + # styles read from each source layer. + CLASS + END +END + + +LAYER + STATUS OFF + NAME 'layer1' + TYPE POLYGON + DATA 'layer1' + CLASSITEM "AREA" + CLASS + NAME "green" + COLOR 0 255 0 + OUTLINECOLOR 255 0 0 + END +END + + +LAYER + STATUS OFF + NAME 'layer2' + TYPE POLYGON + DATA 'layer2' + CLASSITEM "AREA" + CLASS + NAME "red" + COLOR 255 0 0 + OUTLINECOLOR 255 0 0 + END +END + + +END 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) 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/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 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 0000000000..611bdedc0e Binary files /dev/null and b/msautotest/renderers/expected/font-fail-missing-glyph.png differ diff --git a/msautotest/renderers/expected/legend_sizeunits_meters.png b/msautotest/renderers/expected/legend_sizeunits_meters.png index 4e32ab4e06..336c7558b0 100644 Binary files a/msautotest/renderers/expected/legend_sizeunits_meters.png and b/msautotest/renderers/expected/legend_sizeunits_meters.png differ diff --git a/msautotest/renderers/expected/legend_sizeunits_meters.svg b/msautotest/renderers/expected/legend_sizeunits_meters.svg index 9d7b9e64c9..1a21bac8ff 100644 --- a/msautotest/renderers/expected/legend_sizeunits_meters.svg +++ b/msautotest/renderers/expected/legend_sizeunits_meters.svg @@ -27,15 +27,15 @@ - + - - + + - + - - + + 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/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/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 0000000000..13ef5b8f3b Binary files /dev/null and b/msautotest/sld/expected/sld_url_linemark.png differ 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] +# diff --git a/msautotest/wxs/data/test_6325.gpkg b/msautotest/wxs/data/test_6325.gpkg new file mode 100644 index 0000000000..74b3aa6616 Binary files /dev/null and b/msautotest/wxs/data/test_6325.gpkg differ 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/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/expected/wfs_ogr_gpkg_issue_6325.xml b/msautotest/wxs/expected/wfs_ogr_gpkg_issue_6325.xml new file mode 100644 index 0000000000..e8ede1b19c --- /dev/null +++ b/msautotest/wxs/expected/wfs_ogr_gpkg_issue_6325.xml @@ -0,0 +1,36 @@ +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 + LINESTRING(0 0,10 10) + + + + 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/expected/wms_filter_color_bind.png b/msautotest/wxs/expected/wms_filter_color_bind.png new file mode 100644 index 0000000000..81ff9c5f3f Binary files /dev/null and b/msautotest/wxs/expected/wms_filter_color_bind.png differ 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 + + + + 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] 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 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 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 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 + 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/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) 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 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/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 diff --git a/textlayout.c b/textlayout.c index 3fc706b0fb..7eddb33ade 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) @@ -689,7 +696,6 @@ int msLayoutTextSymbol(mapObj *map, textSymbolObj *ts, textPathObj *tgret) { for(i=0;inumlines + runs[i].line_number) * tgret->line_height; if(peny != oldpeny) { @@ -707,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; @@ -739,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; 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