pax_global_header00006660000000000000000000000064140564142070014515gustar00rootroot0000000000000052 comment=9a56286c216c2f2f07b8a18335eff903c60c506c osm2pgrouting-2.3.8/000077500000000000000000000000001405641420700143465ustar00rootroot00000000000000osm2pgrouting-2.3.8/.github/000077500000000000000000000000001405641420700157065ustar00rootroot00000000000000osm2pgrouting-2.3.8/.github/workflows/000077500000000000000000000000001405641420700177435ustar00rootroot00000000000000osm2pgrouting-2.3.8/.github/workflows/libpqxx.yml000066400000000000000000000050641405641420700221620ustar00rootroot00000000000000name: Supported versions of libpqxx on: push: branches-ignore: - 'translations_*' tags: [] pull_request: paths-ignore: - '**.po' jobs: build: name: Build runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: libpqxx: [6, 7] os: [ubuntu-latest] steps: - uses: actions/checkout@v2 - name: Get postgres version run: | sudo service postgresql start pgver=$(psql --version | grep -Po '(?<=psql \(PostgreSQL\) )[^;]+(?=\.\d \()') echo "PGVER=${pgver}" >> $GITHUB_ENV echo "PGIS=3" >> $GITHUB_ENV - name: Add PostgreSQL APT repository run: | sudo apt-get install curl ca-certificates gnupg curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add - sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt/ \ $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' - name: Install dependencies run: | sudo apt-get update sudo apt-get install -y \ libboost-program-options-dev \ libtap-parser-sourcehandler-pgtap-perl \ postgresql-${PGVER} \ postgresql-${PGVER}-pgtap \ postgresql-${PGVER}-postgis-${PGIS} \ postgresql-${PGVER}-postgis-${PGIS}-scripts \ postgresql-${PGVER}-pgrouting \ postgresql-server-dev-${PGVER} - name: Install libpqxx-dev v6 if: matrix.libpqxx == 6 run: | sudo apt-get install -y \ libpqxx-dev - name: download libpqxx-dev v7 if: matrix.libpqxx == 7 uses: actions/checkout@master with: repository: jtv/libpqxx path: ./libpqxx - name: Install libpqxx-dev v7 if: matrix.libpqxx == 7 run: | cd ./libpqxx cmake -DSKIP_BUILD_TEST=on . cmake --build . sudo cmake --install . - name: Configure run: | export PATH=/usr/lib/postgresql/${PGVER}/bin:$PATH mkdir build cd build cmake -DPOSTGRESQL_VERSION=${PGVER} -DCMAKE_BUILD_TYPE=Release -DWITH_DOC=OFF .. - name: Build run: | cd build make -j 4 sudo make install - name: Test if: false run: | sudo service postgresql start sudo -u postgres createdb -p ${PGPORT} ___vrp___test___ sudo -u postgres bash ./tools/testers/pg_prove_tests.sh postgres ${PGPORT} Release osm2pgrouting-2.3.8/.github/workflows/ubuntu.yml000066400000000000000000000044321405641420700220130ustar00rootroot00000000000000name: Build for Ubuntu on: push: branches-ignore: - 'translations_*' tags: [] pull_request: paths-ignore: - '**.po' jobs: build: name: Build runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: psql: [9.6,10,11,12, 13] postgis: [2.5,3] os: [ubuntu-latest] steps: - uses: actions/checkout@v2 - name: get postgres version run: | sudo service postgresql start pgver=$(psql --version | grep -Po '(?<=psql \(PostgreSQL\) )[^;]+(?=\.\d \()') echo "PGVER=${pgver}" >> $GITHUB_ENV PGP=5433 if [ "${{ matrix.psql }}" == "${pgver}" ]; then PGP=5432; fi echo "PGPORT=${PGP}" >> $GITHUB_ENV - name: Add PostgreSQL APT repository run: | sudo apt-get install curl ca-certificates gnupg curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add - sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt/ \ $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' - name: Install dependencies run: | sudo apt-get update sudo apt-get install -y \ libboost-program-options-dev \ libtap-parser-sourcehandler-pgtap-perl \ postgresql-${{ matrix.psql }} \ postgresql-${{ matrix.psql }}-pgtap \ postgresql-${{ matrix.psql }}-postgis-${{ matrix.postgis }} \ postgresql-${{ matrix.psql }}-postgis-${{ matrix.postgis }}-scripts \ postgresql-${{ matrix.psql }}-pgrouting \ libpqxx-dev \ postgresql-server-dev-${{ matrix.psql }} - name: Configure run: | export PATH=/usr/lib/postgresql/${{ matrix.psql }}/bin:$PATH mkdir build cd build cmake -DPOSTGRESQL_VERSION=${{ matrix.psql }} -DCMAKE_BUILD_TYPE=Release -DWITH_DOC=OFF .. - name: Build run: | cd build make -j 4 sudo make install - name: Test if: false run: | sudo service postgresql start sudo -u postgres createdb -p ${PGPORT} ___vrp___test___ sudo -u postgres bash ./tools/testers/pg_prove_tests.sh postgres ${PGPORT} Release osm2pgrouting-2.3.8/.gitignore000066400000000000000000000002711405641420700163360ustar00rootroot00000000000000build bin/* Sample/* build osm2pgrouting tools/vagrant/packaging.sh fix_typos *.o *.out *.osm *.osm.bz2 *.osm.gz *.tar.gz *~ .project .cproject .vagrant run.sh LOG.txt .directory *.swp osm2pgrouting-2.3.8/AUTHORS.md000066400000000000000000000004321405641420700160140ustar00rootroot00000000000000# Project contributors: ## pgROuting Team: * Daniel Kastl * Ko Nagase * Regina Obe * Virginia Vergara ## Other contributors: * Aakash Sharma * Adrien Pavie * Anton Patrushev * Daniel Wendt (Initial author) * Jordan Anderson * J Kishore Kumar * Luís de Sousa * Sarthak Agarwal osm2pgrouting-2.3.8/BOOST_LICENSE_1_0.txt000066400000000000000000000024721405641420700176030ustar00rootroot00000000000000Boost Software License - Version 1.0 - August 17th, 2003 Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. osm2pgrouting-2.3.8/CMakeLists.txt000066400000000000000000000067641405641420700171230ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.2 FATAL_ERROR) if ( ${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR} ) message(FATAL_ERROR "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there. You may need to remove CMakeCache.txt." ) endif() PROJECT(osm2pgrouting) LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") SET(SHARE_DIR "${CMAKE_INSTALL_PREFIX}/share/osm2pgrouting") find_package(PostgreSQL REQUIRED) find_package(PQXX REQUIRED) include_directories(${PQXX_INCLUDE_DIR}) find_package(EXPAT REQUIRED) FILE(GLOB osm2pgrouting_lib_SOURCES "${CMAKE_SOURCE_DIR}/src/*/*.cpp") #--------------------------------------------- # C++ Compiler requirements #--------------------------------------------- #--------------------------------------------- #--------------------------------------------- # Boost #--------------------------------------------- #--------------------------------------------- find_package(Boost ${BOOST_MINIMUM_VERSION} REQUIRED COMPONENTS program_options) if (NOT Boost_VERSION_MACRO) set(Boost_VERSION_MACRO ${Boost_VERSION}) endif() add_definitions(-DBoost_VERSION_MACRO=${Boost_VERSION_MACRO}) add_definitions(-DBOOST_ALLOW_DEPRECATED_HEADERS) include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) message(STATUS "PQXX_VERSION=${PQXX_VERSION}") if (PQXX_VERSION VERSION_GREATER_EQUAL "7.0.0") set(CMAKE_CXX_STANDARD 17) else() add_definitions(-DPQXX_DISCONNECT) set(CMAKE_CXX_STANDARD 14) endif() set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_FILE_OFFSET_BITS=64") set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -Wconversion -pedantic -Wextra -frounding-math -Wno-deprecated -fmax-errors=10") if(WIN32 AND MSVC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_DEPRECATE") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_SCL_SECURE_NO_DEPRECATE") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_SCL_SECURE_NO_WARNINGS") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_NONSTDC_NO_DEPRECATE") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -EHsc") endif() #-------------------------------------------------------- set (OSM2PGROUTING_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/include") message(STATUS "PQXX_INCLUDE_DIR: ${PQXX_INCLUDE_DIR}") message(STATUS "POSTGRESQL_INCLUDE_DIR: ${POSTGRESQL_INCLUDE_DIR}") message(STATUS "EXPAT_INCLUDE_DIRS: ${EXPAT_INCLUDE_DIRS}") message(STATUS "Boost_INCLUDE_DIRS: ${Boost_INCLUDE_DIRS}") message(STATUS "POSTGRESQL_LIBRARIES: ${POSTGRESQL_LIBRARIES}") message(STATUS "Boost_LIBRARIES: ${boost_LIBRARIES}") message(STATUS "PQXX_LIBRARIES: ${PQXX_LIBRARIES}") INCLUDE_DIRECTORIES(src ${POSTGRESQL_INCLUDE_DIR} ${EXPAT_INCLUDE_DIRS} ${OSM2PGROUTING_INCLUDE_DIRS} ) ADD_EXECUTABLE(osm2pgrouting ${osm2pgrouting_lib_SOURCES}) TARGET_LINK_LIBRARIES(osm2pgrouting ${PQXX_LIBRARIES} ${POSTGRESQL_LIBRARIES} ${EXPAT_LIBRARIES} ${Boost_LIBRARIES} ) INSTALL(TARGETS osm2pgrouting RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin" ) if(WIN32) target_link_libraries(osm2pgrouting wsock32 ws2_32) endif() INSTALL(FILES "${CMAKE_SOURCE_DIR}/COPYING" "${CMAKE_SOURCE_DIR}/README.md" "${CMAKE_SOURCE_DIR}/mapconfig.xml" "${CMAKE_SOURCE_DIR}/mapconfig_for_cars.xml" "${CMAKE_SOURCE_DIR}/mapconfig_for_bicycles.xml" "${CMAKE_SOURCE_DIR}/mapconfig_for_pedestrian.xml" DESTINATION "${SHARE_DIR}") #INCLUDE_DIRECTORIES( ${Boost_INCLUDE_DIR} ) #TARGET_LINK_LIBRARIES( osm2pgrouting ${Boost_LIBRARIES} ) osm2pgrouting-2.3.8/CODE_OF_CONDUCT.md000066400000000000000000000135541405641420700171550ustar00rootroot00000000000000Contributors to pgRouting are expected to act respectfully toward others in accordance with the http://www.osgeo.org/code_of_conduct. Full transcription: # OSGeo Code of Conduct Version: 1.0 Date: May 2015 ## Introduction This code of conduct governs how we behave in any OSGeo forum or event and whenever we will be judged by our actions. We expect it to be honored by everyone who participates in the OSGeo community formally or informally, or claims any affiliation with the OSGeo Foundation. It applies to in-person events (such as conferences and related social events), IRC, public and private mailing lists, the issue tracker, the wiki, blogs, Twitter, and any other forums which the community uses for communication and interactions. This code is not exhaustive or complete. It serves to distill our common understanding of a collaborative, shared environment and goals. We expect it to be followed in spirit as much as in the letter, so that it can enrich all of us and the technical communities in which we participate. ## Diversity Statement OSGeo welcomes and encourages participation by everyone. We are committed to being a community that everyone feels good about joining, and we will always work to treat everyone well. No matter how you identify yourself or how others perceive you: we welcome you. Specific Guidelines We strive to: - Be open. We invite anyone to participate in our community. We preferably use public methods of communication for project-related messages, unless discussing something sensitive. This applies to messages for help or project-related support, too; not only is a public support request much more likely to result in an answer to a question, it also makes sure that any inadvertent mistakes made by people answering will be more easily detected and corrected. - Be empathetic, welcoming, friendly, and patient. We work together to resolve conflict, assume good intentions, and do our best to act in an empathetic fashion. We may all experience some frustration from time to time, but we do not allow frustration to turn into a personal attack. A community where people feel uncomfortable or threatened is not a productive one. Note that we have a multi-cultural, multi-lingual community and some of us are non-native speakers. We should be respectful when dealing with other community members as well as with people outside our community. - Be collaborative. Our work will be used by other people, and in turn we will depend on the work of others. When we make something for the benefit of OSGeo, we are willing to explain to others how it works, so that they can build on the work to make it even better. Any decision we make will affect users and colleagues, and we take those consequences seriously when making decisions. - Be inquisitive. Nobody knows everything! Asking questions early avoids many problems later, so questions are encouraged, though they may be directed to the appropriate forum. Those who are asked should be responsive and helpful, within the context of our shared goal of improving OSGeo. - Be careful in the words that we choose. Whether we are participating as professionals or volunteers, we value professionalism in all interactions, and take responsibility for our own speech. Be kind to others. Do not insult or put down other participants. - Be concise Keep in mind that what you write once will be read by hundreds of persons. Writing a short email means people can understand the conversation as efficiently as possible. Short emails should always strive to be empathetic, welcoming, friendly and patient. When a long explanation is necessary, consider adding a summary. Try to bring new ideas to a conversation so that each mail adds something unique to the thread, keeping in mind that the rest of the thread still contains the other messages with arguments that have already been made. Try to stay on topic, especially in discussions that are already fairly large. - Step down considerately. Members of every project come and go. When somebody leaves or disengages from the project they should tell people they are leaving and take the proper steps to ensure that others can pick up where they left off. In doing so, they should remain respectful of those who continue to participate in the project and should not misrepresent the project's goals or achievements. Likewise, community members should respect any individual's choice to leave the project. ## Anti-Harassment Harassment and other exclusionary behaviour are not acceptable. This includes, but is not limited to: - Personal insults or discriminatory jokes and language, especially those using racist or sexist terms. - Offensive comments, excessive or unnecessary profanity. - Intimidation, violent threats or demands. - Sustained disruption of sessions or events. - Stalking, harassing photography or recording. - Unwelcome physical contact or sexual attention. - Repeated harassment of others. In general, if someone asks you to stop, then stop. - Posting (or threatening to post) other people's personally identifying information ("doxing"). - Sharing private content, such as emails sent privately or non-publicly, or unlogged forums such as IRC channel history. - Advocating for, or encouraging, any of the above behaviour. ## Reporting Guidelines If you believe someone is breaking this code of conduct, you may reply to them, and point to this code of conduct. Such messages may be in public or in private, whatever is most appropriate. Assume good faith; it is more likely that participants are unaware of their bad behaviour than that they intentionally try to degrade the quality of the discussion. Should there be difficulties in dealing with the situation, you may report your concerns to event staff, a forum leader or the OSGeo Board. Serious or persistent offenders may be expelled from the event or forum by event organizers or forum leaders. osm2pgrouting-2.3.8/CONTRIBUTING.md000066400000000000000000000041341405641420700166010ustar00rootroot00000000000000# How to contribute We are really glad you are reading this, because we need volunteer developers to help this project. If you have not already, come find us in [![Join the chat at https://gitter.im/pgRouting/pgrouting](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/pgRouting/pgrouting?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) or contact us via [mailing list](http://lists.osgeo.org/mailman/listinfo/pgrouting-dev). We want you working on things you're excited about. Drop a message and if some one can assist you, will contact you back ASAP. Here are some important resources: * [pgRouting Documentation](http://docs.pgrouting.org/) The plugin is based on pgRouting * [Wish list on the wiki](https://github.com/pgRouting/pgrouting/wiki/GSoC-Ideas) is the foot view of what we think the community needs and can be done by GSoC students. * [Additional wish list](https://github.com/pgRouting/osm2pgrouting/issues?q=is%3Aopen+is%3Aissue+label%3A%22Functionality+Request%22). * Participate on the [discussions](https://github.com/pgRouting/osm2pgrouting/issues?q=is%3Aopen+is%3Aissue+label%3ADiscussion). * Participate fixing [Documentation](https://github.com/pgRouting/osm2pgrouting/issues?q=is%3Aopen+is%3Aissue+label%3ADocumentation). * Find a bug? [Let us know](https://github.com/pgRouting/osm2pgrouting/issues). ## Contribution agreement Any kind of contribution will automatically fall to the following Licences: - Code contribution: GNU GENERAL PUBLIC LICENSE Version 2, - Directly by making a pul explicit pull request. - Indirectly by posting code on issues/wiki/gitter/mailng lists - Documentation contribution: - Creative Commons Attribution-Share Alike 3.0 ## Submitting changes Write a clear log message for your commits. One-line messages are fine for small changes, but bigger changes should have more information. Fill the commit message template. ## Coding conventions This is open source software. Consider the people who will read your code, and make it look nice for them. * We indent using four spaces (soft tabs) Thanks, pgRouting team osm2pgrouting-2.3.8/COPYING000066400000000000000000000432541405641420700154110ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. osm2pgrouting-2.3.8/LICENSE000066400000000000000000000432541405641420700153630ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. osm2pgrouting-2.3.8/NEWS000066400000000000000000000103571405641420700150530ustar00rootroot00000000000000osm2pgRouting 2.3.8 * Removing travis tests * Updated FindPQXX to get the version * Adding github actions for compilation: * Postgres 9.6 to 13 * libpqxx 6 & 7 * Minimum Version Postgres 9.6 (supported version at this time) * Minimum Version libpqxx 6 * C++ minimum standard depends on libpqxx * For version libpqxx 6: C++14 * For version libpqxx 7: C++17 osm2pgRouting 2.3.7 * fix: Way - fixed bug handling oneway false conditions * fix: fixed CMakeLists * fix: adjustments to FindPostgreSQL.cmake. * Improvements on mapconfig XML files. * New docs: "Code of Conduct" and "Contributing". * Docs improvements. osm2pgRouting 2.3.6 * Fix: Incorrect classification of one-ways. * Fix: command line configuration in print outputs (db password removed). * Other minor fixes. osm2pgRouting 2.3.5 * Fix: zero division error when max_speed = 0. * Fix: fixed osm_ways parsing - OSMDocument. * Fix: switch to a more inclusive check for nullptr definition (fixes compilation on macOS 10.13 using Xcode 9.3) * Docs improvements: How to release doc. osm2pgRouting 2.3.4 * Fix: osm_elements - fixed boolean assignment with boost::lexical_cast. * Use ${CMAKE_INSTALL_PREFIX} in CMakeLists.txt. * Readme doc improvements: Tips section and Table of contents. osm2pgRouting 2.3.3 * Fix: wrong assumption in implied_oneWay() function * Fix: regression on 2.3.2: fixed hstore refs handling osm2pgRouting 2.3.2 * Fix: Only on linux: use the wc command to count lines * Fix: the `osm_` are created only when addnodes flag is on * Fix: removed unused flag of the command line osm2pgRouting 2.3.1 * Fix: When keys have spaces * Fix: Generating relations osm2pgRouting 2.3.0 * Cost should not return the same value * Added a points of Interest table * Some default one_way values are taken into consideration * Relations are also condsidered * New mapconfig_for_pedestrian.xml osm2pgRouting 2.2.0 * Adding foreign keys to the tables. * Added a progress bar. * New mapconfig_for_bicycles.xml * Improved the C++ code. * remove the use of pointers to avoid leaks. * Table `osm_ways_tags` because information is redundant. osm2pgRouting 2.1.0 New "ways" table structure: Column | Type | Modifications -------------------+---------------------------+---------------- gid | bigint | class_id | integer | length | double precision | length_m | double precision | new column name | text | source | bigint | target | bigint | x1 | double precision | y1 | double precision | x2 | double precision | y2 | double precision | cost | double precision | name changed from 'to_cost' reverse_cost | double precision | cost_s | double precision | new column reverse_cost_s | double precision | new column rule | text | one_way | integer | new column maxspeed_forward | integer | maxspeed_backward | integer | osm_id | bigint | source_osm | bigint | new column target_osm | bigint | new column priority | double precision | the_geom | geometry(LineString,4326) | New "ways_vertices_pgr" table structure Column | Type | Modifications ----------+----------------------+---------------------------------------------------------------- id | bigint | osm_id | bigint | new column cnt | integer | chk | integer | ein | integer | eout | integer | lon | numeric(11,8) | new column lat | numeric(11,8) | new column the_geom | geometry(Point,4326) | Indexes: "ways_vertices_pgr_pkey" PRIMARY KEY, btree (id) "vertex_id" UNIQUE CONSTRAINT, btree (osm_id) "ways_vertices_pgr_gdx" gist (the_geom) "ways_vertices_pgr_osm_id_idx" btree (osm_id) osm2pgrouting-2.3.8/README.md000066400000000000000000000117141405641420700156310ustar00rootroot00000000000000# osm2pgrouting [![Join the chat at https://gitter.im/pgRouting/osm2pgrouting](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/pgRouting/osm2pgrouting?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) # Table of Contents * [Requirements](#requirements) * [Documentation](#documentation) * [Installation](#installation) * [How to use](#how-to-use) * [Tips](#tips) ## Requirements Before you can use this tool for importing Openstreetmap data you need to install: 1. postgresql 2. postgis 3. pgrouting 4. boost 5. expat 5. libpqxx 6. cmake and to prepare a database. ## Documentation See in the documentation of the pgrouting website for more information: http://pgrouting.org ## Installation For compiling this tool, you will need boost, libpqxx, expat and cmake: Then just type the following in the root directory: ``` cmake -H. -Bbuild cd build/ make make install ``` Install some prerequisites: ``` sudo apt-get install expat sudo apt-get install libexpat1-dev sudo apt-get install libboost-dev sudo apt-get install libboost-program-options-dev sudo apt install libpqxx-dev ``` **Note:** FindLibPQXX.cmake does not find the version of libpqxx, but its documentation says C++11 is needed for the latests versions. If you have libraries installed in non-standard locations, you might need to pass in parameters to cmake. Commonly useful parameters are CMAKE options: -DBOOST_ROOT:PATH=/path/to/boost folder that contains include, lib, bin directories for boost -DEXPATH_INCLUDE_DIR:PATH=/path/to/expat/include the include folder for where your expat is installed -DPOSTGRESQL_INCLUDE_DIR:PATH=/path/to/postgresql/include the include folder for postgresql development headers A cmake with custom options might look something like ``` cmake -DBOOST_ROOT:PATH=/local/projects/rel-boost-1.58.0 \ -DPOSTGRESQL_INCLUDE_DIR:PATH=/local/projects/rel-pg94/include -Bbuild ``` ## How to use Prepare the database: ``` createdb routing psql --dbname routing -c 'CREATE EXTENSION postgis' psql --dbname routing -c 'CREATE EXTENSION pgRouting' ``` Start the program like this: ``` osm2pgrouting --f your-OSM-XML-File.osm --conf mapconfig.xml --dbname routing --username postgres --clean ``` Do incremental adition of data without using --clean ``` osm2pgrouting --f next-OSM-XML-File.osm --conf mapconfig.xml --dbname routing --username postgres ``` A complete list of arguments are: ``` osm2pgrouting --help Allowed options: Help: --help Produce help message for this version. -v [ --version ] Print version string General: -f [ --file ] arg REQUIRED: Name of the osm file. -c [ --conf ] arg (=/usr/share/osm2pgrouting/mapconfig.xml) Name of the configuration xml file. --schema arg Database schema to put tables. blank: defaults to default schema dictated by PostgreSQL search_path. --prefix arg Prefix added at the beginning of the table names. --suffix arg Suffix added at the end of the table names. --addnodes Import the osm_nodes, osm_ways & osm_relations tables. --attributes Include attributes information. --tags Include tag information. --chunk arg (=20000) Exporting chunk size. --clean Drop previously created tables. --no-index Do not create indexes (Use when indexes are already created) Database options: -d [ --dbname ] arg Name of your database (Required). -U [ --username ] arg Name of the user, which have write access to the database. -h [ --host ] arg (=localhost) Host of your postgresql database. -p [ --port ] arg (=5432) db_port of your database. -W [ --password ] arg Password for database access. ``` ## Tips Open Street Map (OSM) files contains tags not used at all for routing operations by PgRouting (i.e. author, version, timestamps, etc.). You can reduce a lot the size of your OSM file to import removing this metadata tags from original file (you can get around half size of original file). The best tool to remove tags is [osmconvert](https://wiki.openstreetmap.org/wiki/Osmconvert). There are another tools but osmconvert is the fastest parsing osm files. Example: ``` $ osmconvert output_data.osm.pbf --drop-author --drop-version --out-osm -o=output_data_reduc.osm ``` You can download OSM data as PBF (protobuffer) format. This is a binary format and it has a lower size than OSM raw files (better for downloading operations). osm2pgrouting-2.3.8/Vagrantfile000066400000000000000000000010531405641420700165320ustar00rootroot00000000000000# -*- mode: ruby -*- # vi: set ft=ruby : pgbox = ENV['BOX'] || "precise64" Vagrant.configure("2") do |config| # Vagrant box configuration config.vm.box = pgbox config.vm.box_url = "http://files.vagrantup.com/%s.box" % [pgbox] # Bootstrap script config.vm.provision :shell, :path => "tools/vagrant/bootstrap.sh" # Forward SSH agent to host config.ssh.forward_agent = true # Create synced folder for GnuPG keys config.vm.synced_folder "~/.gnupg", "/home/vagrant/.gnupg" config.vm.synced_folder "../", "/home/vagrant/repos" end osm2pgrouting-2.3.8/ci/000077500000000000000000000000001405641420700147415ustar00rootroot00000000000000osm2pgrouting-2.3.8/ci/travis/000077500000000000000000000000001405641420700162515ustar00rootroot00000000000000osm2pgrouting-2.3.8/ci/travis/install-libpqxx.sh000077500000000000000000000010521405641420700217410ustar00rootroot00000000000000#!/bin/bash # ------------------------------------------------------------------------------ # Travis CI scripts # Copyright(c) pgRouting Contributors # # Install osm2pgrouting prerequesits # ------------------------------------------------------------------------------ set -e DISTRIBUTION="$1" if [[ -z "$DISTRIBUTION=" ]] ; then exit 1 fi if [[ "$DISTRIBUTION" = "precise" ]] ; then sudo apt-get install -y libpqxx3-dev elif [[ "$DISTRIBUTION" = "trusty" ]] ; then sudo sudo sudo apt-get install -y libpqxx-dev else exit 1 fi osm2pgrouting-2.3.8/ci/travis/install-postgres.sh000077500000000000000000000036211405641420700221240ustar00rootroot00000000000000#!/bin/bash # ------------------------------------------------------------------------------ # Travis CI scripts # Copyright(c) pgRouting Contributors # # Install pgRouting prerequesits # ------------------------------------------------------------------------------ set -e POSTGRESQL_VERSION="$1" PGUSER="$2" if [[ -z "$POSTGRESQL_VERSION" ]] ; then exit 1 fi if [[ "$POSTGRESQL_VERSION" = "9.5" || "$POSTGRESQL_VERSION" = "9.6" ]] ; then POSTGIS_VERSION="2.3" fi echo "Postgres $POSTGRESQL_VERSION" echo "User $PGUSER" sudo /etc/init.d/postgresql stop sudo apt-get install -y \ postgresql-server-dev-$POSTGRESQL_VERSION if [[ "$POSTGRESQL_VERSION" = "9.5" || "$POSTGRESQL_VERSION" = "9.6" ]] ; then ## removing unused postgresql #sudo apt-get -y remove --purge postgresql-9.1 #sudo apt-get -y remove --purge postgresql-9.2 #sudo apt-get -y remove --purge postgresql-9.3 #sudo apt-get -y remove --purge postgresql-9.4 #sudo apt-get -y remove --purge postgresql-9.5 #sudo apt-get -y remove --purge postgresql-9.6 echo "Installing postgresql $POSTGRESQL_VERSION & postgis " sudo apt-get install -y \ postgresql-$POSTGRESQL_VERSION \ postgresql-$POSTGRESQL_VERSION-postgis-$POSTGIS_VERSION sudo cp /usr/lib/postgresql/$POSTGRESQL_VERSION/bin/pg_config /usr/bin/pg_config sudo /etc/init.d/postgresql stop sudo cat /etc/postgresql/$POSTGRESQL_VERSION/main/pg_hba.conf sudo sed -i -e 's/port = 5433/port = 5432/g' /etc/postgresql/$POSTGRESQL_VERSION/main/postgresql.conf sudo cp $TRAVIS_BUILD_DIR/ci/travis/pg_hba.conf /etc/postgresql/$POSTGRESQL_VERSION/main/pg_hba.conf ps -fea | grep postgres fi sudo /etc/init.d/postgresql start $POSTGRESQL_VERSION # intall pgtap after postgres instance has started sudo apt-get install -y postgresql-$POSTGRESQL_VERSION-pgtap sudo apt-get install -y libtap-parser-sourcehandler-pgtap-perl osm2pgrouting-2.3.8/ci/travis/osm2pgrouting_build.sh000077500000000000000000000005601405641420700226070ustar00rootroot00000000000000#!/bin/bash # ------------------------------------------------------------------------------ # Travis CI scripts # Copyright(c) pgRouting Contributors # # Build pgRouting # ------------------------------------------------------------------------------ # exit script on error set -e # build osm2pgrouting mkdir build cd build cmake .. make sudo make install cd .. osm2pgrouting-2.3.8/ci/travis/pg_hba.conf000077500000000000000000000010501405641420700203370ustar00rootroot00000000000000# PostgreSQL Client Authentication Configuration File # =================================================== # # This file is configured to allow trusted authentication for Travis # automated installation and tests. # # Database administrative login by Unix domain socket local all postgres trust local all all trust host all all 127.0.0.1/32 trust host all all ::1/128 trust osm2pgrouting-2.3.8/cmake/000077500000000000000000000000001405641420700154265ustar00rootroot00000000000000osm2pgrouting-2.3.8/cmake/FindOsmium.cmake000066400000000000000000000322621405641420700205070ustar00rootroot00000000000000#---------------------------------------------------------------------- # # FindOsmium.cmake # # Find the Libosmium headers and, optionally, several components needed # for different Libosmium functions. # #---------------------------------------------------------------------- # # Usage: # # Copy this file somewhere into your project directory, where cmake can # find it. Usually this will be a directory called "cmake" which you can # add to the CMake module search path with the following line in your # CMakeLists.txt: # # list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") # # Then add the following in your CMakeLists.txt: # # find_package(Osmium [version] REQUIRED COMPONENTS ) # include_directories(SYSTEM ${OSMIUM_INCLUDE_DIRS}) # # The version number is optional. If it is not set, any version of # libosmium will do. # # For the substitute a space separated list of one or more of the # following components: # # pbf - include libraries needed for PBF input and output # xml - include libraries needed for XML input and output # io - include libraries needed for any type of input/output # geos - include if you want to use any of the GEOS functions # gdal - include if you want to use any of the OGR functions # proj - include if you want to use any of the Proj.4 functions # sparsehash - include if you use the sparsehash index # # You can check for success with something like this: # # if(NOT OSMIUM_FOUND) # message(WARNING "Libosmium not found!\n") # endif() # #---------------------------------------------------------------------- # # Variables: # # OSMIUM_FOUND - True if Osmium found. # OSMIUM_INCLUDE_DIRS - Where to find include files. # OSMIUM_XML_LIBRARIES - Libraries needed for XML I/O. # OSMIUM_PBF_LIBRARIES - Libraries needed for PBF I/O. # OSMIUM_IO_LIBRARIES - Libraries needed for XML or PBF I/O. # OSMIUM_LIBRARIES - All libraries Osmium uses somewhere. # #---------------------------------------------------------------------- # This is the list of directories where we look for osmium and protozero # includes. set(_osmium_include_path ../libosmium ~/Library/Frameworks /Library/Frameworks /opt/local # DarwinPorts /opt ) # Look for the header file. find_path(OSMIUM_INCLUDE_DIR osmium/version.hpp PATH_SUFFIXES include PATHS ${_osmium_include_path} ) # Check libosmium version number if(Osmium_FIND_VERSION) file(STRINGS "${OSMIUM_INCLUDE_DIR}/osmium/version.hpp" _libosmium_version_define REGEX "#define LIBOSMIUM_VERSION_STRING") if("${_libosmium_version_define}" MATCHES "#define LIBOSMIUM_VERSION_STRING \"([0-9.]+)\"") set(_libosmium_version "${CMAKE_MATCH_1}") else() set(_libosmium_version "unknown") endif() endif() set(OSMIUM_INCLUDE_DIRS "${OSMIUM_INCLUDE_DIR}") #---------------------------------------------------------------------- # # Check for optional components # #---------------------------------------------------------------------- if(Osmium_FIND_COMPONENTS) foreach(_component ${Osmium_FIND_COMPONENTS}) string(TOUPPER ${_component} _component_uppercase) set(Osmium_USE_${_component_uppercase} TRUE) endforeach() endif() #---------------------------------------------------------------------- # Component 'io' is an alias for 'pbf' and 'xml' if(Osmium_USE_IO) set(Osmium_USE_PBF TRUE) set(Osmium_USE_XML TRUE) endif() #---------------------------------------------------------------------- # Component 'ogr' is an alias for 'gdal' if(Osmium_USE_OGR) set(Osmium_USE_GDAL TRUE) endif() #---------------------------------------------------------------------- # Component 'pbf' if(Osmium_USE_PBF) find_package(ZLIB) find_package(Threads) message(STATUS "Looking for protozero") find_path(PROTOZERO_INCLUDE_DIR protozero/version.hpp PATH_SUFFIXES include PATHS ${_osmium_include_path} ${OSMIUM_INCLUDE_DIR} ) if(PROTOZERO_INCLUDE_DIR) message(STATUS "Looking for protozero - found") else() message(STATUS "Looking for protozero - not found") endif() list(APPEND OSMIUM_EXTRA_FIND_VARS ZLIB_FOUND Threads_FOUND PROTOZERO_INCLUDE_DIR) if(ZLIB_FOUND AND Threads_FOUND AND PROTOZERO_INCLUDE_DIR) list(APPEND OSMIUM_PBF_LIBRARIES ${ZLIB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) if(WIN32) # This is needed for the ntohl() function list(APPEND OSMIUM_PBF_LIBRARIES ws2_32) endif() list(APPEND OSMIUM_INCLUDE_DIRS ${ZLIB_INCLUDE_DIR} ${PROTOZERO_INCLUDE_DIR} ) else() message(WARNING "Osmium: Can not find some libraries for PBF input/output, please install them or configure the paths.") endif() endif() #---------------------------------------------------------------------- # Component 'xml' if(Osmium_USE_XML) find_package(EXPAT) find_package(BZip2) find_package(ZLIB) find_package(Threads) list(APPEND OSMIUM_EXTRA_FIND_VARS EXPAT_FOUND BZIP2_FOUND ZLIB_FOUND Threads_FOUND) if(EXPAT_FOUND AND BZIP2_FOUND AND ZLIB_FOUND AND Threads_FOUND) list(APPEND OSMIUM_XML_LIBRARIES ${EXPAT_LIBRARIES} ${BZIP2_LIBRARIES} ${ZLIB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) list(APPEND OSMIUM_INCLUDE_DIRS ${EXPAT_INCLUDE_DIR} ${BZIP2_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR} ) else() message(WARNING "Osmium: Can not find some libraries for XML input/output, please install them or configure the paths.") endif() endif() #---------------------------------------------------------------------- list(APPEND OSMIUM_IO_LIBRARIES ${OSMIUM_PBF_LIBRARIES} ${OSMIUM_XML_LIBRARIES} ) list(APPEND OSMIUM_LIBRARIES ${OSMIUM_IO_LIBRARIES} ) #---------------------------------------------------------------------- # Component 'geos' if(Osmium_USE_GEOS) find_path(GEOS_INCLUDE_DIR geos/geom.h) find_library(GEOS_LIBRARY NAMES geos) list(APPEND OSMIUM_EXTRA_FIND_VARS GEOS_INCLUDE_DIR GEOS_LIBRARY) if(GEOS_INCLUDE_DIR AND GEOS_LIBRARY) SET(GEOS_FOUND 1) list(APPEND OSMIUM_LIBRARIES ${GEOS_LIBRARY}) list(APPEND OSMIUM_INCLUDE_DIRS ${GEOS_INCLUDE_DIR}) else() message(WARNING "Osmium: GEOS library is required but not found, please install it or configure the paths.") endif() endif() #---------------------------------------------------------------------- # Component 'gdal' (alias 'ogr') if(Osmium_USE_GDAL) find_package(GDAL) list(APPEND OSMIUM_EXTRA_FIND_VARS GDAL_FOUND) if(GDAL_FOUND) list(APPEND OSMIUM_LIBRARIES ${GDAL_LIBRARIES}) list(APPEND OSMIUM_INCLUDE_DIRS ${GDAL_INCLUDE_DIRS}) else() message(WARNING "Osmium: GDAL library is required but not found, please install it or configure the paths.") endif() endif() #---------------------------------------------------------------------- # Component 'proj' if(Osmium_USE_PROJ) find_path(PROJ_INCLUDE_DIR proj_api.h) find_library(PROJ_LIBRARY NAMES proj) list(APPEND OSMIUM_EXTRA_FIND_VARS PROJ_INCLUDE_DIR PROJ_LIBRARY) if(PROJ_INCLUDE_DIR AND PROJ_LIBRARY) set(PROJ_FOUND 1) list(APPEND OSMIUM_LIBRARIES ${PROJ_LIBRARY}) list(APPEND OSMIUM_INCLUDE_DIRS ${PROJ_INCLUDE_DIR}) else() message(WARNING "Osmium: PROJ.4 library is required but not found, please install it or configure the paths.") endif() endif() #---------------------------------------------------------------------- # Component 'sparsehash' if(Osmium_USE_SPARSEHASH) find_path(SPARSEHASH_INCLUDE_DIR google/sparsetable) list(APPEND OSMIUM_EXTRA_FIND_VARS SPARSEHASH_INCLUDE_DIR) if(SPARSEHASH_INCLUDE_DIR) # Find size of sparsetable::size_type. This does not work on older # CMake versions because they can do this check only in C, not in C++. if(NOT CMAKE_VERSION VERSION_LESS 3.0) include(CheckTypeSize) set(CMAKE_REQUIRED_INCLUDES ${SPARSEHASH_INCLUDE_DIR}) set(CMAKE_EXTRA_INCLUDE_FILES "google/sparsetable") check_type_size("google::sparsetable::size_type" SPARSETABLE_SIZE_TYPE LANGUAGE CXX) set(CMAKE_EXTRA_INCLUDE_FILES) set(CMAKE_REQUIRED_INCLUDES) else() set(SPARSETABLE_SIZE_TYPE ${CMAKE_SIZEOF_VOID_P}) endif() # Sparsetable::size_type must be at least 8 bytes (64bit), otherwise # OSM object IDs will not fit. if(SPARSETABLE_SIZE_TYPE GREATER 7) set(SPARSEHASH_FOUND 1) add_definitions(-DOSMIUM_WITH_SPARSEHASH=${SPARSEHASH_FOUND}) list(APPEND OSMIUM_INCLUDE_DIRS ${SPARSEHASH_INCLUDE_DIR}) else() message(WARNING "Osmium: Disabled Google SparseHash library on 32bit system (size_type=${SPARSETABLE_SIZE_TYPE}).") endif() else() message(WARNING "Osmium: Google SparseHash library is required but not found, please install it or configure the paths.") endif() endif() #---------------------------------------------------------------------- list(REMOVE_DUPLICATES OSMIUM_INCLUDE_DIRS) if(OSMIUM_XML_LIBRARIES) list(REMOVE_DUPLICATES OSMIUM_XML_LIBRARIES) endif() if(OSMIUM_PBF_LIBRARIES) list(REMOVE_DUPLICATES OSMIUM_PBF_LIBRARIES) endif() if(OSMIUM_IO_LIBRARIES) list(REMOVE_DUPLICATES OSMIUM_IO_LIBRARIES) endif() if(OSMIUM_LIBRARIES) list(REMOVE_DUPLICATES OSMIUM_LIBRARIES) endif() #---------------------------------------------------------------------- # # Check that all required libraries are available # #---------------------------------------------------------------------- if(OSMIUM_EXTRA_FIND_VARS) list(REMOVE_DUPLICATES OSMIUM_EXTRA_FIND_VARS) endif() # Handle the QUIETLY and REQUIRED arguments and the optional version check # and set OSMIUM_FOUND to TRUE if all listed variables are TRUE. include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Osmium REQUIRED_VARS OSMIUM_INCLUDE_DIR ${OSMIUM_EXTRA_FIND_VARS} VERSION_VAR _libosmium_version) unset(OSMIUM_EXTRA_FIND_VARS) #---------------------------------------------------------------------- # # A function for setting the -pthread option in compilers/linkers # #---------------------------------------------------------------------- function(set_pthread_on_target _target) if(NOT MSVC) set_target_properties(${_target} PROPERTIES COMPILE_FLAGS "-pthread") if(NOT APPLE) set_target_properties(${_target} PROPERTIES LINK_FLAGS "-pthread") endif() endif() endfunction() #---------------------------------------------------------------------- # # Add compiler flags # #---------------------------------------------------------------------- add_definitions(-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64) if(MSVC) add_definitions(-wd4996) # Disable warning C4068: "unknown pragma" because we want it to ignore # pragmas for other compilers. add_definitions(-wd4068) # Disable warning C4715: "not all control paths return a value" because # it generates too many false positives. add_definitions(-wd4715) # Disable warning C4351: new behavior: elements of array '...' will be # default initialized. The new behaviour is correct and we don't support # old compilers anyway. add_definitions(-wd4351) # Disable warning C4503: "decorated name length exceeded, name was truncated" # there are more than 150 of generated names in libosmium longer than 4096 symbols supported in MSVC add_definitions(-wd4503) add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN -D_CRT_SECURE_NO_WARNINGS) endif() if(APPLE) # following only available from cmake 2.8.12: # add_compile_options(-stdlib=libc++) # so using this instead: add_definitions(-stdlib=libc++) set(LDFLAGS ${LDFLAGS} -stdlib=libc++) endif() #---------------------------------------------------------------------- # This is a set of recommended warning options that can be added when compiling # libosmium code. if(MSVC) set(OSMIUM_WARNING_OPTIONS "/W3 /wd4514" CACHE STRING "Recommended warning options for libosmium") else() set(OSMIUM_WARNING_OPTIONS "-Wall -Wextra -pedantic -Wredundant-decls -Wdisabled-optimization -Wctor-dtor-privacy -Wnon-virtual-dtor -Woverloaded-virtual -Wsign-promo -Wold-style-cast" CACHE STRING "Recommended warning options for libosmium") endif() set(OSMIUM_DRACONIC_CLANG_OPTIONS "-Wdocumentation -Wunused-exception-parameter -Wmissing-declarations -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-unused-macros -Wno-exit-time-destructors -Wno-global-constructors -Wno-padded -Wno-switch-enum -Wno-missing-prototypes -Wno-weak-vtables -Wno-cast-align -Wno-float-equal") if(Osmium_DEBUG) message(STATUS "OSMIUM_XML_LIBRARIES=" ${OSMIUM_XML_LIBRARIES}) message(STATUS "OSMIUM_PBF_LIBRARIES=" ${OSMIUM_PBF_LIBRARIES}) message(STATUS "OSMIUM_IO_LIBRARIES=" ${OSMIUM_IO_LIBRARIES}) message(STATUS "OSMIUM_LIBRARIES=" ${OSMIUM_LIBRARIES}) message(STATUS "OSMIUM_INCLUDE_DIRS=" ${OSMIUM_INCLUDE_DIRS}) endif() osm2pgrouting-2.3.8/cmake/FindPQXX.cmake000066400000000000000000000036331405641420700200360ustar00rootroot00000000000000# - Find libpqxx # Find the libpqxx includes and client library # This module defines # PQXX_INCLUDE_DIRS # PQXX_LIBRARIES # PQXX_FOUND include (FindPackageHandleStandardArgs) # # Look for an installation. # find_path( PQXX_INCLUDE_DIR NAMES pqxx/pqxx PATHS ${_PQXX_DIR}/include ${_PQXX_DIR} ${CMAKE_INSTALL_PREFIX}/include /usr/local/pgsql/include /usr/local/include /usr/include DOC "pqxx include directories" ) mark_as_advanced (PQXX_INCLUDE_DIR) find_library (PQXX_LIBRARIES NAMES pqxx DOC "pqxx library" ) mark_as_advanced (PQXX_LIBRARY) if (PQXX_INCLUDE_DIR) if (EXISTS "${PQXX_INCLUDE_DIR}/pqxx/version.hxx") set (PQXX_VERSION_FILE "${PQXX_INCLUDE_DIR}/pqxx/version.hxx") message(STATUS "PQXX_VERSION_FILE=${PQXX_VERSION_FILE}") file(READ "${PQXX_VERSION_FILE}" PQXX_FILE) string(REGEX MATCH "PQXX_VERSION \"([0-9]*).([0-9]*).([0-9]*)" PQXX_VERSION_LINE ${PQXX_FILE}) string(REGEX REPLACE "PQXX_VERSION \"([0-9]*).([0-9]*).([0-9]*)" "\\1" PQXX_VERSION_MAYOR ${PQXX_VERSION_LINE}) string(REGEX REPLACE "PQXX_VERSION \"([0-9]*).([0-9]*).([0-9]*)" "\\2" PQXX_VERSION_MINOR ${PQXX_VERSION_LINE}) string(REGEX REPLACE "PQXX_VERSION \"([0-9]*).([0-9]*).([0-9]*)" "\\3" PQXX_VERSION_MICRO ${PQXX_VERSION_LINE}) set(PQXX_VERSION "${PQXX_VERSION_MAYOR}.${PQXX_VERSION_MINOR}.${PQXX_VERSION_MICRO}") message(STATUS "PQXX_VERSION=${PQXX_VERSION}") message(STATUS "PQXX_VERSION_MAYOR=${PQXX_VERSION_MAYOR}") message(STATUS "PQXX_VERSION_MINOR=${PQXX_VERSION_MINOR}") message(STATUS "PQXX_VERSION_MICRO=${PQXX_VERSION_MICRO}") unset(PQXX_VERSION_MAYOR) unset(PQXX_VERSION_MINOR) unset(PQXX_VERSION_MICRO) unset(PQXX_FILE) unset(PQXX_VERSION_LINE) endif() endif() FIND_PACKAGE_HANDLE_STANDARD_ARGS("PQXX" VERSION_VAR PQXX_VERSION REQUIRED_VARS PQXX_LIBRARIES PQXX_INCLUDE_DIR FAIL_MESSAGE "libpqxx couldn't be found" ) osm2pgrouting-2.3.8/cmake/FindPostgreSQL.cmake000066400000000000000000000050671405641420700212440ustar00rootroot00000000000000# - Find PostgreSQL # Find the PostgreSQL includes and client library # This module defines # POSTGRESQL_INCLUDE_DIR, where to find POSTGRESQL.h # POSTGRESQL_LIBRARIES, the libraries needed to use POSTGRESQL. # POSTGRESQL_FOUND, If false, do not try to use PostgreSQL. # Copyright (c) 2006, Jaroslaw Staniek, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. if(POSTGRESQL_INCLUDE_DIR AND POSTGRESQL_LIBRARIES) set(POSTGRESQL_FOUND TRUE) else(POSTGRESQL_INCLUDE_DIR AND POSTGRESQL_LIBRARIES) find_program(POSTGRESQL_PG_CONFIG NAMES pg_config PATHS /usr/lib/postgresql/*/bin/ ) message(STATUS "POSTGRESQL_PG_CONFIG is " ${POSTGRESQL_PG_CONFIG}) if(POSTGRESQL_PG_CONFIG) execute_process( COMMAND ${POSTGRESQL_PG_CONFIG} --includedir OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE T_POSTGRESQL_INCLUDE_DIR) endif(POSTGRESQL_PG_CONFIG) find_path(POSTGRESQL_INCLUDE_DIR libpq-fe.h ${T_POSTGRESQL_INCLUDE_DIR} /usr/pgsql-*/include /usr/include /usr/include/pgsql /usr/local/include/pgsql /usr/include/postgresql /usr/include/postgresql/* /usr/local/include/postgresql /usr/local/include/postgresql/* $ENV{ProgramFiles}/PostgreSQL/*/include $ENV{SystemDrive}/PostgreSQL/*/include ) if(POSTGRESQL_PG_CONFIG) execute_process( COMMAND ${POSTGRESQL_PG_CONFIG} --libdir OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE T_POSTGRESQL_LIB_DIR) endif(POSTGRESQL_PG_CONFIG) find_library(POSTGRESQL_LIBRARIES NAMES pq libpq ${T_POSTGRESQL_LIB_DIR} PATHS /usr/pgsql-*/lib /usr/lib /usr/local/lib /usr/lib/postgresql $ENV{ProgramFiles}/PostgreSQL/*/lib $ENV{SystemDrive}/PostgreSQL/*/lib ) if(POSTGRESQL_INCLUDE_DIR AND POSTGRESQL_LIBRARIES) set(POSTGRESQL_FOUND TRUE) message(STATUS "POSTGRESQL_INCLUDE_DIR: ${POSTGRESQL_INCLUDE_DIR}") message(STATUS "POSTGRESQL_LIBRARIES: ${POSTGRESQL_LIBRARIES}") include_directories(${POSTGRESQL_INCLUDE_DIR}) else(POSTGRESQL_INCLUDE_DIR AND POSTGRESQL_LIBRARIES) set(POSTGRESQL_FOUND FALSE) message(STATUS "PostgreSQL not found.") endif(POSTGRESQL_INCLUDE_DIR AND POSTGRESQL_LIBRARIES) mark_as_advanced(POSTGRESQL_INCLUDE_DIR POSTGRESQL_LIBRARIES) endif(POSTGRESQL_INCLUDE_DIR AND POSTGRESQL_LIBRARIES) osm2pgrouting-2.3.8/docs/000077500000000000000000000000001405641420700152765ustar00rootroot00000000000000osm2pgrouting-2.3.8/docs/CNAME000066400000000000000000000000221405641420700160360ustar00rootroot00000000000000osm.pgrouting.org osm2pgrouting-2.3.8/docs/how_to_release.md000066400000000000000000000025241405641420700206220ustar00rootroot00000000000000# How to release a new version This document explains step by step how to release a new version for osm2pgrouting. ## Steps to follow 0. Make sure that the NEWS file has the changes for the release. (review issues closed in milestone). 1. Clone branch master: ``` $ git clone git@github.com:pgRouting/osm2pgrouting.git ``` 2. Make sure you are in the last update: ``` $ git pull origin main ``` 3. Make sure last updates was compiled without errors in Travis CI: https://travis-ci.org/pgRouting/osm2pgrouting/builds 4. Make the tag for this version (change version number from example): ``` $ git tag -a -m "Create osm2pgrouting v2.3.4 tag" v2.3.4 ``` 5. Push the tag (change version number from example): ``` $ git push origin v2.3.4 ``` 6. Go to Github repository and make sure the new tag was created: https://github.com/pgRouting/osm2pgrouting/releases 7. Click on the tag number then click on the edit tag button for release title (use the same number, i.e. v2.3.4). 8. Write comments about changes introduced by this new release (review issues closed in milestone or the NEWS file). Click on update release button. 9. Close milestone. 10. Prepare next release: - Create new milestone. - Update version in this file: https://github.com/pgRouting/osm2pgrouting/blob/master/src/osm_elements/osm2pgrouting.cpp#L101. osm2pgrouting-2.3.8/include/000077500000000000000000000000001405641420700157715ustar00rootroot00000000000000osm2pgrouting-2.3.8/include/configuration/000077500000000000000000000000001405641420700206405ustar00rootroot00000000000000osm2pgrouting-2.3.8/include/configuration/configuration.h000066400000000000000000000066041405641420700236660ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2016 by pgRouting developers * * project@pgrouting.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License t &or more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef SRC_CONFIGURATION_H_ #define SRC_CONFIGURATION_H_ #include #include #include #include "configuration/tag_key.h" #include "configuration/tag_value.h" #include "osm_elements/osm_tag.h" namespace osm2pgr { class Configuration { public: Configuration() = default; /** @brief add group of tag_key + all the tag_values * * @param[in] t_key Tag_key found in the configuration */ void add_tag_key(const Tag_key &t_key); /** @brief retrieves the Tag_value (attrributes * * @param[in] tag Tag found in the configuration * @returns Tag_value */ const Tag_value& tag_value(const Tag &tag) const; /* Is the (key, value) pair in the configuration? * * * @param[in] tag Tag (key, value) pair */ bool has_tag(const Tag &tag) const; /** retrieves the maxspeed based on the tag * * if the (key,value) has a value this is returned * else if the (key, *) has a value this is returned * else 50 is returned */ double maxspeed(const Tag &tag) const; double maxspeed_forward(const Tag &tag) const; double maxspeed_backward(const Tag &tag) const; /** retrieves the priority based on the tag * * if the (key,value) has a value this is returned * else if the (key, *) has a value this is returned * else 0 is returned */ double priority(const Tag &tag) const; /* * data to be exported to configuration TABLE */ const std::map& types() const {return m_Tag_keys;} private: /** @brief is the tag key in the configuration file * * @param[in] key tag_key name="key" */ bool has_tag_key(const std::string &key) const; const Tag_key& tag_key(const Tag &tag) const; private: std::map m_Tag_keys; }; } // end namespace osm2pgr #endif // SRC_CONFIGURATION_H_ osm2pgrouting-2.3.8/include/configuration/tag_key.h000066400000000000000000000047621405641420700224450ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2016 by pgRouting developers * * project@pgrouting.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License t &or more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef SRC_TYPE_H_ #define SRC_TYPE_H_ #include #include #include #include #include "./tag_value.h" namespace osm2pgr { class Tag_key : Element { public: Tag_key() = default; Tag_key(const Tag_key &) = default; /** @brief build it * @param atts attributes read py the parser */ explicit Tag_key(const char **atts); void add_tag_value(const Tag_value &p_values); /* to have or not to have */ bool has(const Tag &tag, const std::string &str) const; bool has_tag_value(const Tag &tag) const; /* get it*/ std::string get(const Tag &tag, const std::string &str) const; const Tag_value& tag_value(const Tag &tag) const; inline int64_t id() const {return osm_id();} inline std::string name() const {return get_attribute("name");} /* used in the export function */ std::vector values( const std::vector &columns) const; private: std::map m_Tag_values; }; } // namespace osm2pgr #endif // SRC_TYPE_H_ osm2pgrouting-2.3.8/include/configuration/tag_value.h000066400000000000000000000041351405641420700227630ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2016 by pgRouting developers * * project@pgrouting.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License t &or more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef SRC_CLASS_H_ #define SRC_CLASS_H_ #include #include #include #include #include #include namespace osm2pgr { class Tag_value : public Element { public: Tag_value() = default; Tag_value(const Tag_value &) = default; /** @brief build it */ explicit Tag_value(const char ** attributes); /** @brief get it */ inline int64_t id() const {return osm_id();} std::string name() const; std::string get(const std::string &str) const; std::vector export_values() const; }; } // end namespace osm2pgr #endif // SRC_CLASS_H_ osm2pgrouting-2.3.8/include/database/000077500000000000000000000000001405641420700175355ustar00rootroot00000000000000osm2pgrouting-2.3.8/include/database/Export2DB.h000066400000000000000000000116161405641420700214640ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2016 by pgRouting developers * * project@pgrouting.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License t &or more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef SRC_EXPORT2DB_H_ #define SRC_EXPORT2DB_H_ #include #include #include #include #include #include "osm_elements/Node.h" #include "osm_elements/Way.h" #include "osm_elements/Relation.h" #include "configuration/configuration.h" #include "utilities/prog_options.h" #include "database/table_management.h" namespace osm2pgr { /** * This class connects to a postgresql database. For using this class, * you also need to install postgis and pgrouting */ class Export2DB { public: typedef std::vector Nodes; typedef std::vector Ways; typedef std::vector Relations; /** * Constructor * @param vm variable map holding the configuration * @param db_conn conection string * */ explicit Export2DB(const po::variables_map &vm, const std::string &db_conn); /** * Destructor * closes the connection to the database */ ~Export2DB(); #if 1 //! connects to database int connect(); #endif bool has_extension(const std::string &name) const; #ifndef NDBEUG bool install_postGIS() const; #endif //! creates needed tables and geometries void createTables() const; /** @brief export values to osm_* table * * T must have: * T.values * * @param[in] items vector of values to be inserted into * @param[in] table */ template void export_osm ( std::vector &items, const std::string &table) const { auto osm_table = m_tables.get_table(table); std::vector values(items.size(), ""); size_t i(0); for (auto it = items.begin(); it != items.end(); ++it, ++i) { auto item = *it; values[i] = tab_separated(item.values(osm_table.columns(), true)); } export_osm(values, osm_table); } void export_configuration( const std::map& items) const; void exportWays( const Ways &ways, const Configuration &config) const; void dropTables() const; void createFKeys() const; void process_pois() const; bool exists(const std::string &table) const; private: void export_osm( const std::vector &values, const Table &table) const; void process_section(const std::string &ways_columns, pqxx::work &Xaction) const; void fill_vertices_table( const std::string &table, const std::string &vertices_tab, pqxx::work &Xaction) const; void fill_source_target( const std::string &table, const std::string &vertices_tab, pqxx::work &Xaction) const; int64_t get_val(const std::string sql) const; void execute(const std::string sql) const; Table configuration() const {return m_tables.configuration();} Table vertices() const {return m_tables.vertices();} Table ways() const {return m_tables.ways();} Table pois() const {return m_tables.pois();} Table osm_ways() const {return m_tables.osm_ways();} Table osm_nodes() const {return m_tables.osm_nodes();} Table osm_relations() const {return m_tables.osm_relations();} private: po::variables_map m_vm; std::string conninf; Tables m_tables; }; } // namespace osm2pgr #endif // SRC_EXPORT2DB_H_ osm2pgrouting-2.3.8/include/database/table_management.h000066400000000000000000000110721405641420700231720ustar00rootroot00000000000000/*PGR-GNU***************************************************************** Copyright (c) 2017 pgRouting developers Mail: project@pgrouting.org ------ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ********************************************************************PGR-GNU*/ /** @file **/ #pragma once #include #include "utilities/prog_options.h" namespace osm2pgr { class Table { public: Table() = default; Table(const Table &) = default; Table( const std::string &schema, const std::string &name, const std::string &full_name, const std::string &create_str, const std::string &other_columns, const std::string &geometry ); void set_columns(const std::vector &columns); /** @brief prefixNameSufix */ inline std::string table_name() const { return m_full_name; } /** @brief schema.prefixNameSufix * * schema.prefixNameSufix * OR * prefixNameSufix * */ std::string addSchema() const; std::string temp_name() const; std::string name() const {return m_name;}; std::string full_name() const {return m_full_name;}; /** sql queries */ std::string primary_key(const std::string &column) const; std::string unique(const std::string &column) const; std::string foreign_key( const std::string &column, const Table &table, const std::string &table_column) const; std::string gist_index() const; inline std::vector columns() const { return m_columns; } std::string sql(int i) const {return m_sql[i];} std::string tmp_create() const; std::string create() const; std::string drop() const; /* modifier */ void add_sql(const string& sql) { m_sql.push_back(sql); } private: std::string m_name; std::string m_schema; std::string m_full_name; std::string m_create; std::string m_other_columns; std::string m_constraint; std::string m_geometry; std::vector m_columns; /** aditional sqls (for pois) to keep code clean*/ std::vector m_sql; }; class Tables { public: Tables(const po::variables_map &vm); const Table& get_table(const std::string &name) const { if (name == "osm_nodes") return osm_nodes(); else if (name == "osm_ways") return osm_ways(); else if (name == "osm_relations") return osm_relations(); else if (name == "configuration") return configuration(); else if (name == "pointsofinterest") return pois(); else if (name == "ways") return ways(); else return vertices(); } std::string post_process(const Table &table) const; po::variables_map m_vm; private: /* * Conpulsory tables */ Table m_ways; Table m_ways_vertices_pgr; Table m_points_of_interest; Table m_configuration; /* * Optional tables */ Table m_osm_nodes; Table m_osm_ways; Table m_osm_relations; public: const Table& ways() const {return m_ways;} const Table& vertices() const {return m_ways_vertices_pgr;} const Table& pois() const {return m_points_of_interest;} const Table& configuration() const {return m_configuration;} const Table& osm_nodes() const {return m_osm_nodes;} const Table& osm_ways() const {return m_osm_ways;} const Table& osm_relations() const {return m_osm_relations;} private: Table osm_nodes_config() const; Table pois_config() const; Table osm_ways_config() const; Table osm_relations_config() const; Table configuration_config() const; Table ways_config() const; Table ways_vertices_pgr_config() const; }; } osm2pgrouting-2.3.8/include/osm_elements/000077500000000000000000000000001405641420700204635ustar00rootroot00000000000000osm2pgrouting-2.3.8/include/osm_elements/Node.h000066400000000000000000000072651405641420700215330ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2016 by pgRouting developers * * project@pgrouting.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License t &or more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef SRC_NODE_H_ #define SRC_NODE_H_ #pragma once #include #include #include #include #include "./osm_element.h" namespace osm2pgr { class Tag; /** @code @endcode */ class Node : public Element { public: Node() = default; Node(const Node&) = default; /** * @param atts attributes read py the parser */ explicit Node(const char **atts); ~Node() {}; inline std::string geom_str(const std::string separator) const { return get_attribute("lon") + separator + get_attribute("lat"); } inline std::string lat() {return get_attribute("lat");} inline std::string lon() {return get_attribute("lon");} void tag_config(const Tag &tag); std::string get_geometry() const { return std::string("srid=4326; POINT(") + geom_str(" ") + ")"; } inline std::string osm_id_str() { return boost::lexical_cast(m_osm_id); } double getLength(const Node &previous) const; inline uint16_t incrementUse() {return ++m_numsOfUse;} inline uint16_t numsOfUse() const {return m_numsOfUse;} inline void numsOfUse(uint16_t val) {m_numsOfUse = val;} private: /** * counts the rate, how much this node is used in different ways */ uint16_t m_numsOfUse; }; } // end namespace osm2pgr #endif // SRC_NODE_H_ osm2pgrouting-2.3.8/include/osm_elements/OSMDocument.h000066400000000000000000000114271405641420700227760ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2016 by pgRouting developers * * project@pgrouting.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License t &or more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef SRC_OSMDOCUMENT_H_ #define SRC_OSMDOCUMENT_H_ #include #include #include #include "utilities/utilities.h" #include "configuration/configuration.h" #include "utilities/prog_options.h" #include "database/Export2DB.h" namespace osm2pgr { class Node; class Way; class Relation; /** An osm-document. */ class OSMDocument { public: typedef std::vector Nodes; typedef std::vector Ways; typedef std::vector Relations; //! Constructor OSMDocument( const Configuration& config, const po::variables_map &vm, const Export2DB &db_conn, size_t lines); inline size_t lines() const {return m_lines;} //! Do the configuration has the @b tag ? inline bool config_has_tag(const Tag &tag) const { return m_rConfig.has_tag(tag); } inline double priority(const Tag &tag) const { return m_rConfig.priority(tag); } inline double maxspeed(const Tag &tag) const { return m_rConfig.maxspeed(tag); } const Nodes& nodes() const {return m_nodes;} const Ways& ways() const {return m_ways;} const Relations& relations() const {return m_relations;} void AddNode(const Node &n); void AddWay(const Way &w); void AddRelation(const Relation &r); void endOfFile(); //! find node by using an ID bool has_node(int64_t nodeRefId) const; Node* FindNode(int64_t nodeRefId); bool has_way(int64_t way_id) const; Way* FindWay(int64_t way_id); void add_node(Way &way, const char **atts); /** * add the configuration tag used for the speeds */ void add_config(Element *osm_element, const Tag &tag) const; inline uint16_t nodeErrs() const {return m_nodeErrs;} private: template bool do_export_osm(const T &container) { return m_vm.count("addnodes") && (container.size() % m_chunk_size) == 0; } void wait_child() const; template void osm_table_export(const T &osm_items, const std::string &table) const { if (osm_items.empty()) return; if (m_vm.count("addnodes")) { #if 0 auto pid = fork(); if (pid < 0) { std::cerr << "Failed to fork" << endl; exit(1); } if (pid > 0) return; #endif } auto residue = osm_items.size() % m_chunk_size; size_t start = residue? osm_items.size() - residue : osm_items.size() - m_chunk_size; auto export_items = T(osm_items.begin() + start, osm_items.end()); m_db_conn.export_osm(export_items, table); if (m_vm.count("addnodes")) { #if 0 /* * finish the child process */ _exit(0); #endif } } void export_pois() const; private: // ! parsed nodes TODO change to sorted vector Nodes m_nodes; //! parsed ways Ways m_ways; //! parsed relations Relations m_relations; bool m_relPending; bool m_waysPending; const Configuration& m_rConfig; po::variables_map m_vm; const Export2DB &m_db_conn; size_t m_chunk_size; uint16_t m_nodeErrs; size_t m_lines; }; } // end namespace osm2pgr #endif // SRC_OSMDOCUMENT_H_ osm2pgrouting-2.3.8/include/osm_elements/Relation.h000066400000000000000000000061331405641420700224140ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2016 by pgRouting developers * * project@pgrouting.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License t &or more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef SRC_RELATION_H_ #define SRC_RELATION_H_ #include #include #include #include "./osm_element.h" namespace osm2pgr { class Way; /** \code \endcode */ class Relation : public Element{ public: /** * @param atts attributes read py the parser */ explicit Relation(const char ** atts); Relation() = delete; ~Relation() {}; Relation(const Relation&) = default; std::vector way_refs() const {return m_WayRefs;} std::vector& way_refs() {return m_WayRefs;} std::string get_geometry() const {return std::string("");} /** * saves the nodes of the way * @param atts member attributes read py the parser */ int64_t add_member(const char **atts); std::string members_str() const; friend std::ostream& operator<<(std::ostream &os, const Relation &r); private: std::vector m_WayRefs; }; } // end namespace osm2pgr #endif // SRC_RELATION_H_ osm2pgrouting-2.3.8/include/osm_elements/Way.h000066400000000000000000000107151405641420700214000ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2016 by pgRouting developers * * project@pgrouting.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License t &or more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef SRC_WAY_H_ #define SRC_WAY_H_ #include #include #include #include #include "./osm_element.h" #include "./Node.h" namespace osm2pgr { /** \code \endcode */ class Way : public Element { public: Way() = default; ~Way() {}; /** * @param atts attributes read py the parser */ explicit Way(const char **atts); Tag add_tag(const Tag &tag); void add_node(Node* node); void add_node(int64_t node_id); std::vector& nodeRefs() {return m_NodeRefs;} const std::vector nodeRefs() const {return m_NodeRefs;} std::string members_str() const; public: inline void maxspeed_forward(double p_max) {m_maxspeed_forward = p_max;} inline void maxspeed_backward(double p_max) {m_maxspeed_backward = p_max;} inline std::string name() const {return has_tag("name")? get_tag("name") : "";} std::string oneWay() const; std::string oneWayType_str() const; inline bool is_oneway() const { return m_oneWay == "YES";} inline bool is_reversed() const { return m_oneWay == "REVERSED";} inline double maxspeed_forward() const {return m_maxspeed_forward;} inline double maxspeed_backward() const { return m_maxspeed_backward;} std::string get_geometry() const; std::string length_str() const; inline std::string maxspeed_forward_str() const { return boost::lexical_cast(m_maxspeed_forward); } inline std::string maxspeed_backward_str() const { return boost::lexical_cast(m_maxspeed_backward); } //! splits the way std::vector> split_me(); std::string geometry_str(const std::vector &) const; std::string length_str(const std::vector &) const; /** * to insert the relations tags */ void insert_tags(const std::map &tags); #ifndef NDEBUG friend std::ostream& operator<<(std::ostream &, const Way &); #endif private: bool is_number(const std::string& s) const; double get_kph(const std::string &value) const; void max_speed(const Tag& tag); void oneWay(const Tag& tag); void implied_oneWay(const Tag& tag); private: /** references to node that its on the file */ std::vector m_NodeRefs; /** node identifiers found as part of the way */ std::vector m_node_ids; double m_maxspeed_forward; double m_maxspeed_backward; std::string m_oneWay; }; } // end namespace osm2pgr #endif // SRC_WAY_H_ osm2pgrouting-2.3.8/include/osm_elements/osm_element.h000066400000000000000000000067321405641420700231530ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2016 by pgRouting developers * * project@pgrouting.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License t &or more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef SRC_OSM_ELEMENT_H_ #define SRC_OSM_ELEMENT_H_ #pragma once #include #include #include #include "./osm_tag.h" namespace osm2pgr { /** @brief osm elements osm elements can be: @code & attributes() {return m_attributes;} const std::map attributes() const { return m_attributes; } bool has_tag(const std::string&) const; std::string get_tag(const std::string&) const; bool has_tags() const {return !m_tags.empty();} std::map& tags() {return m_tags;} const std::map tags() const {return m_tags;} std::vector values( const std::vector &columns, bool is_hstore) const; virtual std::string members_str() const {return std::string();}; protected: // ! OSM ID of the element // or id of a configuraton int64_t m_osm_id; bool m_visible; Tag m_tag_config; std::map m_tags; std::map m_attributes; }; } // namespace osm2pgr #endif // SRC_OSM_ELEMENT_H_ osm2pgrouting-2.3.8/include/osm_elements/osm_tag.h000066400000000000000000000052111405641420700222640ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2016 by pgRouting developers * * project@pgrouting.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License t &or more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef SRC_OSM_TAG_H_ #define SRC_OSM_TAG_H_ #pragma once #include #include #include #include namespace osm2pgr { /** @code @endcode */ class Tag { public: Tag() = default; Tag(const Tag&) = default; /** * Constructor * @param atts attributes pointer returned by the XML parser */ explicit Tag(const char **atts); Tag(const std::string &k, const std::string &v) { m_key = k; m_value = v; } inline std::string key() const {return m_key;} inline std::string value() const {return m_value;} friend std::ostream& operator<<(std::ostream &os, const Tag& tag); private: // ! key std::string m_key; // ! value std::string m_value; }; } // end namespace osm2pgr #endif // SRC_OSM_TAG_H_ osm2pgrouting-2.3.8/include/parser/000077500000000000000000000000001405641420700172655ustar00rootroot00000000000000osm2pgrouting-2.3.8/include/parser/ConfigurationParserCallback.h000066400000000000000000000044721405641420700250460ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2016 by pgRouting developers * * project@pgrouting.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License t &or more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef SRC_CONFIGURATIONPARSERCALLBACK_H_ #define SRC_CONFIGURATIONPARSERCALLBACK_H_ #pragma once #ifdef BOOST_NO_CXX11_NULLPTR #define nullptr NULL #endif #include #include "XMLParser.h" namespace osm2pgr { class Configuration; class Tag_key; /** Parser callback for configuration files */ class ConfigurationParserCallback : public xml::XMLParserCallback { public: /** * Constructor */ explicit ConfigurationParserCallback(Configuration& doc) : m_config(doc), m_current(nullptr) { } private: //! reference to a Configuration object Configuration& m_config; //! current type, which will be parsed Tag_key* m_current; virtual void StartElement(const char *name, const char** atts); virtual void EndElement(const char* name); }; } // namespace osm2pgr #endif // SRC_CONFIGURATIONPARSERCALLBACK_H_ osm2pgrouting-2.3.8/include/parser/OSMDocumentParserCallback.h000066400000000000000000000052271405641420700243730ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2016 by pgRouting developers * * project@pgrouting.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License t &or more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef SRC_OSMDOCUMENTPARSERCALLBACK_H_ #define SRC_OSMDOCUMENTPARSERCALLBACK_H_ #pragma once #ifdef BOOST_NO_CXX11_NULLPTR #define nullptr NULL #endif #include #include "./XMLParser.h" namespace osm2pgr { class OSMDocument; class Node; class Way; class Relation; /** Parser callback for OSMDocument files */ class OSMDocumentParserCallback : public xml::XMLParserCallback { //! reference to a OSMDocument object OSMDocument& m_rDocument; //! current way, which will be parsed // Way* m_pActWay; Relation* m_pActRelation; virtual void StartElement(const char *name, const char** atts); virtual void EndElement(const char* name); public: /** * Constructor */ explicit OSMDocumentParserCallback(OSMDocument& doc) : m_rDocument(doc), m_pActRelation(0), last_node(nullptr), last_way(nullptr), last_relation(nullptr), m_line(0), m_section(1) { } private: void show_progress(); private: Node *last_node; Way *last_way; Relation* last_relation; size_t m_line; int m_section; }; // class OSMDocumentParserCallback } // end namespace osm2pgr #endif // SRC_OSMDOCUMENTPARSERCALLBACK_H_ osm2pgrouting-2.3.8/include/parser/XMLParser.h000066400000000000000000000055671405641420700212700ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2016 by pgRouting developers * * project@pgrouting.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License t &or more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef SRC_XMLPARSER_H_ #define SRC_XMLPARSER_H_ #include namespace xml { /** Callback to be used with XMLParser */ class XMLParserCallback { public: // ! Constructor_ XMLParserCallback() {} // ! Destructor virtual ~XMLParserCallback() {} /** Implement to construct an element with the given name, call back for parser event "start element" \param name [IN] element name \param atts [IN] the attributes */ virtual void StartElement(const char *name, const char** atts) = 0; /** Implement to process parser event "end element" */ virtual void EndElement(const char *elementName) = 0; }; /** XML-Parser based on expat library by James Clark http://www.jclark.com/xml/expat.html. Fast, event driven, non-validating parser Dependencies: - link with xmlparse.lib - uses xmlparse.dll */ class XMLParser { public: //! Constructor XMLParser() {} //! Destructor virtual ~XMLParser() {} /** Parse a file from the file system- \param rCallback [IN] the parser callback \param chFileName [IN] name of the file to be parsed \return 0: everything ok, 1: file not found, 2: parsing error */ int Parse(XMLParserCallback& rCallback, const char* chFileName); private: //! the expat parser object / imported from „expat.h“ XML_Parser m_ParserCtxt; }; } // end namespace xml #endif // SRC_XMLPARSER_H_ osm2pgrouting-2.3.8/include/utilities/000077500000000000000000000000001405641420700200045ustar00rootroot00000000000000osm2pgrouting-2.3.8/include/utilities/handle_pgpass.h000066400000000000000000000020651405641420700227700ustar00rootroot00000000000000/*PGR-GNU***************************************************************** Copyright (c) 2017 pgRouting developers Mail: project@pgrouting.org ------ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ********************************************************************PGR-GNU*/ /** @file **/ #include #include namespace po = boost::program_options; void handle_pgpass(po::variables_map &vm); osm2pgrouting-2.3.8/include/utilities/print_progress.h000066400000000000000000000043311405641420700232360ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2016 by pgRouting developers * * project@pgrouting.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License t &or more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef SRC_PRINT_PROGRESS_H_ #define SRC_PRINT_PROGRESS_H_ #pragma once #include #include template < typename T1 , typename T2> void print_progress(T1 wantProgress, T2 currentProgress) { int length = 50; double percent = static_cast (currentProgress) / static_cast (wantProgress); int fillerLength = static_cast(percent * length); std::string bar = "["; for (int i = 0; i < fillerLength; i++) { bar += "*"; } bar += "|"; for (int i = 0; i < length - fillerLength; i++) { bar += " "; } bar += "]"; std::cout << "\r" << bar << " (" << static_cast(100 * percent) << "%)" << " Total processed: " << currentProgress << std::flush; } #endif // SRC_PRINT_PROGRESS_H_ osm2pgrouting-2.3.8/include/utilities/prog_options.h000066400000000000000000000034041405641420700227000ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2016 by pgRouting developers * * project@pgrouting.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License t &or more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef SRC_PROG_OPTIONS_H_ #define SRC_PROG_OPTIONS_H_ #include #include namespace po = boost::program_options; using namespace std; void get_option_description(po::options_description &od_desc); void process_command_line(po::variables_map &vm); #endif // SRC_PROG_OPTIONS_H_ osm2pgrouting-2.3.8/include/utilities/utilities.h000066400000000000000000000021541405641420700221720ustar00rootroot00000000000000/*PGR-GNU***************************************************************** Copyright (c) 2017 pgRouting developers Mail: project@pgrouting.org ------ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ********************************************************************PGR-GNU*/ /** @file **/ #include #include std::string comma_separated(const std::vector &columns); std::string tab_separated(const std::vector &columns); osm2pgrouting-2.3.8/mapconfig.xml000066400000000000000000000040301405641420700170300ustar00rootroot00000000000000 osm2pgrouting-2.3.8/mapconfig_for_bicycles.xml000066400000000000000000000061041405641420700215570ustar00rootroot00000000000000 osm2pgrouting-2.3.8/mapconfig_for_cars.xml000066400000000000000000000026421405641420700207150ustar00rootroot00000000000000 osm2pgrouting-2.3.8/mapconfig_for_pedestrian.xml000066400000000000000000000023731405641420700221240ustar00rootroot00000000000000 osm2pgrouting-2.3.8/src/000077500000000000000000000000001405641420700151355ustar00rootroot00000000000000osm2pgrouting-2.3.8/src/configuration/000077500000000000000000000000001405641420700200045ustar00rootroot00000000000000osm2pgrouting-2.3.8/src/configuration/configuration.cpp000066400000000000000000000063341405641420700233650ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2016 by pgRouting developers * * project@pgrouting.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License t &or more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "configuration/configuration.h" #include #include #include namespace osm2pgr { void Configuration::add_tag_key(const Tag_key &t_key) { if (has_tag_key(t_key.name())) { std::cerr << "Duplicate Tag_key found in condfiguration file" << t_key.name() << " .... sikipping\n"; return; } m_Tag_keys[t_key.name()] = t_key; } bool Configuration::has_tag_key(const std::string &key) const { return m_Tag_keys.count(key) != 0; } bool Configuration::has_tag(const Tag &tag) const { return has_tag_key(tag.key()) && tag_key(tag).has_tag_value(tag); } const Tag_value& Configuration::tag_value(const Tag &tag) const { return tag_key(tag).tag_value(tag); } const Tag_key& Configuration::tag_key(const Tag &tag) const { return m_Tag_keys.at(tag.key()); } double Configuration::maxspeed(const Tag &tag) const { if (tag_key(tag).has(tag, "maxspeed")) return boost::lexical_cast(tag_key(tag).get(tag, "maxspeed")); return 50; } double Configuration::maxspeed_forward(const Tag &tag) const { if (tag_key(tag).has(tag, "maxspeed:backward")) return boost::lexical_cast(tag_key(tag).get(tag, "maxspeed:backward")); return maxspeed(tag); } double Configuration::maxspeed_backward(const Tag &tag) const { if (tag_key(tag).has(tag, "maxspeed:forward")) return boost::lexical_cast(tag_key(tag).get(tag, "maxspeed:backward")); return maxspeed(tag); } double Configuration::priority(const Tag &tag) const { if (tag_key(tag).has(tag, "priority")) return boost::lexical_cast(tag_key(tag).get(tag, "priority")); return 0; } } // end namespace osm2pgr osm2pgrouting-2.3.8/src/configuration/tag_key.cpp000066400000000000000000000057121405641420700221400ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2016 by pgRouting developers * * project@pgrouting.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License t &or more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "configuration/tag_key.h" #include "utilities/utilities.h" #include #include #include namespace osm2pgr { Tag_key::Tag_key(const char **atts) : Element(atts) { } void Tag_key::add_tag_value(const Tag_value &value) { m_Tag_values[value.name()] = value; } bool Tag_key::has_tag_value(const Tag &tag) const { return m_Tag_values.count(tag.value()); } const Tag_value& Tag_key::tag_value( const Tag &tag) const { return m_Tag_values.at(tag.value()); } bool Tag_key::has(const Tag &tag, const std::string &str) const { return tag_value(tag).has_attribute(str) || this->has_attribute(str); } std::string Tag_key::get(const Tag &tag, const std::string &str) const { assert(this->has(tag, str)); return (tag_value(tag).has_attribute(str)) ? tag_value(tag).get(str) : this->get_attribute(str); } std::vector Tag_key::values(const std::vector &columns) const { std::vector export_values; for (const auto &item : m_Tag_values) { auto row = item.second.values(columns, true); row[1] = name(); row[2] = item.second.get_attribute("name"); // row[3] has priority if (row[4] == "") row[4] = "40"; // max_speed if (row[5] == "") row[5] = row[4]; if (row[6] == "") row[6] = row[4]; if (row[7] == "") row[7] = "N"; export_values.push_back(tab_separated(row)); } return export_values; } } // end namespace osm2pgr osm2pgrouting-2.3.8/src/configuration/tag_value.cpp000066400000000000000000000042631405641420700224640ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2016 by pgRouting developers * * project@pgrouting.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License t &or more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "configuration/tag_value.h" #include #include #include namespace osm2pgr { Tag_value::Tag_value(const char **atts) : Element(atts){ assert(has_attribute("name")); } std::string Tag_value::get(const std::string &str) const { assert(has_attribute(str)); return get_attribute(str); } #if 0 std::string Tag_value::priority() const { assert(has_attribute("priority")); return get_attribute("priority"); } std::string Tag_value::maxspeed() const { assert(has_attribute("maxspeed")); return get_attribute("maxspeed"); } #endif std::string Tag_value::name() const { assert(has_attribute("name")); return get_attribute("name"); } } // end namespace osm2pgr osm2pgrouting-2.3.8/src/database/000077500000000000000000000000001405641420700167015ustar00rootroot00000000000000osm2pgrouting-2.3.8/src/database/Export2DB.cpp000066400000000000000000000602711405641420700211640ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2016 by pgRouting developers * * project@pgrouting.org * * * * This program IS free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License AS published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program IS distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License t &or more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "database/Export2DB.h" #include "database/table_management.h" #include #include #include #include #include #include "utilities/print_progress.h" #include "utilities/prog_options.h" #include "utilities/utilities.h" #include "boost/algorithm/string/replace.hpp" namespace osm2pgr { template static std::string TO_STR(const T &x) { return boost::lexical_cast(x); } Export2DB::Export2DB(const po::variables_map &vm, const std::string &connection) : m_vm(vm), conninf(connection), m_tables(vm) { } Export2DB::~Export2DB() { #if 0 PQfinish(mycon); #endif } int Export2DB::connect() { try { pqxx::connection db_conn(conninf); pqxx::work Xaction(db_conn); cout << "connection success"<< endl; return 0; } catch (const std::exception &e) { cerr << e.what() << std::endl; return 1; } } bool Export2DB::has_extension(const std::string &name) const { try { pqxx::connection db_conn(conninf); pqxx::work Xaction(db_conn); std::string sql = "SELECT * FROM pg_extension WHERE extname = '" + name + "'"; auto result = Xaction.exec(sql); return result.size() == 1; } catch (const std::exception &e) { cerr << e.what() << std::endl; return false; } } #ifndef NDEBUG bool Export2DB::install_postGIS() const { try { pqxx::connection db_conn(conninf); pqxx::work Xaction(db_conn); Xaction.exec("CREATE EXTENSION postgis"); Xaction.exec("CREATE EXTENSION hstore"); Xaction.commit(); return true; } catch (const std::exception &e) { // cerr << e.what() << std::endl; } return false; } #endif // ///////////////////// bool Export2DB::exists(const std::string &table) const { try { pqxx::connection db_conn(conninf); pqxx::work Xaction(db_conn); Xaction.exec(std::string("SELECT '") + table + "'::regclass"); std::cout << "TABLE: " << vertices().addSchema() << " already exists.\n"; return true; } catch (const std::exception &e) { return false; } } void Export2DB::createTables() const { try { pqxx::connection db_conn(conninf); pqxx::work Xaction(db_conn); if (!exists(vertices().addSchema())) { Xaction.exec(vertices().create()); std::cout << "TABLE: " << vertices().addSchema() << " created ... OK.\n"; } if (!exists(ways().addSchema())) { Xaction.exec(ways().create()); std::cout << "TABLE: " << ways().addSchema() << " created ... OK.\n"; } if (!exists(pois().addSchema())) { Xaction.exec(pois().create()); std::cout << "TABLE: " << pois().addSchema() << " created ... OK.\n"; } if (!exists(configuration().addSchema())) { Xaction.exec(configuration().create()); std::cout << "TABLE: " << configuration().addSchema() << " created ... OK.\n"; } Xaction.commit(); } catch (const std::exception &e) { std::cerr << "\n" << e.what() << std::endl; std::cerr << "FATAL ERROR: could not routing tables" << std::endl; exit(1); } if (m_vm.count("addnodes")) { try { pqxx::connection db_conn(conninf); pqxx::work Xaction(db_conn); /* * optional tables */ if (!exists(osm_nodes().addSchema())) { Xaction.exec(osm_nodes().create()); std::cout << "TABLE: " << osm_nodes().addSchema() << " created ... OK.\n"; } if (!exists(osm_ways().addSchema())) { Xaction.exec(osm_ways().create()); std::cout << "TABLE: " << osm_ways().addSchema() << " created ... OK.\n"; } if (!exists(osm_relations().addSchema())) { Xaction.exec(osm_relations().create()); std::cout << "TABLE: " << osm_relations().addSchema() << " created ... OK.\n"; } Xaction.commit(); } catch (const std::exception &e) { std::cerr << "\n" << e.what() << std::endl; std::cerr << "WARNING: could not create osm-* tables" << std::endl; std::cerr << " Insertions on osm_* tables are going to be ignored" << std::endl; } } } void Export2DB::dropTables() const { try { pqxx::connection db_conn(conninf); pqxx::work Xaction(db_conn); Xaction.exec(ways().drop()); std::cout << "TABLE: " << ways().addSchema() << " dropped ... OK.\n"; Xaction.exec(vertices().drop()); std::cout << "TABLE: " << vertices().addSchema() << " dropped ... OK.\n"; Xaction.exec(pois().drop()); std::cout << "TABLE: " << pois().addSchema() << " dropped ... OK.\n"; Xaction.exec(configuration().drop()); std::cout << "TABLE: " << configuration().addSchema() << " dropped ... OK.\n"; Xaction.commit(); } catch (const std::exception &e) { cerr << e.what() << std::endl; cerr << "ROLLBACK applied"; } try { pqxx::connection db_conn(conninf); pqxx::work Xaction(db_conn); Xaction.exec(osm_nodes().drop()); std::cout << "TABLE: " << osm_nodes().addSchema() << " dropped ... OK.\n"; Xaction.exec(osm_ways().drop()); std::cout << "TABLE: " << osm_ways().addSchema() << " dropped ... OK.\n"; Xaction.exec(osm_relations().drop()); std::cout << "TABLE: " << osm_relations().addSchema() << " dropped ... OK.\n"; Xaction.commit(); } catch (const std::exception &e) { cerr << e.what() << std::endl; } } void Export2DB::export_configuration(const std::map& items) const { auto osm_table = m_tables.get_table("configuration"); std::vector values; for (const auto &item : items) { auto row = item.second.values(osm_table.columns()); values.insert(values.end(), row.begin(), row.end()); } export_osm(values, osm_table); } void Export2DB::export_osm( const std::vector &values, const Table &table) const { if (values.empty()) return; auto columns = table.columns(); std::string temp_table(table.temp_name()); auto create_sql = table.tmp_create(); std::string copy_sql( "COPY " + temp_table + " (" + comma_separated(columns) + ") FROM STDIN"); #if 0 std::cout << "\n" << create_sql; std::cout << "\n" << copy_sql; #endif size_t count = 0; try { pqxx::connection db_con(conninf); pqxx::work Xaction(db_con); PGconn *mycon = PQconnectdb(conninf.c_str()); PGresult *res = PQexec(mycon, create_sql.c_str()); res = PQexec(mycon, copy_sql.c_str()); if (res) {}; for (auto it = values.begin(); it != values.end(); ++it) { auto str = *it; ++count; PQputline(mycon, str.c_str()); } PQputline(mycon, "\\.\n"); if (PQendcopy(mycon) != 0) { Xaction.exec("DROP TABLE " + temp_table); PQfinish(mycon); Xaction.commit(); if (values.size() < 2) { for (const auto &v : values) { std::cout << "\n*****ERROR HERE:\n" << v << "\n******"; } return; } size_t inc = values.size() / 2; export_osm(std::vector(values.begin(), values.begin() + inc), table); export_osm(std::vector(values.begin() + inc , values.end()), table); return; }; PQfinish(mycon); Xaction.exec(m_tables.post_process(table)); Xaction.exec("DROP TABLE " + temp_table); Xaction.commit(); } catch (const std::exception &e) { std::cerr << "\n" << e.what() << std::endl; std::cerr << "While exporting to " << table.addSchema() << " TODO insert one by one skip the guilty one\n"; } } /*! */ void Export2DB::fill_vertices_table( const std::string &table, const std::string &vertices_tab, pqxx::work &Xaction) const { // std::cout << "Filling '" << vertices_tab << "' based on '" << table <<"'\n"; std::string sql( "WITH osm_vertex AS (" "(SELECT source_osm AS osm_id, x1 AS lon, y1 AS lat FROM " + table + " where source IS NULL)" " union " "(SELECT target_osm AS osm_id, x2 AS lon, y2 AS lat FROM " + table + " where target IS NULL)" ") , " " data1 AS (SELECT osm_id, lon, lat FROM (SELECT DISTINCT * FROM osm_vertex) a " ") " " INSERT INTO " + vertices_tab + " (osm_id, lon, lat, the_geom) (SELECT data1.*, ST_SetSRID(ST_Point(lon, lat), 4326) FROM data1)"); auto result = Xaction.exec(sql); std::cout << "\t Vertices inserted: " << result.affected_rows(); } void Export2DB::fill_source_target( const std::string &table, const std::string &vertices_tab, pqxx::work &Xaction) const { // std::cout << " Filling 'source' column of '" << table << "':'" << vertices_tab << "'\n"; std::string sql1( " UPDATE " + table + " AS w" " SET source = v.id " " FROM " + vertices_tab + " AS v" " WHERE w.source IS NULL and w.source_osm = v.osm_id;"); Xaction.exec(sql1); std::string sql2( " UPDATE " + table + " AS w" " SET target = v.id " " FROM " + vertices_tab + " AS v" " WHERE w.target IS NULL and w.target_osm = v.osm_id;"); Xaction.exec(sql2); std::string sql3( " UPDATE " + table + " SET length_m = ST_length(geography(ST_Transform(the_geom, 4326)))," " cost_s = CASE " " WHEN one_way = -1 THEN -ST_length(geography(ST_Transform(the_geom, 4326))) / (maxspeed_forward::float * 5.0 / 18.0)" " ELSE ST_length(geography(ST_Transform(the_geom, 4326))) / (maxspeed_backward::float * 5.0 / 18.0)" " END, " " reverse_cost_s = CASE " " WHEN one_way = 1 THEN -ST_length(geography(ST_Transform(the_geom, 4326))) / (maxspeed_backward::float * 5.0 / 18.0)" " ELSE ST_length(geography(ST_Transform(the_geom, 4326))) / (maxspeed_backward::float * 5.0 / 18.0)" " END " " WHERE length_m IS NULL AND maxspeed_backward !=0 AND maxspeed_forward != 0;"); Xaction.exec(sql3); } void Export2DB::exportWays(const Ways &ways, const Configuration &config) const { std::cout << " Processing " << ways.size() << " ways" << ":\n"; Table table = this->ways(); auto columns = table.columns(); auto ways_columns = comma_separated(columns); size_t chunck_size = m_vm["chunk"].as(); auto create_sql = table.tmp_create(); auto temp_table(table.temp_name()); std::string copy_sql( "COPY " + temp_table + " (" + comma_separated(columns) + ") FROM STDIN"); int64_t split_count = 0; int64_t count = 0; size_t start = 0; auto it = ways.begin(); while (start < ways.size()) { auto limit = (start + chunck_size) < ways.size() ? start + chunck_size : ways.size(); try { pqxx::connection db_con(conninf); pqxx::work Xaction(db_con); PGconn *mycon = PQconnectdb(conninf.c_str()); PGresult *res = PQexec(mycon, create_sql.c_str()); res = PQexec(mycon, copy_sql.c_str()); if (res) {}; for (auto i = start; i < limit; ++i) { auto way = *it; ++count; ++it; if (way.tag_config().key() == "" || way.tag_config().value() == "") continue; std::vector common_values; common_values.push_back(TO_STR(config.tag_value(way.tag_config()).id())); common_values.push_back(TO_STR(way.osm_id())); common_values.push_back(way.maxspeed_forward_str() == "-1" ? TO_STR(config.maxspeed_forward(way.tag_config())) : way.maxspeed_forward_str()) ; common_values.push_back(way.maxspeed_backward_str() == "-1" ? TO_STR(config.maxspeed_backward(way.tag_config())) : way.maxspeed_backward_str()) ; common_values.push_back(way.oneWayType_str()); common_values.push_back(way.oneWay()); // common_values.push_back(way.has_attribute("oneway") ? way.get_attribute("oneway") : std::string("")); common_values.push_back(TO_STR(config.priority(way.tag_config()))); auto splits = way.split_me(); split_count += splits.size(); for (size_t j = 0; j < splits.size(); ++j) { auto length = way.length_str(splits[j]); auto values = common_values; values.push_back(length); values.push_back(splits[j].front()->lon()); values.push_back(splits[j].front()->lat()); values.push_back(splits[j].back()->lon()); values.push_back(splits[j].back()->lat()); values.push_back(TO_STR(splits[j].front()->osm_id())); values.push_back(TO_STR(splits[j].back()->osm_id())); values.push_back(way.geometry_str(splits[j])); // cost based on oneway if (way.is_reversed()) values.push_back(std::string("-") + length); else values.push_back(length); // reverse_cost if (way.is_oneway()) values.push_back(std::string("-") + length); else values.push_back(length); values.push_back(way.name()); PQputline(mycon, tab_separated(values).c_str()); } } PQputline(mycon, "\\.\n"); PQendcopy(mycon); print_progress(ways.size(), count); process_section(ways_columns, Xaction); Xaction.exec("DROP TABLE " + temp_table); Xaction.commit(); } catch (const std::exception &e) { std::cerr << "\n" << e.what() << std::endl; std::cerr << "While processing FROM " << start << "th \t to: " << limit << "th way\n"; std::cerr << "count" << count << " While processing FROM " << start << "th \t to: " << limit << "th way\n"; } start = limit; } } void Export2DB::process_section(const std::string &ways_columns, pqxx::work &Xaction) const { // std::cout << "Creating indices in temporary table\n"; auto temp_table(ways().temp_name()); Xaction.exec("CREATE INDEX "+ temp_table + "_gdx ON "+ temp_table + " using gist(the_geom);"); Xaction.exec("CREATE INDEX ON "+ temp_table + " USING btree (source_osm)"); Xaction.exec("CREATE INDEX ON "+ temp_table + " USING btree (target_osm)"); // std::cout << "Deleting duplicated ways FROM temporary table\n"; std::string delete_from_temp( " DELETE FROM "+ temp_table + " a " " USING " + ways().addSchema() + " b " " WHERE a.the_geom ~= b.the_geom AND ST_OrderingEquals(a.the_geom, b.the_geom);"); Xaction.exec(delete_from_temp); // std::cout << "Updating to existing toplology the temporary table\n"; fill_source_target(temp_table, vertices().addSchema(), Xaction); // std::cout << "Inserting new vertices in the vertex table\n"; fill_vertices_table(temp_table, vertices().addSchema(), Xaction); // std::cout << "Updating to new toplology the temporary table\n"; fill_source_target(temp_table, vertices().addSchema(), Xaction); // std::cout << "Inserting new split ways to '" << addSchema(full_table_name("ways")) << "'\n"; std::string insert_into_ways( " INSERT INTO " + ways().addSchema() + "(" + ways_columns + ", source, target, length_m, cost_s, reverse_cost_s) " " (SELECT " + ways_columns + ", source, target, length_m, cost_s, reverse_cost_s FROM " + temp_table + "); "); auto result = Xaction.exec(insert_into_ways); std::cout << "\tSplit ways inserted " << result.affected_rows() << "\n"; } int64_t Export2DB::get_val(const std::string sql) const { #if 0 std::cout << "\nExecuting: \n" << sql << "\n"; #endif try { pqxx::connection db_conn(conninf); pqxx::work Xaction(db_conn); auto result = Xaction.exec(sql); Xaction.commit(); if (result.size() == 0) return 0; return result[0][0].as(); } catch (const std::exception &e) { std::cout << "\nWARNING: " << e.what() << std::endl; std::cout << sql << "\n"; } return 0; } void Export2DB::execute(const std::string sql) const { #if 0 std::cout << "\nExecuting: \n" << sql << "\n"; #endif try { pqxx::connection db_conn(conninf); pqxx::work Xaction(db_conn); Xaction.exec(sql); Xaction.commit(); } catch (const std::exception &e) { std::cout << "\nWARNING: " << e.what() << std::endl; std::cout << sql << "\n"; } } /* * * Integrity of the OSM data IS not ensured so failings are ignored * * Due to the fact that indexes slow down the process, no index IS created * * After all the data IS inserted then its time to create indices & foreign keys * */ void Export2DB::createFKeys() const { /* * configuration: */ execute(configuration().primary_key("id")); execute(configuration().unique("tag_id")); /* * vertices */ execute(vertices().primary_key("id")); execute(vertices().unique("osm_id")); execute(vertices().gist_index()); /* * Ways */ execute(ways().primary_key("gid")); execute(ways().foreign_key("source", vertices(), "id")); execute(ways().foreign_key("target", vertices(), "id")); execute(ways().foreign_key("source_osm", vertices(), "osm_id")); execute(ways().foreign_key("target_osm", vertices(), "osm_id")); execute(ways().foreign_key("tag_id", configuration(), "tag_id")); execute(ways().gist_index()); /* * ponitsOfInterest */ execute(pois().primary_key("pid")); execute(pois().gist_index()); execute(pois().unique("osm_id")); } void Export2DB::process_pois() const { if (!m_vm.count("addnodes")) return; std::cout << "\nAdding functions for processing Points of Interest ..." << endl; /* osm2pgr_pois_update_part_of_topology */ execute(pois().sql(0)); /* osm2pgr_pois_update_not_part_of_topology */ execute(pois().sql(1)); /* osm2pgr_pois_find_side */ execute(pois().sql(2)); /* osm2pgr_pois_new_geom */ execute(pois().sql(3)); /* osm2pgr_pois_update */ execute(pois().sql(4)); std::cout << "\nTo process pointsOfInterest table:\n" #if 0 //TODO << m_schema << (m_schema == "" ? "" : ".") #endif << "osm2pgr_pois_update(radius default 200, within default 50)\n" "\n - Using areas of (radius)mts on POIS" "\n - Using edges that are at least (within) mts of each POI" "\nPOIS that do not have a closest edge is considered as too far\n"; return; #if 0 std::string array; int64_t total = 0; auto limit = get_val( "SELECT count(*) FROM " + pois().addSchema() + "\n WHERE vertex_id IS NULL AND edge_id IS NULL"); std::cout << "\nFinding closest edge to " << limit << " Points Of Interest\n"; for (int64_t i = 0; i < limit; ++i) { auto curr_tot = get_val( "SELECT osm2pgr_pois_update_not_part_of_topology(200, 50, ARRAY[" + array + "]::BIGINT[])"); total += curr_tot; if (curr_tot == 0) { auto pid_outOfRange = get_val(" SELECT pid FROM " + pois().addSchema() +"\n WHERE vertex_id IS NULL AND edge_id IS NULL" +"\n AND pid not in (SELECT unnest(ARRAY[" + array +"]::BIGINT[]))" +"\n limit 1;"); if (pid_outOfRange == 0) break; if (array.empty()) { array += boost::lexical_cast(pid_outOfRange); } else { array += "," + boost::lexical_cast(pid_outOfRange); } if (get_val( +"SELECT count(*) FROM (SELECT * FROM " + pois().addSchema() +"\n WHERE vertex_id IS NULL AND edge_id IS NULL" +"\n AND pid not in (SELECT unnest(ARRAY[" + array +"]::BIGINT[]))) AS a" ) == 0) break; } print_progress(limit, total); } if (!array.empty()) { std::cout << "\nNo edge found within distance (200 + 50)mts on pid(s): " << array << "\n"; } execute("SELECT osm2pgr_pois_find_side()"); execute("SELECT osm2pgr_pois_new_geom()"); execute( "\n WITH " "\n base AS (" "\n SELECT pid, w.id AS wid, w.the_geom AS wgeom, p.the_geom AS pgeom" "\n FROM " + pois().addSchema() + " AS p JOIN " + ways().addSchema() + " AS w ON (edge_id = w.id)" + "\n WHERE edge_id IS not NULL" + "\n )," + "\n foo AS (" + "\n SELECT wid, ST_dumppoints(wgeom) AS dp" + "\n FROM base" + "\n )," + "\n blade AS (" + "\n SELECT wid, ST_collect((dp).geom) AS blade" + "\n FROM foo" + "\n GROUP BY wid" + "\n )," + "\n split AS (" + "\n SELECT base.*, (ST_Dump(ST_split(wgeom, blade))).geom AS line" + "\n FROM blade JOIN base" + "\n USING (wid)" + "\n )," + "\n distance AS (" + "\n SELECT split.*, ST_distance(line, pgeom) AS dist FROM split" + "\n )," + "\n second AS (" + "\n SELECT pid, min(dist) FROM distance GROUP BY pid" + "\n ), " + "\n last AS (" + "\n SELECT pid," + "\n (ST_y(ST_startpoint(line)) - ST_y(ST_endpoint(line))) * ST_x(pgeom)" + "\n + (ST_x(ST_endpoint(line)) - ST_x(ST_startpoint(line))) * ST_y(pgeom)" + "\n + (ST_x(ST_startpoint(line)) * ST_y(ST_endpoint(line)) - ST_x(ST_endpoint(line))" + "\n * ST_y(ST_startpoint(line))) AS val" + "\n FROM distance join second using (pid) where dist = min" + "\n )" + "\n UPDATE " + pois().addSchema() + " set side = case when val>0 then 'L' when val<0 then 'R' else 'B' end " + "\n FROM last " + "\n WHERE last.pid = " + pois().addSchema() + ".pid;" ); #endif } } // namespace osm2pgr osm2pgrouting-2.3.8/src/database/configuration_config.cpp000066400000000000000000000043601405641420700236040ustar00rootroot00000000000000/*PGR-GNU***************************************************************** Copyright (c) 2017 pgRouting developers Mail: project@pgrouting.org ------ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ********************************************************************PGR-GNU*/ #include "boost/lexical_cast.hpp" #include "database/table_management.h" #include "utilities/utilities.h" namespace osm2pgr { /* * configuring TABLE configuration */ Table Tables::configuration_config() const { Table table( /* name */ "configuration", /* schema */ m_vm["schema"].as(), /* full name */ "configuration", /* standard column creation string */ std::string( " id serial" ", tag_id INTEGER" ", tag_key TEXT" ", tag_value TEXT" ", priority double precision" ", maxspeed double precision" ", maxspeed_forward double precision" ", maxspeed_backward double precision" ", force char"), /* other columns */ "", /* geometry */ ""); std::vector columns; columns.push_back("tag_id"); columns.push_back("tag_key"); columns.push_back("tag_value"); columns.push_back("priority"); columns.push_back("maxspeed"); columns.push_back("maxspeed_forward"); columns.push_back("maxspeed_backward"); columns.push_back("force"); table.set_columns(columns); return table; } } //namespace osm2pgr osm2pgrouting-2.3.8/src/database/osm_nodes_config.cpp000066400000000000000000000051041405641420700227200ustar00rootroot00000000000000/*PGR-GNU***************************************************************** Copyright (c) 2017 pgRouting developers Mail: project@pgrouting.org ------ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ********************************************************************PGR-GNU*/ #include "boost/lexical_cast.hpp" #include "database/table_management.h" #include "utilities/utilities.h" namespace osm2pgr { /* * configuring TABLE osm_nodes */ Table Tables::osm_nodes_config() const { Table table( /* name */ std::string("osm_nodes"), /* schema */ m_vm["schema"].as(), /* full name */ "osm_nodes", /* standard column creation string */ std::string( " osm_id bigint PRIMARY KEY" + (m_vm.count("attributes") && m_vm.count("addnodes") ? (std::string(", attributes hstore")) : "") + (m_vm.count("tags") && m_vm.count("addnodes") ? (std::string(", tags hstore")) #if 0 (std::string(", tags ") + (m_vm.count("hstore") ? "hstore" : "json")) #endif : "")), /* other columns */ // TODO get from the configuration maybe this task is to be done on the configuration*/ ", tag_name TEXT" ", tag_value TEXT" ", name TEXT ", // end todo /* geometry */ "POINT"); std::vector columns; columns.push_back("osm_id"); columns.push_back("the_geom"); // TODO get from the configuration columns.push_back("tag_name"); columns.push_back("tag_value"); columns.push_back("name"); // end todo if (m_vm.count("attributes")) columns.push_back("attributes"); if (m_vm.count("tags")) columns.push_back("tags"); table.set_columns(columns); return table; } } //namespace osm2pgr osm2pgrouting-2.3.8/src/database/osm_relations_config.cpp000066400000000000000000000050211405641420700236060ustar00rootroot00000000000000/*PGR-GNU***************************************************************** Copyright (c) 2017 pgRouting developers Mail: project@pgrouting.org ------ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ********************************************************************PGR-GNU*/ #include "boost/lexical_cast.hpp" #include "database/table_management.h" #include "utilities/utilities.h" namespace osm2pgr { /* * configuring TABLE osm_relations */ Table Tables::osm_relations_config() const { Table table( /* name */ "osm_relations", /* schema */ m_vm["schema"].as(), /* full name */ "osm_relations", /* standard column creation string */ std::string( " osm_id bigint PRIMARY KEY" " , members hstore" + (m_vm.count("attributes") && m_vm.count("addnodes") ? (std::string(", attributes hstore")) : "") + (m_vm.count("tags") && m_vm.count("addnodes")? (std::string(", tags hstore")) : "") ), /* other columns */ // TODO get from the configuration maybe this task is to be done on the configuration*/ ", tag_name TEXT" ", tag_value TEXT" ", name TEXT ", // end todo /* geometry */ ""); std::vector columns; columns.push_back("osm_id"); columns.push_back("members"); // TODO get from the configuration columns.push_back("tag_name"); columns.push_back("tag_value"); columns.push_back("name"); // end todo if (m_vm.count("attributes")) columns.push_back("attributes"); if (m_vm.count("tags")) columns.push_back("tags"); table.set_columns(columns); return table; } } //namespace osm2pgr osm2pgrouting-2.3.8/src/database/osm_ways_config.cpp000066400000000000000000000052331405641420700225760ustar00rootroot00000000000000/*PGR-GNU***************************************************************** Copyright (c) 2017 pgRouting developers Mail: project@pgrouting.org ------ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ********************************************************************PGR-GNU*/ #include "database/table_management.h" #include "utilities/utilities.h" #include namespace osm2pgr { /* * configuring TABLE osm_ways */ Table Tables::osm_ways_config() const { Table table( /* name */ "osm_ways", /* schema */ m_vm["schema"].as(), /* full name */ "osm_ways", /* standard column creation string */ std::string( " osm_id bigint PRIMARY KEY" " , members hstore" + (m_vm.count("attributes") && m_vm.count("addnodes") ? (std::string(", attributes hstore")) : "") + (m_vm.count("tags") && m_vm.count("addnodes") ? (std::string(", tags hstore")) #if 0 (std::string(", tags ") + (m_vm.count("hstore") ? "hstore" : "json")) #endif : "")), /* other columns */ /* TODO get from the configuration maybe this task is to be done on the configuration*/ std::string( ", tag_name TEXT" ", tag_value TEXT" ", name TEXT "), // end todo /* geometry */ "LINESTRING"); std::vector columns; columns.push_back("osm_id"); columns.push_back("members"); // TODO get from the configuration columns.push_back("tag_name"); columns.push_back("tag_value"); columns.push_back("name"); // end todo if (m_vm.count("attributes")) columns.push_back("attributes"); if (m_vm.count("tags")) columns.push_back("tags"); columns.push_back("the_geom"); table.set_columns(columns); return table; } } //namespace osm2pgr osm2pgrouting-2.3.8/src/database/pois_config.cpp000066400000000000000000000052771405641420700217170ustar00rootroot00000000000000/*PGR-GNU***************************************************************** Copyright (c) 2017 pgRouting developers Mail: project@pgrouting.org ------ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ********************************************************************PGR-GNU*/ #include "boost/lexical_cast.hpp" #include "database/table_management.h" #include "utilities/utilities.h" namespace osm2pgr { /* * configuring TABLE osm_nodes */ Table Tables::pois_config() const { Table table( /* name */ std::string("pointsofinterest"), /* schema */ m_vm["schema"].as(), /* full name */ std::string( m_vm["prefix"].as() + "pointsofinterest" + m_vm["suffix"].as()), /* standard column creation string */ std::string( " pid bigserial" ", osm_id bigint" ", vertex_id bigint" ", edge_id bigint" ", side CHAR" ", fraction FLOAT" ", length_m FLOAT" + (m_vm.count("attributes") ? (std::string(", attributes hstore")) : "") + (m_vm.count("tags") ? (std::string(", tags hstore")) : "")), /* other columns */ std::string( ", tag_name TEXT" ", tag_value TEXT" ", name TEXT "), // end todo /* geometry */ "POINT"); std::vector columns; columns.push_back("osm_id"); columns.push_back("the_geom"); // TODO get from the configuration columns.push_back("tag_name"); columns.push_back("tag_value"); columns.push_back("name"); // end todo if (m_vm.count("attributes")) columns.push_back("attributes"); if (m_vm.count("tags")) columns.push_back("tags"); table.set_columns(columns); return table; } } //namespace osm2pgr osm2pgrouting-2.3.8/src/database/table_management.cpp000066400000000000000000000427411405641420700227000ustar00rootroot00000000000000/*PGR-GNU***************************************************************** Copyright (c) 2017 pgRouting developers Mail: project@pgrouting.org ------ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ********************************************************************PGR-GNU*/ #include #include "boost/lexical_cast.hpp" #include "database/table_management.h" #include "utilities/utilities.h" namespace osm2pgr { Table::Table( const std::string &name, const std::string &schema, const std::string &full_name, const std::string &create_str, const std::string &other_columns, const std::string &geometry ): m_name(name), m_schema(schema), m_full_name(full_name), m_create(create_str), m_other_columns(other_columns), m_geometry(geometry) { } std::string Table::gist_index() const { return "CREATE INDEX ON " + addSchema() + "\n USING GIST (the_geom);"; } std::string Table::foreign_key( const std::string &column, const Table &table, const std::string &table_column) const { return "ALTER TABLE " + addSchema() + "\n ADD FOREIGN KEY (" + column + ")" + "\n REFERENCES " + table.addSchema() + "(" + table_column + ")" + "\n ON UPDATE NO ACTION \n ON DELETE NO ACTION;"; } std::string Table::unique(const std::string &column) const { return std::string("ALTER TABLE " + addSchema() + "\n ADD UNIQUE (" + column + ")"); } std::string Table::primary_key(const std::string &column) const { return std::string("ALTER TABLE " + addSchema() + "\n ADD PRIMARY KEY (" + column + ")"); } void Table::set_columns(const std::vector &columns) { m_columns = columns; } std::string Table::addSchema() const { return m_schema + (m_schema == "" ? "" : ".") + m_full_name; } /* * sql strings */ std::string Table::create() const { std::string sql = "CREATE TABLE " + addSchema() + " (" + m_create + m_other_columns + m_constraint + ")"; sql += " WITH (autovacuum_enabled = false);"; if (m_geometry != "") { sql += "SELECT AddGeometryColumn('" + m_schema + (m_schema == "" ? "" : "', '") + table_name() + "', 'the_geom', 4326, '" + m_geometry + "', 2);"; if (name() == "pointsofinterest") { sql += "SELECT AddGeometryColumn('" + m_schema + (m_schema == "" ? "" : "', '") + table_name() + "', 'new_geom', 4326, '" + m_geometry + "', 2);"; } } return sql; } std::string Table::drop() const { return "DROP TABLE IF EXISTS " + addSchema() + " CASCADE;"; } std::string Table::temp_name() const { return "__" + table_name() + boost::lexical_cast(getpid()); } std::string Table::tmp_create() const { std::string sql = "CREATE UNLOGGED TABLE " + temp_name() + " (" + m_create + m_other_columns + ");"; if (m_geometry != "") { sql += "SELECT AddGeometryColumn('" + temp_name() + "', 'the_geom', 4326, '" + m_geometry + "', 2);"; } return sql; } std::string Tables::post_process(const Table &table) const { if (table.name() == "osm_nodes" || table.name() == "pointsofinterest" || table.name() == "osm_ways" || table.name() == "osm_relations") { std::string str( " WITH data AS (" " SELECT a.* " " FROM " + table.temp_name() + " a LEFT JOIN " + table.addSchema() + " b USING (osm_id) WHERE (b.osm_id IS NULL))" + " INSERT INTO " + table.addSchema() + "(" + comma_separated(table.columns()) + ") " + " (SELECT " + comma_separated(table.columns()) + " FROM data); "); return str; } else if (table.name() == "configuration") { std::string str( " WITH data AS (" " SELECT a.* " " FROM " + configuration().temp_name() + " a LEFT JOIN " + configuration().addSchema() + " b USING (tag_id) WHERE (b.tag_id IS NULL))" + " INSERT INTO " + configuration().addSchema() + "(" + comma_separated(configuration().columns()) + ") " + " (SELECT " + comma_separated(configuration().columns()) + " FROM data); "); return str; } return ""; } Tables::Tables(const po::variables_map &vm) : m_vm(vm), /* * initializing tables */ m_ways(ways_config()), m_ways_vertices_pgr(ways_vertices_pgr_config()), m_points_of_interest(pois_config()), m_configuration(configuration_config()), m_osm_nodes(osm_nodes_config()), m_osm_ways(osm_ways_config()), m_osm_relations(osm_relations_config()) { auto m_schema(vm["schema"].as()); m_schema += (m_schema == "" ? "" : "."); m_points_of_interest.add_sql( "\nCREATE OR REPLACE FUNCTION " + m_schema + "osm2pgr_pois_update_part_of_topology()" "\nRETURNS BIGINT AS" "\n$$" "\n-----------------------------------------------------------------" "\n-- When the point of interest is part of the routing topology" "\n-- Sets the vid value to the corresponding id of the vertices table" "\n-- The distance from the routing topólogy to the point of Interest is 0" "\n-----------------------------------------------------------------\n" "\nDECLARE" "\n curr_tot BIGINT;" "\nBEGIN" "\n UPDATE " + pois().addSchema() + " AS pois" + "\n SET (vertex_id, length_m) = (vertices.id, 0)" + "\n FROM " + vertices().addSchema() + " AS vertices" + "\n WHERE vertex_id IS NULL AND pois.osm_id = vertices.osm_id;" + "\n GET DIAGNOSTICS curr_tot = ROW_COUNT;" + "\n RETURN curr_tot;" + "\nEND;" + "\n$$" + "\nLANGUAGE plpgsql;" + "\nCOMMENT ON FUNCTION " + m_schema + "osm2pgr_pois_update_part_of_topology()" + "\n IS 'osm2pgrouting generated function';" ); m_points_of_interest.add_sql( "CREATE OR REPLACE FUNCTION " + m_schema +"osm2pgr_pois_update_not_part_of_topology(radius FLOAT, within FLOAT, tooFar BIGINT[])" "\n RETURNS BIGINT AS" "\n $$" "\n-----------------------------------------------------------------" "\n-- When A point of interest is NOT part of the routing topology" "\n-- Looks for a POI that has not being assigned a vid or an edge_id" "\n-- - grabs all the POIS that are within the (radius) mts distance of the POI" "\n-- - grabs all the edges that are within the (radius + within)mts of the POI" "\n-- - FOR each POI in POIS" "\n-- - For closest edge to the POI:" "\n-- - if the closest point in the edge to the POI is the an ending vertex:" "\n-- - Sets the vid value to the corresponding id of the vertices table" "\n-- - if the closest point in the edge to the POI is NOT an ending vertex:" "\n-- - Sets the edge_id and the fraction values" "\n-----------------------------------------------------------------" "\n DECLARE" "\n curr_tot BIGINT;" "\n BEGIN" "\n WITH " "\n poi AS (" "\n SELECT ST_buffer(the_geom::geography, $1)::geometry AS bufferPois," "\n ST_buffer(the_geom::geography, $1 + $2)::geometry AS bufferWays" "\n FROM " + pois().addSchema() +"\n WHERE vertex_id IS NULL AND edge_id IS NULL" +"\n AND pid not in (SELECT unnest(tooFar))" +"\n limit 1" +"\n )," +"\n pois AS (" +"\n SELECT * FROM " + pois().addSchema() + ", poi" +"\n WHERE ST_Within(the_geom, bufferPois) " +"\n AND vertex_id IS NULL AND edge_id IS NULL" +"\n AND pid not in (SELECT unnest(tooFar))" +"\n )," +"\n wayss AS (" +"\n SELECT * FROM " + ways().addSchema() + ", poi" +"\n WHERE ST_Intersects(the_geom, bufferWays)" +"\n )," +"\n first AS (" +"\n SELECT ways.gid AS wid," +"\n source_osm, target_osm," +"\n ST_distance(pois.the_geom::geography, ways.the_geom::geography) AS dist," +"\n pois.osm_id AS vid," +"\n ST_linelocatepoint(ways.the_geom, pois.the_geom) AS fraction" +"\n FROM wayss AS ways , pois" +"\n WHERE pois.vertex_id IS NULL AND pois.edge_id IS NULL" +"\n )," +"\n second AS (" +"\n SELECT vid, min(dist) FROM first group by vid" +"\n )," +"\n third AS (" +"\n SELECT first.vid, NULL::bigint AS wid, NULL::FLOAT AS fraction, first.dist, source_osm AS v_osm_id FROM first, second WHERE dist = min AND fraction in (0)" +"\n UNION " +"\n SELECT first.vid, NULL::bigint AS wid, NULL::FLOAT AS fraction, first.dist, target_osm AS v_osm_id FROM first, second WHERE dist = min AND fraction in (1)" +"\n )," +"\n last AS (" +"\n SELECT third.*, b.id FROM third join " + vertices().addSchema() + " AS b ON (third.v_osm_id = b.osm_id)" +"\n UNION" +"\n SELECT first.vid, first.wid, first.fraction, first.dist, NULL AS v_osm_id, NULL::bigint AS id FROM first, second WHERE dist = min AND fraction not in (0, 1)" +"\n )" +"\n UPDATE " + pois().addSchema() +" AS pois SET (vertex_id, edge_id, fraction, length_m) = (last.id, last.wid, last.fraction, last.dist)" +"\n FROM last WHERE pois.osm_id = last.vid;" +"\n GET DIAGNOSTICS curr_tot = ROW_COUNT;" +"\n return curr_tot;" +"\n END;" +"\n $$" +"\n LANGUAGE plpgsql;" +"\nCOMMENT ON FUNCTION " + m_schema + "osm2pgr_pois_update_not_part_of_topology(float,float,bigint[])" + "\n IS 'osm2pgrouting generated function';" ); m_points_of_interest.add_sql( "CREATE OR REPLACE FUNCTION " + m_schema +"osm2pgr_pois_find_side()" "\n RETURNS VOID AS" "\n $$" "\n WITH " "\n base AS (" "\n SELECT pid, w.gid AS wid, w.the_geom AS wgeom, p.the_geom AS pgeom" "\n FROM " + pois().addSchema() + " AS p JOIN " + ways().addSchema() + " AS w ON (edge_id = w.gid)" + "\n WHERE edge_id IS NOT NULL AND side IS NULL" + "\n )," + "\n foo AS (" + "\n SELECT wid, ST_dumppoints(wgeom) AS dp" + "\n FROM base" + "\n )," + "\n blade AS (" + "\n SELECT wid, ST_collect((dp).geom) AS blade" + "\n FROM foo" + "\n GROUP BY wid" + "\n )," + "\n split AS (" + "\n SELECT base.*, (ST_Dump(ST_split(wgeom, blade))).geom AS line" + "\n FROM blade JOIN base" + "\n USING (wid)" + "\n )," + "\n distance AS (" + "\n SELECT split.*, ST_distance(line, pgeom) AS dist FROM split" + "\n )," + "\n second AS (" + "\n SELECT pid, min(dist) FROM distance GROUP BY pid" + "\n ), " + "\n last AS (" + "\n SELECT pid," + "\n (ST_y(ST_startpoint(line)) - ST_y(ST_endpoint(line))) * ST_x(pgeom)" + "\n + (ST_x(ST_endpoint(line)) - ST_x(ST_startpoint(line))) * ST_y(pgeom)" + "\n + (ST_x(ST_startpoint(line)) * ST_y(ST_endpoint(line)) - ST_x(ST_endpoint(line))" + "\n * ST_y(ST_startpoint(line))) AS val" + "\n FROM distance join second using (pid) where dist = min" + "\n )" + "\n UPDATE " + pois().addSchema() + " set side = case when val>0 then 'L' when val<0 then 'R' else 'B' end " + "\n FROM last " + "\n WHERE last.pid = " + pois().addSchema() + ".pid;" +"\n $$" +"\n LANGUAGE sql;" +"\nCOMMENT ON FUNCTION " + m_schema + "osm2pgr_pois_find_side()" + "\n IS 'osm2pgrouting generated function';" ); m_points_of_interest.add_sql( "CREATE OR REPLACE FUNCTION " + m_schema +"osm2pgr_pois_new_geom()" "\n RETURNS VOID AS" "\n $$" "\n UPDATE " + pois().addSchema() + "\n SET new_geom = ST_LineInterpolatePoint(e.the_geom, fraction)" + "\n FROM " + ways().addSchema() + " AS e WHERE edge_id = gid;" "\n UPDATE " + pois().addSchema() + "\n SET new_geom = the_geom" + "\n WHERE vertex_id IS NOT NULL;" + "\n $$" + "\n LANGUAGE sql;" +"\nCOMMENT ON FUNCTION " + m_schema + "osm2pgr_pois_new_geom()" + "\n IS 'osm2pgrouting generated function';" ); m_points_of_interest.add_sql( "\nCREATE OR REPLACE FUNCTION " + m_schema +"osm2pgr_pois_update(radius FLOAT DEFAULT 200, within FLOAT DEFAULT 50)" "\n RETURNS BIGINT AS" "\n $$" "\n-----------------------------------------------------------------" "\n-- Cycles thru all the POIS to either:" "\n-- Sets the vid value to the corresponding id of the vertices table" "\n-- Set the edge_id & disance" "\n-- Sets the side" "\n-- " "\n-- By working on areas of (radius)mts on POIS" "\n-- looking on edges that are at least (within) mts of each POI" "\n-- POIS that do not have a closest edge is considered as tooFar" "\n-- " "\n-- TooFar: are the POIS that do not have a closest edge within (radius + within)mts" "\n-- Recommended values radius = 200, within 50 mts" "\n-----------------------------------------------------------------\n" "\n DECLARE" "\n curr_tot BIGINT;" "\n total BIGINT :=0;" "\n rec RECORD;" "\n factor FLOAT = 0.5;" "\n tooFar BIGINT[];" "\n BEGIN" "\n total = " + m_schema + "osm2pgr_pois_update_part_of_topology();" "\n SELECT count(*) FROM " + pois().addSchema() +"\n WHERE vertex_id IS NULL AND edge_id IS NULL" +"\n INTO rec; " +"\n FOR i IN 1..rec.count LOOP" +"\n curr_tot = " + m_schema + "osm2pgr_pois_update_not_part_of_topology(radius, within, tooFar);" +"\n RAISE NOTICE '%: Updated % points of Interest', i, curr_tot;" +"\n total := total + curr_tot;" +"\n IF (curr_tot = 0) THEN" +"\n SELECT pid FROM " + pois().addSchema() +"\n WHERE vertex_id IS NULL AND edge_id IS NULL" +"\n AND pid not in (SELECT unnest(tooFar))" +"\n limit 1 INTO rec;" +"\n raise notice 'Not within range: pid = %', rec.pid;" +"\n tooFar := tooFar || rec.pid;" +"\n SELECT count(*) FROM (SELECT * FROM " + pois().addSchema() +"\n WHERE vertex_id IS NULL AND edge_id IS NULL" +"\n AND pid not in (SELECT unnest(tooFar)) LIMIT 1) a INTO rec;" +"\n EXIT WHEN rec.count = 0;" +"\n END IF;" +"\n END LOOP;" +"\n PERFORM " + m_schema + "osm2pgr_pois_find_side();" +"\n PERFORM " + m_schema + "osm2pgr_pois_new_geom();" +"\n return total;" +"\n END;" +"\n $$" +"\n LANGUAGE plpgsql;" +"\nCOMMENT ON FUNCTION " + m_schema + "osm2pgr_pois_update(float, float)" + "\n IS 'osm2pgrouting generated function. " + m_schema + "osm2pgr_pois_update(radius, within)\nworking on areas of (radius)mts\nOn edges that are at least (within) mts of each POI';" ); } } // namespace osm2pgr osm2pgrouting-2.3.8/src/database/ways_config.cpp000066400000000000000000000076131405641420700217240ustar00rootroot00000000000000/*PGR-GNU***************************************************************** Copyright (c) 2017 pgRouting developers Mail: project@pgrouting.org ------ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ********************************************************************PGR-GNU*/ #include "boost/lexical_cast.hpp" #include "database/table_management.h" #include "utilities/utilities.h" #include namespace osm2pgr { /* * configuring TABLE osm_nodes */ Table Tables::ways_config() const { Table table( /* name */ "ways", /* schema */ m_vm["schema"].as(), /* full name */ std::string( m_vm["prefix"].as() + "ways" + m_vm["suffix"].as()), /* standard column creation string */ std::string( " gid bigserial" ", osm_id bigint" ", tag_id integer" ", length double precision" ", length_m double precision" ", name text" ", source bigint" ", target bigint" ", source_osm bigint" ", target_osm bigint" ", cost double precision" ", reverse_cost double precision" ", cost_s double precision " ", reverse_cost_s double precision" ", rule text" ", one_way int " ", oneway TEXT " ", x1 double precision" ", y1 double precision" ", x2 double precision" ", y2 double precision" ", maxspeed_forward double precision" ", maxspeed_backward double precision" ", priority double precision DEFAULT 1" #if 0 + (m_vm.count("attributes") ? (std::string(", attributes ") + (m_vm.count("hstore") ? "hstore" : "json")) : "") + (m_vm.count("tags") ? (std::string(", tags ") + (m_vm.count("hstore") ? "hstore" : "json")) : "") #endif ), /* other columns */ "", /* geometry */ "LINESTRING"); std::vector columns; columns.push_back("tag_id"); columns.push_back("osm_id"); columns.push_back("maxspeed_forward"); columns.push_back("maxspeed_backward"); columns.push_back("one_way"); columns.push_back("oneway"); columns.push_back("priority"); columns.push_back("length"); columns.push_back("x1"); columns.push_back("y1"); columns.push_back("x2"); columns.push_back("y2"); columns.push_back("source_osm"); columns.push_back("target_osm"); columns.push_back("the_geom"); columns.push_back("cost"); columns.push_back("reverse_cost"); columns.push_back("name"); #if 0 // TODO get from the configuration columns.push_back("tag_name"); columns.push_back("tag_value"); // end todo if (m_vm.count("attributes")) columns.push_back("attributes"); if (m_vm.count("tags")) columns.push_back("tags"); #endif table.set_columns(columns); return table; } } //namespace osm2pgr osm2pgrouting-2.3.8/src/database/ways_vertices_pgr_config.cpp000066400000000000000000000045261405641420700245000ustar00rootroot00000000000000/*PGR-GNU***************************************************************** Copyright (c) 2017 pgRouting developers Mail: project@pgrouting.org ------ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ********************************************************************PGR-GNU*/ #include "boost/lexical_cast.hpp" #include "database/table_management.h" #include "utilities/utilities.h" #include namespace osm2pgr { /* * configuring TABLE osm_nodes */ Table Tables::ways_vertices_pgr_config() const { Table table( /* name */ "ways_vertices_pgr", /* schema */ m_vm["schema"].as(), /* full name */ std::string( m_vm["prefix"].as() + "ways" + m_vm["suffix"].as() + "_vertices_pgr"), /* standard column creation string */ std::string( " id bigserial" ", osm_id bigint" ", eout integer" ", lon decimal(11,8)" ", lat decimal(11,8)" ", cnt integer" ", chk integer" ", ein integer" #if 0 + (m_vm.count("attributes") ? (std::string(", attributes ") + (m_vm.count("hstore") ? "hstore" : "json")) : "") + (m_vm.count("tags") ? (std::string(", tags ") + (m_vm.count("hstore") ? "hstore" : "json")) : "") #endif ), /* other columns */ "", /* geometry */ "POINT"); return table; } } //namespace osm2pgr osm2pgrouting-2.3.8/src/osm_elements/000077500000000000000000000000001405641420700176275ustar00rootroot00000000000000osm2pgrouting-2.3.8/src/osm_elements/Node.cpp000066400000000000000000000056451405641420700212320ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2016 by pgRouting developers * * project@pgrouting.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License t &or more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #if 0 #include #include #endif #include #include #include #include #include "osm_elements/osm_tag.h" #include "osm_elements/Node.h" namespace osm2pgr { Node::Node(const char **atts) : Element(atts), m_numsOfUse(0) { assert(has_attribute("lat")); assert(has_attribute("lon")); } void Node::tag_config(const Tag &tag) { Element::tag_config(tag); ++m_numsOfUse; ++m_numsOfUse; } double Node::getLength(const Node &previous) const { auto y1 = boost::lexical_cast(get_attribute("lat")); auto x1 = boost::lexical_cast(get_attribute("lon")); auto y2 = boost::lexical_cast(previous.get_attribute("lat")); auto x2 = boost::lexical_cast(previous.get_attribute("lon")); return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); #if 0 typedef boost::geometry::model::d2::point_xy point_type; /* converted point to fit boost.geomtery * * (`p` and `q` are same as `a ` and `b`) * */ point_type p( boost::lexical_cast(get_attribute("lat")), boost::lexical_cast(get_attribute("lon"))); point_type q( boost::lexical_cast(previous.get_attribute("lat")), boost::lexical_cast(previous.get_attribute("lon"))); return boost::geometry::distance(p, q); #endif } } // namespace osm2pgr osm2pgrouting-2.3.8/src/osm_elements/OSMDocument.cpp000066400000000000000000000164241405641420700224770ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2016 by pgRouting developers * * project@pgrouting.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License t &or more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "osm_elements/OSMDocument.h" #include #include #include #include #include #include #include #if 0 #include #endif #include "utilities/utilities.h" #include "configuration/configuration.h" #include "osm_elements/Node.h" #include "osm_elements/Relation.h" #include "osm_elements/Way.h" #include "database/Export2DB.h" namespace osm2pgr { OSMDocument::OSMDocument( const Configuration &config, const po::variables_map &vm, const Export2DB &db_conn, size_t lines) : m_relPending(false), m_waysPending(true), m_rConfig(config), m_vm(vm), m_db_conn(db_conn), m_chunk_size(vm["chunk"].as()), m_nodeErrs(0), m_lines(lines) { } void OSMDocument::wait_child() const { #if 0 while (true) { int status; pid_t done = wait(&status); if (done == -1) { if (errno == ECHILD) break; // no more child processes } else { if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { cerr << "pid " << done << " failed" << endl; exit(1); } } } #endif } void OSMDocument::AddNode(const Node &n) { if (m_vm.count("addnodes")) { if ((m_nodes.size() % m_chunk_size) == 0) { wait_child(); std::cout << "\rCurrent osm_nodes:\t" << m_nodes.size(); osm_table_export(m_nodes, "osm_nodes"); export_pois(); } } m_nodes.push_back(n); } void OSMDocument::AddWay(const Way &w) { if (m_ways.empty() && m_vm.count("addnodes")) { wait_child(); osm_table_export(m_nodes, "osm_nodes"); export_pois(); std::cout << "\nFinal osm_nodes:\t" << m_nodes.size() << "\n"; } if (m_vm.count("addnodes")) { if ((m_ways.size() % m_chunk_size) == 0) { wait_child(); std::cout << "\rCurrent osm_ways:\t" << m_ways.size(); osm_table_export(m_ways, "osm_ways"); } } m_ways.push_back(w); } void OSMDocument::AddRelation(const Relation &r) { m_relPending = true; m_relations.push_back(r); if (m_vm.count("addnodes")) { if (m_relations.size() % m_chunk_size == 0) { wait_child(); std::cout << "Current osm_relations:\t" << m_relations.size(); osm_table_export(m_relations, "osm_relations"); m_relPending = false; } } } void OSMDocument::endOfFile() { if (m_vm.count("addnodes") && m_waysPending) { m_waysPending = false; wait_child(); osm_table_export(m_ways, "osm_ways"); std::cout << "\nFinal osm_ways:\t\t" << m_ways.size(); } if (m_vm.count("addnodes") && m_relPending) { m_relPending = false; wait_child(); std::cout << "\nFinal osm_relations:\t" << m_relations.size() << "\n"; osm_table_export(m_relations, "osm_relations"); } std::cout << "\nEnd Of file\n\n\n"; } template static bool less(const T &item, const int64_t &id) { return item.osm_id() < id; } Node* OSMDocument::FindNode(int64_t node_id) { auto it = std::lower_bound(m_nodes.begin(), m_nodes.end(), node_id, less); return &*it; } bool OSMDocument::has_node(int64_t node_id) const { auto it = std::lower_bound(m_nodes.begin(), m_nodes.end(), node_id, less); return (it != m_nodes.end()); } Way* OSMDocument::FindWay(int64_t way_id) { auto it = std::lower_bound(m_ways.begin(), m_ways.end(), way_id, less); return &*it; } bool OSMDocument::has_way(int64_t way_id) const { auto it = std::lower_bound(m_ways.begin(), m_ways.end(), way_id, less); return (it != m_ways.end()); } void OSMDocument::add_node(Way &way, const char **atts) { auto **attribut = atts; std::string key = *attribut++; std::string value = *attribut++; auto node_id = (key == "ref")? boost::lexical_cast(value): -1; way.add_node(node_id); #if 1 // TODO leave this when splitting if (!has_node(node_id)) { ++m_nodeErrs; } else { auto node = FindNode(node_id); node->incrementUse(); way.add_node(node); } #endif } /* * for example * * * * And the configuration file has: * * * */ void OSMDocument::add_config(Element *item, const Tag &tag) const { auto k = tag.key(); auto v = tag.value(); if (config_has_tag(tag)) { if (!(item->is_tag_configured()) || (config_has_tag(item->tag_config()) && m_rConfig.priority(tag) < m_rConfig.priority(item->tag_config()) )) { item->tag_config(tag); } } } static bool has_no_tags(const Node &node) { return !node.has_tags(); } void OSMDocument::export_pois() const { std::string table("pointsofinterest"); if (m_nodes.empty()) return; #if 0 if (m_vm.count("fork")) { auto pid = fork(); if (pid < 0) { std::cerr << "Failed to fork" << endl; exit(1); } if (pid > 0) return; } #endif auto residue = m_nodes.size() % m_chunk_size; size_t start = residue? m_nodes.size() - residue : m_nodes.size() - m_chunk_size; auto export_items = Nodes(m_nodes.begin() + start, m_nodes.end()); /* * deleting nodes with no tag information */ export_items.erase( std::remove_if(export_items.begin(), export_items.end(), has_no_tags), export_items.end()); if (!export_items.empty()) { m_db_conn.export_osm(export_items, table); } #if 0 if (m_vm.count("fork")) { /* * finish the child process */ _exit(0); } #endif } } // namespace osm2pgr osm2pgrouting-2.3.8/src/osm_elements/Relation.cpp000066400000000000000000000055721405641420700221210ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2016 by pgRouting developers * * project@pgrouting.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License t &or more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include #include "osm_elements/Relation.h" namespace osm2pgr { Relation::Relation(const char **atts) : Element(atts) { } int64_t Relation::add_member(const char **atts) { #if 0 std::cout << " --> " << __PRETTY_FUNCTION__ << "\n"; #endif auto **attribut = atts; std::string type; int64_t osm_id(0); std::string role; while (*attribut != NULL) { std::string key = *attribut++; std::string value = *attribut++; /* * currently only adding way */ if (key == "type") { if (value != "way") return -1; } if (key == "ref") { osm_id = boost::lexical_cast(value); } if (key == "role") { role = value; } } m_WayRefs.push_back(osm_id); #if 0 std::cout << "members" << members_str() << "\n"; std::cout << " <-- " << __PRETTY_FUNCTION__ << "\n"; #endif return osm_id; } std::string Relation::members_str() const { std::string way_list(""); for (const auto &way_ref : m_WayRefs) { way_list += boost::lexical_cast(way_ref) /* * currently only adding way */ + "=>\"type=>way\","; } way_list[way_list.size() -1] = ' '; return way_list; } std::ostream& operator<<(std::ostream &os, const Relation &r) { os << r.members_str(); return os; } } // end namespace osm2pgr osm2pgrouting-2.3.8/src/osm_elements/Way.cpp000066400000000000000000000222651405641420700211020ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2016 by pgRouting developers * * project@pgrouting.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License t &or more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "osm_elements/Way.h" #include "boost/lexical_cast.hpp" #include #include #include #include #include #include #include "osm_elements/OSMDocument.h" #include "osm_elements/osm_tag.h" #include "osm_elements/Node.h" namespace osm2pgr { Way::Way(const char **atts) : Element(atts), m_maxspeed_forward(-1), m_maxspeed_backward(-1), m_oneWay("UNKNOWN") { } Tag Way::add_tag(const Tag &tag) { m_tags[tag.key()] = tag.value(); implied_oneWay(tag); oneWay(tag); max_speed(tag); return tag; } void Way::add_node(int64_t node_id) { m_node_ids.push_back(node_id); } void Way::add_node(Node *node) { assert(node); m_NodeRefs.push_back(node); } std::string Way::get_geometry() const { return geometry_str(m_NodeRefs); } std::string Way::length_str() const { return length_str(m_NodeRefs); } std::string Way::geometry_str(const std::vector &nodeRefs) const { if (nodeRefs.size() < 2) return "srid=4326;LINESTRING EMPTY"; std::string geometry("srid=4326;LINESTRING("); for (auto it = nodeRefs.begin(); it != nodeRefs.end(); ++it) { auto node_ptr = *it; geometry += node_ptr->geom_str(" "); geometry += ", "; } geometry[geometry.size() - 2] = ')'; return geometry; } std::string Way::length_str(const std::vector &nodeRefs) const { double length = 0; auto prev_node_ptr = nodeRefs.front(); for (auto it = nodeRefs.begin(); it != nodeRefs.end(); ++it) { auto node_ptr = *it; length += node_ptr->getLength(*prev_node_ptr); prev_node_ptr = node_ptr; } return boost::lexical_cast(length); } std::vector> Way::split_me() { if (nodeRefs().size() < 2) { /* * The way is ill formed */ return std::vector>(); } std::vector> m_split_ways; auto it_node(nodeRefs().begin()); auto last_node(nodeRefs().end()); while (it_node != last_node) { /* * starting a new split */ std::vector split_way; split_way.push_back(*it_node); ++it_node; if (it_node != last_node) { while (it_node != last_node) { split_way.push_back(*it_node); if ((*it_node)->numsOfUse() > 1) { break; } ++it_node; } } if (split_way.size() > 1) m_split_ways.push_back(split_way); } return m_split_ways; } std::string Way::oneWay() const { return static_cast(m_oneWay); } void Way::oneWay(const Tag &tag) { auto key = tag.key(); auto value = tag.value(); if (key != "oneway") { return; } if (m_oneWay != "UNKNOWN") return; // one way tag if ((value == "yes") || value == "true" || value == "1") { m_oneWay = "YES"; } // check false conditions: 0, no, false if ((value == "no") || value == "false" || value == "0") { m_oneWay = "NO"; } // check reversible condition if (value == "reversible") { m_oneWay = "REVERSIBLE"; } // check revers conditions: -1 if (value == "-1") { m_oneWay = "REVERSED"; } } void Way::implied_oneWay(const Tag &tag) { auto key = tag.key(); auto value = tag.value(); /* * was tagged, so not using implied tagging */ if (m_oneWay != "UNKNOWN") return; if ((key == "junction" && value == "roundabout") || (key == "highway" && value == "motorway")) { m_oneWay = "YES"; return; } } #if 0 void Way::pedestrian(const std::string &key, const std::string &value) { // TODO(vicky) for 3.0 // m_pedestrian("UNKNOWN") <-- the default in the constructor if ((key == "sidewalk" && value == "no") || (key == "foot" && value == "no")) { m_pedestrian = "NO"; } if ((key == "highway" && value == "pedestrian") { || (key == "highway" && value == "footway") || (key == "highway" && value == "cycleway") || (key == "highway" && value == "bridleway") || (key == "highway" && value == "track") || (key == "sidewalk" && value != "no") ) || (key == "foot" && value != "no") ) || (key == "highway" && value == "steps") { m_pedestrian = "YES"; return } } } #endif bool Way::is_number(const std::string& s) const { std::string::const_iterator it = s.begin(); while (it != s.end() && std::isdigit(*it)) ++it; return !s.empty() && it == s.end(); } /* * takes the fist value found */ double Way::get_kph(const std::string &value) const { auto mph_pos = value.find(" mph"); if (mph_pos != std::string::npos) { auto newstr = value; newstr.erase(mph_pos, std::string::npos); if (is_number(newstr)) { return boost::lexical_cast(newstr) * 1.609346; } } mph_pos = value.find("knots"); if (mph_pos != std::string::npos) { auto newstr = value; newstr.erase(mph_pos, std::string::npos); if (is_number(newstr)) { return boost::lexical_cast(newstr) * 1.852; } } if (is_number(value)) { return boost::lexical_cast(value); } // TODO(vicky): handle non-numeric values, ex.: RO:urban // maybe using a configuration option // http://wiki.openstreetmap.org/wiki/Speed_limits // // TODO(vicky): handle multiple values for lanes // the way with N lanes generates N ways that have to be split // with the different Speeds ??? return 50; } void Way::max_speed(const Tag &tag) { auto key = tag.key(); auto value = tag.value(); if (key == "maxspeed:forward") { m_maxspeed_forward = get_kph(value); return; } if (key == "maxspeed:backward") { m_maxspeed_backward = get_kph(value); return; } if (key == "maxspeed") { m_maxspeed_backward = get_kph(value); m_maxspeed_forward = get_kph(value); return; } } std::string Way::oneWayType_str() const { if (m_oneWay == "YES") return "1"; if (m_oneWay == "NO") return "2"; if (m_oneWay == "REVERSIBLE") return "3"; if (m_oneWay == "REVERSED") return "-1"; if (m_oneWay == "UNKNOWN") return "0"; return "0"; } void Way::insert_tags(const std::map &tags) { for (auto it = tags.begin(); it != tags.end(); ++it) { auto tag = *it; m_tags[tag.first] = tag.second; } } std::string Way::members_str() const { /* this list comes from the node_ids becuase a node might not be on the file */ std::string node_list(""); for (const auto &node_id : m_node_ids) { node_list += boost::lexical_cast(node_id) + "=>\"type=>nd\","; } node_list[node_list.size() -1] = ' '; return node_list; } #ifndef NDEBUG std::ostream& operator<<(std::ostream &os, const Way &way) { std::cout << "\nWay" << "\t m_osm_id: " << way.m_osm_id << "\t m_tag_config: " << way.m_tag_config << "\t m_visible: " << way.m_visible << "\t m_maxspeed_forward: " << way.m_maxspeed_forward << "\t m_maxspeed_backward: " << way.m_maxspeed_backward << "\t m_oneWay: " << way.m_oneWay; std::cout << "\n\n ************ attributes: " << way.attributes_str(); std::cout << "\n\n ************ tags: " << way.tags_str(); std::cout << "\n nodes: \n"; for (auto it = way.m_NodeRefs.begin(); it != way.m_NodeRefs.end(); ++it) { auto e = *it; std::cout << e->osm_id() << ", "; } return os; } #endif } // namespace osm2pgr osm2pgrouting-2.3.8/src/osm_elements/osm2pgrouting.cpp000066400000000000000000000225271405641420700231620ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2016 by pgRouting developers * * project@pgrouting.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License t &or more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #if __GNUC__ > 5 || \ (__GNUC__ == 4 && (__GNUC_MINOR__ >= 7)) #define WITH_TIME #endif #include #include #include #ifdef WITH_TIME #include #include #endif #include #include "parser/ConfigurationParserCallback.h" #include "parser/OSMDocumentParserCallback.h" #include "osm_elements/OSMDocument.h" #include "database/Export2DB.h" #include "utilities/handle_pgpass.h" #include "utilities/prog_options.h" #if defined(__linux__) static size_t lines_in_file(const std::string file_name) { FILE *in; char buff[512]; std::string command = "wc -l " + file_name; if (!(in = popen(command.c_str(), "r"))) { std::cerr << "File not found" << file_name << "\n"; exit(1); } std::string word; if (fgets(buff, 512, in) != NULL) { word = buff; } pclose(in); std::istringstream iss(word); std::string number; iss >> number; try { return boost::lexical_cast(number); } catch (...) { std::cerr << "File not found" << file_name << "\n"; exit(1); } } #endif int main(int argc, char* argv[]) { #ifdef WITH_TIME /* * Start Timers * version prior to 4.7.0 std::chrono::steady_clock was not implemented. * so using timers work on g++4.7 up */ clock_t begin = clock(); std::time_t start_t = std::time(NULL); std::chrono::steady_clock::time_point begin_elapsed = std::chrono::steady_clock::now(); #endif try { po::options_description od_desc("Allowed options"); get_option_description(od_desc); po::variables_map vm; po::store(po::command_line_parser(argc, argv). options(od_desc).run(), vm); if (vm.count("help")) { std::cout << od_desc << "\n"; return 0; } if (vm.count("version")) { std::cout << "This is osm2pgrouting Version 2.3.8\n"; return 0; } try { notify(vm); } catch(exception &ex) { std::cout << ex.what() << "\n"; std::cout << od_desc << "\n"; return 0; } #ifdef WITH_TIME std::cout << "Execution starts at: " << std::ctime(&start_t) << "\n"; #endif process_command_line(vm); auto dataFile(vm["file"].as()); auto confFile(vm["conf"].as()); auto clean(vm.count("clean")); auto no_index(vm.count("no-index")); handle_pgpass(vm); std::string connection_str( "host=" + vm["host"].as() + " user=" + vm["username"].as() + " dbname=" + vm["dbname"].as() + " port=" + vm["port"].as() + " password=" + vm["password"].as()); try { cout << "Testing database connection: " << vm["dbname"].as() << endl; pqxx::connection C(connection_str); if (C.is_open()) { cout << "database connection successful: " << C.dbname() << endl; } else { cout << "Can't open database" << endl; return 1; } #ifdef PQXX_DISCONNECT C.disconnect (); #endif }catch (const std::exception &e){ cerr << e.what() << std::endl; return 1; } /* * preparing the databasse */ std::cout << "Connecting to the database" << endl; osm2pgr::Export2DB dbConnection(vm, connection_str); if (dbConnection.connect() == 1) return 1; #ifndef NDEBUG dbConnection.install_postGIS(); #endif if (!dbConnection.has_extension("postgis")) { std::cout << "ERROR: postGIS not found\n"; std::cout << " HINT: CREATE EXTENSION postGIS\n"; return 1; } if ((vm.count("attributes") || vm.count("tags") || vm.count("addnodes")) && !dbConnection.has_extension("hstore")) { std::cout << "ERROR: hstore not found\n"; std::cout << " HINT: CREATE EXTENSION hstore\n"; return 1; } if (clean) { std::cout << "\nDropping tables..." << endl; dbConnection.dropTables(); } std::cout << "\nCreating tables..." << endl; dbConnection.createTables(); /* * End: preparing the databasse */ std::cout << "Opening configuration file: " << confFile.c_str() << endl; osm2pgr::Configuration config; osm2pgr::ConfigurationParserCallback cCallback(config); std::cout << " Parsing configuration\n" << endl; xml::XMLParser parser; int ret = parser.Parse(cCallback, confFile.c_str()); if (ret != 0) { cout << "Failed to open / parse config file\n" << confFile.c_str() << endl; return 1; } std::cout << "Exporting configuration ...\n"; dbConnection.export_configuration(config.types()); std::cout << " - Done \n"; #if defined(__linux__) std::cout << "Counting lines ...\n"; auto total_lines = lines_in_file(dataFile); std::cout << " - Done \n"; std::cout << "Opening data file: " << dataFile << "\ttotal lines: " << total_lines << endl; #else size_t total_lines = 0; #endif osm2pgr::OSMDocument document(config, vm, dbConnection, total_lines); osm2pgr::OSMDocumentParserCallback callback(document); std::cout << " Parsing data\n" << endl; ret = parser.Parse(callback, dataFile.c_str()); if (ret != 0) { cerr << "Failed to open / parse data file " << dataFile << endl; return 1; } std::cout << " Finish Parsing data\n" << endl; if (document.nodeErrs()) { std::cerr << "******\nNOTICE: Found " << document.nodeErrs() << " node references with no \n*****"; } //############# Export2DB { std::cout << "Adding auxiliary tables to database..." << endl; std::cout << "\nExport Ways ..." << endl; dbConnection.exportWays(document.ways(), config); if (!no_index) { std::cout << "\nCreating indexes ..." << endl; dbConnection.createFKeys(); } std::cout << "\nProcessing Points of Interest ..." << endl; dbConnection.process_pois(); } std::cout << "#########################" << endl; std::cout << "size of streets: " << document.ways().size() << endl; #ifdef WITH_TIME clock_t end = clock(); double elapsed_secs = static_cast(end - begin) / static_cast(CLOCKS_PER_SEC); std::time_t end_t = std::time(NULL); std::chrono::steady_clock::time_point end_elapsed = std::chrono::steady_clock::now(); typedef std::chrono::duration millisecs_t; millisecs_t duration = std::chrono::duration_cast( end_elapsed - begin_elapsed); std::cout << "Execution started at: " << std::ctime(&start_t); std::cout << "Execution ended at: " << std::ctime(&end_t); std::cout << "Elapsed time: " << static_cast(duration.count())/static_cast(1000) << " Seconds.\n"; std::cout << "User CPU time: -> " << elapsed_secs << " seconds\n"; #endif std::cout << "#########################" << endl; exit(0); } catch (exception &e) { std::cout << e.what() << endl; return 1; } catch (string &e) { std::cout << e << endl; return 1; } catch (...) { std::cout << "Terminating" << endl; return 1; } } osm2pgrouting-2.3.8/src/osm_elements/osm_element.cpp000066400000000000000000000151121405641420700226420ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2016 by pgRouting developers * * project@pgrouting.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License t &or more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include #include #include "osm_elements/osm_tag.h" #include "osm_elements/osm_element.h" namespace osm2pgr { Element::Element(const char **atts) : m_visible(true) { auto **attribut = atts; while (*attribut != NULL) { std::string name = *attribut++; std::string value = *attribut++; if (name == "id") { m_osm_id = boost::lexical_cast(value); } if (name == "visible") { m_visible = (value == "true")? true : false; } m_attributes[name] = value; } } void Element::tag_config(const Tag &tag) { m_tag_config = tag; } Tag Element::add_tag(const Tag &tag) { m_tags[tag.key()] = tag.value(); return tag; } bool Element::has_tag(const std::string& key) const { return m_tags.find(key) != m_tags.end(); } std::string Element::get_tag(const std::string& key) const { return m_tags.find(key)->second; } bool Element::is_tag_configured() const { return (m_tag_config.key() != "" && m_tag_config.value() != ""); } bool Element::has_attribute(const std::string& key) const { return m_attributes.find(key) != m_attributes.end(); } std::string Element::get_attribute(const std::string& key) const { return m_attributes.find(key)->second; } std::string Element::attributes_str() const { if (m_tags.empty()) return "\"\""; std::string str("\""); for (auto it = m_attributes.begin(); it != m_attributes.end(); ++it) { auto attribute = *it; str += attribute.first + "=>" + attribute.second + ","; } str[str.size()-1] = '\"'; return str; } std::string Element::tags_str() const { if (m_tags.empty()) return ""; std::string str("\""); for (auto it = m_tags.begin(); it != m_tags.end(); ++it) { auto tag = *it; str += tag.first + "=>" + tag.second + ","; } str[str.size()-1] = '\"'; return str; } static std::string addquotes(const std::string str, bool force) { std::string result(""); for (auto c : str) { if ( c == '"' ) { /* * To avoid problems with json & hstore * all quotes are converted to single quotes */ result += "\'\'"; continue; } else if ( c == '\\' ) { result += '\\'; } else if (c == '\'') { result += '\''; } else if (c == '\n') { result += "\\n"; continue; } else if (c == '\r') { result += "\\r"; continue; } else if (c == '\t') { result += "\\t"; continue; } result += c; } if (!force) { for (auto c : result) { if (c == ' ' || c == ',' || c == '=' || c == '>' || c == ':') { return std::string("\"") + result + "\""; } } return result; } return std::string("\"") + result + "\""; } static std::string getHstore(const std::map &values) { std::string hstore; if (values.empty()) return std::string(); for (const auto item : values) { hstore += addquotes(item.first, true) + " => " + addquotes(item.second, true) + ","; } hstore[hstore.size() - 1] = ' '; hstore += ""; return hstore; } #if 0 static std::string getJSON(const std::map &values) { if (values.empty()) return std::string("{}"); std::string json("{"); for (const auto item : values) { json += addquotes(item.first, true) + ":" + addquotes(item.second, true) + ","; } json[json.size() - 1] = '}'; json += ""; return json; } #endif std::vector Element::values(const std::vector &columns, bool is_hstore) const { std::vector values; for (const auto column : columns) { if (column == "osm_id" || column == "tag_id") { values.push_back(boost::lexical_cast(osm_id())); continue; } if (column == "tag_name") { values.push_back(m_tag_config.key()); continue; } if (column == "tag_value") { values.push_back(m_tag_config.value()); continue; } if (column == "the_geom") { values.push_back(get_geometry()); continue; } if (column == "members") { values.push_back(members_str()); continue; } if (column == "attributes") { values.push_back(getHstore(m_attributes)); continue; } if (column == "tags") { values.push_back(getHstore(m_tags)); if (is_hstore) {}; continue; } if (has_attribute(column)) { values.push_back(get_attribute(column)); continue; } if (has_tag(column)) { values.push_back(get_tag(column)); continue; } values.push_back(std::string("")); } return values; } } // namespace osm2pgr osm2pgrouting-2.3.8/src/osm_elements/osm_tag.cpp000066400000000000000000000041241405641420700217650ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2016 by pgRouting developers * * project@pgrouting.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License t &or more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "osm_elements/osm_tag.h" #include namespace osm2pgr { Tag::Tag(const char **atts) { auto **attribut = atts; while (*attribut != NULL) { std::string name = *attribut++; std::string value = *attribut++; if (name == "k") { std::transform(value.begin(), value.end(), value.begin(), [](char ch) { return ch == ' ' ? '_' : ch;}); m_key = value; } else if (name == "v") { m_value = value; } } } std::ostream& operator<<(std::ostream &os, const Tag& tag) { os << tag.m_key << "=>" << tag.m_value; return os; } } // namespace osm2pgr osm2pgrouting-2.3.8/src/parser/000077500000000000000000000000001405641420700164315ustar00rootroot00000000000000osm2pgrouting-2.3.8/src/parser/ConfigurationParserCallback.cpp000066400000000000000000000045121405641420700245400ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2016 by pgRouting developers * * project@pgrouting.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License t &or more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "parser/ConfigurationParserCallback.h" #include #include #include "osm_elements/OSMDocument.h" #include "configuration/configuration.h" namespace osm2pgr { /*! Parser callback for configuration files */ void ConfigurationParserCallback::StartElement( const char *name, const char** atts) { /* the type is the tag_key */ if (strcmp(name, "tag_name") == 0) { m_current = new Tag_key(atts); /* the class is the tag_value */ } else if (strcmp(name, "tag_value") == 0) { m_current->add_tag_value(Tag_value(atts)); } else if (strcmp(name, "configuration") == 0) { } } void ConfigurationParserCallback::EndElement(const char* name) { /* the type is the tag_key */ if (strcmp(name, "tag_name") == 0) { m_config.add_tag_key(*m_current); delete m_current; } } } // namespace osm2pgr osm2pgrouting-2.3.8/src/parser/OSMDocumentParserCallback.cpp000066400000000000000000000156661405641420700241020ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2016 by pgRouting developers * * project@pgrouting.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License t &or more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "parser/OSMDocumentParserCallback.h" #include #include #include #include #include #include "osm_elements/OSMDocument.h" #include "osm_elements/Relation.h" #include "osm_elements/osm_tag.h" #include "osm_elements/Way.h" #include "osm_elements/Node.h" #include "utilities/print_progress.h" namespace osm2pgr { /* */ void OSMDocumentParserCallback::show_progress() { #if 0 try { if (m_line == 0) return; assert(m_rDocument.lines()); if (m_rDocument.lines() == 0) return; if (((++m_line) % (m_rDocument.lines() / 100)) == 0) { print_progress(m_rDocument.lines(), m_line); } } catch(...) { m_line = 1; } #endif } /** Parser callback for OSMDocument files */ void OSMDocumentParserCallback::StartElement( const char *name, const char** atts) { show_progress(); if (strcmp(name, "osm") == 0) { m_section = 1; } if ((m_section == 1 && (strcmp(name, "way") == 0)) || (m_section == 2 && (strcmp(name, "relation") == 0))) { ++m_section; } if (m_section == 1) { if (strcmp(name, "node") == 0) { last_node = new Node(atts); } if (strcmp(name, "tag") == 0) { auto tag = last_node->add_tag(Tag(atts)); m_rDocument.add_config(last_node, tag); } return; } if (m_section == 2) { if (strcmp(name, "way") == 0) { last_way = new Way(atts); } if (strcmp(name, "tag") == 0) { auto tag = last_way->add_tag(Tag(atts)); m_rDocument.add_config(last_way, tag); } if (strcmp(name, "nd") == 0) { m_rDocument.add_node(*last_way, atts); } return; } if (m_section == 3) { /* * START RELATIONS CODE */ if (strcmp(name, "relation") == 0) { last_relation = new Relation(atts); return; } if (strcmp(name, "member") == 0) { /* */ auto way_id = last_relation->add_member(atts); if (way_id == -1) return; assert(!last_relation->way_refs().empty()); if (m_rDocument.has_way(way_id)) { Way* way_ptr = m_rDocument.FindWay(way_id); way_ptr->insert_tags(last_relation->tags()); } else { assert(!last_relation->way_refs().empty()); last_relation->way_refs().pop_back(); } return; } if (strcmp(name, "tag") == 0) { auto tag = last_relation->add_tag(Tag(atts)); m_rDocument.add_config(last_relation, tag); } } if (strcmp(name, "osm") == 0) { } } void OSMDocumentParserCallback::EndElement(const char* name) { if (strcmp(name, "osm") == 0) { m_rDocument.endOfFile(); show_progress(); return; } if (strcmp(name, "node") == 0) { m_rDocument.AddNode(*last_node); delete last_node; return; } if (strcmp(name, "way") == 0) { m_rDocument.AddWay(*last_way); if (m_rDocument.config_has_tag(last_way->tag_config())) { auto maxspeed = m_rDocument.maxspeed(last_way->tag_config()); if (last_way->maxspeed_forward() <= 0) { last_way->maxspeed_forward(maxspeed); } if (last_way->maxspeed_backward() <= 0) { last_way->maxspeed_backward(maxspeed); } } delete last_way; return; } if (strcmp(name, "relation") == 0) { if (m_rDocument.config_has_tag(last_relation->tag_config())) { for (auto it = last_relation->way_refs().begin(); it != last_relation->way_refs().end(); ++it) { auto way_id = *it; assert(m_rDocument.has_way(way_id)); if (m_rDocument.has_way(way_id)) { Way* way_ptr = m_rDocument.FindWay(way_id); way_ptr->tag_config(last_relation->tag_config()); auto newValue = m_rDocument.maxspeed( last_relation->tag_config()); if (way_ptr->maxspeed_forward() <= 0) { way_ptr->maxspeed_forward(newValue); } if (way_ptr->maxspeed_backward() <= 0) { way_ptr->maxspeed_backward(newValue); } } } m_rDocument.AddRelation(*last_relation); } // TODO add all other relations delete last_relation; return; } } } // end namespace osm2pgr osm2pgrouting-2.3.8/src/parser/XMLParser.cpp000066400000000000000000000065311405641420700207570ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2016 by pgRouting developers * * project@pgrouting.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License t &or more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "parser/XMLParser.h" #include #include #include #include namespace xml { //------------------------------------- global Expat Callbacks: static void startElement(void *userData, const char *name, const char **atts) { XMLParserCallback* pCallback = reinterpret_cast(userData); if (pCallback) pCallback->StartElement(name, atts); } static void endElement(void *userData, const char *name) { XMLParserCallback* pCallback = reinterpret_cast(userData); if (pCallback) pCallback->EndElement(name); } int XMLParser::Parse(XMLParserCallback& rCallback, const char* chFileName) { int ret = 1; // File not found FILE* fp = fopen(chFileName, "rb"); if (fp) { XML_Parser parser = XML_ParserCreate(NULL); XML_SetUserData(parser, static_cast(&rCallback)); // register Callbacks for start- and end-element events of the parser: XML_SetElementHandler(parser, startElement, endElement); int done; do { // loop over whole file content char buf[BUFSIZ]; size_t len = fread(buf, 1, sizeof(buf), fp); // read chunk of data // end of file reached if buffer not completely filled done = len < sizeof(buf); if (!XML_Parse(parser, buf, static_cast(len), done)) { // a parse error occurred: std::cerr << XML_ErrorString(XML_GetErrorCode(parser)) << " at line " << static_cast(XML_GetCurrentLineNumber(parser)); fclose(fp); ret = 2; // quit, return = 2 indicating parsing error done = 1; return ret; } } while (!done); XML_ParserFree(parser); fclose(fp); ret = 0; } else { std::cerr << "Error opening " << chFileName << ":" << strerror(errno); } return ret; // return = 0 indicating success } } // end namespace xml //! \endcond osm2pgrouting-2.3.8/src/utilities/000077500000000000000000000000001405641420700171505ustar00rootroot00000000000000osm2pgrouting-2.3.8/src/utilities/handle_pgpass.cpp000066400000000000000000000055631405641420700224750ustar00rootroot00000000000000/*PGR-GNU***************************************************************** Copyright (c) 2017 pgRouting developers Mail: project@pgrouting.org ------ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ********************************************************************PGR-GNU*/ #include #include #include #include #include #include #include namespace po = boost::program_options; void handle_pgpass(po::variables_map &vm) { if (!vm["password"].as().empty()) { /* * nothing to do password is given */ return; } std::string file; auto filename(getenv("PGPASSFILE")); if (!filename) { #if 0 std::cout << "No PGPASSFILE found \n"; std::cout << "Looking for .pgpass \n"; #endif auto homedir(getenv("HOME")); if (!homedir) { std::cout << "No $HOME found \n"; return; } #if 0 std::cout << "home directory" << homedir << "\n"; #endif file = std::string(homedir) + "/.pgpass"; } else { file = filename; } std::ifstream infile(file.c_str()); if (!infile.good()) { return; } std::string host; std::string port; std::string dbase; std::string user; std::string passwd; std::string username = vm["username"].as().empty() ? getenv("USER") : vm["username"].as(); vm.at("username").value() = username; while (std::getline(infile, host, ':')) { std::getline(infile, port, ':'); std::getline(infile, dbase, ':'); std::getline(infile, user, ':'); std::getline(infile, passwd); if ((host == "" || host == "*" || host == vm["host"].as()) && (port == "*" || port == vm["port"].as()) && (dbase == "*" || dbase == vm["dbname"].as()) && (user == "*" || user == username) && (dbase == "*" || host == vm["dbname"].as())) { infile.close(); #if 0 std::cout << passwd << "\n"; #endif vm.at("password").value() = passwd; return; } } return; } osm2pgrouting-2.3.8/src/utilities/prog_options.cpp000066400000000000000000000141541405641420700224030ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2016 by pgRouting developers * * project@pgrouting.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License t &or more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include #include #include #include #include namespace po = boost::program_options; void get_option_description(po::options_description &od_desc) { /* po::options_description help_od_desc("Help"), required_od_desc("Required options"), optional_od_desc("Optional options"); */ po::options_description help_od_desc("Help"), general_od_desc("General"), db_options_od_desc("Database options"), not_used_od_desc("Not used currently"); help_od_desc.add_options() // help ("help", "Produce help message for this version.") ("version,v", "Print version string"); general_od_desc.add_options() // general ("file,f", po::value()->required(), "REQUIRED: Name of the osm file.") ("conf,c", po::value()->default_value("/usr/share/osm2pgrouting/mapconfig.xml"), "Name of the configuration xml file.") ("schema", po::value()->default_value(""), "Database schema to put tables.\n blank:\t defaults to default schema dictated by PostgreSQL search_path.") ("prefix", po::value()->default_value(""), "Prefix added at the beginning of the table names.") ("suffix", po::value()->default_value(""), "Suffix added at the end of the table names.") #if 0 ("postgis", "Install postgis if not found.") // TODO(vicky) remove before realesing #endif ("addnodes", "Import the osm_nodes, osm_ways & osm_relations tables.") ("attributes", "Include attributes information.") ("tags", "Include tag information.") ("chunk", po::value()->default_value(20000), "Exporting chunk size.") ("clean", "Drop previously created tables.") ("no-index", "Do not create indexes (Use when indexes are already created)"); #if 0 ("addways", "Import the osm_ways table.") ("addrelations", "Import the osm_relations table.") ("fork", "Use fork (works on small files)."); ("hstore", "Use hstore for attributes and/or tags. (not indicating will use json)") #endif db_options_od_desc.add_options() // database options ("dbname,d", po::value()->required(), "Name of your database (Required).") ("username,U", po::value()->default_value(""), "Name of the user, which have write access to the database.") ("host,h", po::value()->default_value("localhost"), "Host of your postgresql database.") ("port,p", po::value()->default_value("5432"), "db_port of your database.") ("password,W", po::value()->default_value(""), "Password for database access."); not_used_od_desc.add_options() ("threads,t", po::value()->default_value(false), "threads.") ("multimodal,m", po::value()->default_value(false), "multimodal.") ("multilevel,l", po::value()->default_value(false), "multilevel."); od_desc.add(help_od_desc).add(general_od_desc).add(db_options_od_desc); // .add(not_used_od_desc); return; } void process_command_line(po::variables_map &vm) { std::cout << "***************************************************\n"; std::cout << " COMMAND LINE CONFIGURATION *\n"; std::cout << "***************************************************\n"; std::cout << "Filename = " << vm["file"].as() << "\n"; std::cout << "Configuration file = " << vm["conf"].as() << "\n"; std::cout << "host = " << vm["host"].as() << "\n"; std::cout << "port = " << vm["port"].as() << "\n"; std::cout << "dbname = " << vm["dbname"].as() << "\n"; std::cout << "username = " << vm["username"].as() << "\n"; std::cout << "schema= " << vm["schema"].as() << "\n"; std::cout << "prefix = " << vm["prefix"].as() << "\n"; std::cout << "suffix = " << vm["suffix"].as() << "\n"; #if 0 std::cout << (vm.count("postgis")? "I" : "Don't I") << "nstall postgis if not found\n"; #endif std::cout << (vm.count("clean")? "D" : "Don't d") << "rop tables\n"; std::cout << (vm.count("no-index")? "D" : "Don't c") << "reate indexes\n"; std::cout << (vm.count("addnodes")? "A" : "Don't a") << "dd OSM nodes\n"; #if 0 std::cout << (vm.count("addways")? "A" : "Don't a") << "dd OSM ways\n"; std::cout << (vm.count("addrelations")? "A" : "Don't a") << "dd OSM relations\n"; std::cout << (vm.count("fork")? "F" : "Don't f") << "ork\n"; #endif std::cout << "***************************************************\n"; } osm2pgrouting-2.3.8/src/utilities/utilities.cpp000066400000000000000000000030521405641420700216670ustar00rootroot00000000000000/*PGR-GNU***************************************************************** Copyright (c) 2017 pgRouting developers Mail: project@pgrouting.org ------ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ********************************************************************PGR-GNU*/ #include #include std::string comma_separated(const std::vector &columns) { std::string result(" "); for (auto column: columns) { result += column + ","; } result[result.size() - 1] = ' '; return result; } std::string tab_separated(const std::vector &columns) { std::string result(" "); for (auto column: columns) { if (column.empty() || column == "") { result += "\\N\t"; } else { result += column + "\t"; } } result[result.size() - 1] = '\n'; return result; } osm2pgrouting-2.3.8/tools/000077500000000000000000000000001405641420700155065ustar00rootroot00000000000000osm2pgrouting-2.3.8/tools/cpplint.py000077500000000000000000007303651405641420700175520ustar00rootroot00000000000000#Copyright (c) 2009 Google Inc. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following disclaimer # in the documentation and/or other materials provided with the # distribution. # * Neither the name of Google Inc. nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """Does google-lint on c++ files. The goal of this script is to identify places in the code that *may* be in non-compliance with google style. It does not attempt to fix up these problems -- the point is to educate. It does also not attempt to find all problems, or to ensure that everything it does find is legitimately a problem. In particular, we can get very confused by /* and // inside strings! We do a small hack, which is to ignore //'s with "'s after them on the same line, but it is far from perfect (in either direction). """ import codecs import copy import getopt import math # for log import os import re import sre_compile import string import sys import unicodedata _USAGE = """ Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] [--counting=total|toplevel|detailed] [--root=subdir] [--linelength=digits] [file] ... The style guidelines this tries to follow are those in http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml Every problem is given a confidence score from 1-5, with 5 meaning we are certain of the problem, and 1 meaning it could be a legitimate construct. This will miss some errors, and is not a substitute for a code review. To suppress false-positive errors of a certain category, add a 'NOLINT(category)' comment to the line. NOLINT or NOLINT(*) suppresses errors of all categories on that line. The files passed in will be linted; at least one file must be provided. Default linted extensions are .cc, .cpp, .cu, .cuh and .h. Change the extensions with the --extensions flag. Flags: output=vs7 By default, the output is formatted to ease emacs parsing. Visual Studio compatible output (vs7) may also be used. Other formats are unsupported. verbose=# Specify a number 0-5 to restrict errors to certain verbosity levels. filter=-x,+y,... Specify a comma-separated list of category-filters to apply: only error messages whose category names pass the filters will be printed. (Category names are printed with the message and look like "[whitespace/indent]".) Filters are evaluated left to right. "-FOO" and "FOO" means "do not print categories that start with FOO". "+FOO" means "do print categories that start with FOO". Examples: --filter=-whitespace,+whitespace/braces --filter=whitespace,runtime/printf,+runtime/printf_format --filter=-,+build/include_what_you_use To see a list of all the categories used in cpplint, pass no arg: --filter= counting=total|toplevel|detailed The total number of errors found is always printed. If 'toplevel' is provided, then the count of errors in each of the top-level categories like 'build' and 'whitespace' will also be printed. If 'detailed' is provided, then a count is provided for each category like 'build/class'. root=subdir The root directory used for deriving header guard CPP variable. By default, the header guard CPP variable is calculated as the relative path to the directory that contains .git, .hg, or .svn. When this flag is specified, the relative path is calculated from the specified directory. If the specified directory does not exist, this flag is ignored. Examples: Assuming that src/.git exists, the header guard CPP variables for src/chrome/browser/ui/browser.h are: No flag => CHROME_BROWSER_UI_BROWSER_H_ --root=chrome => BROWSER_UI_BROWSER_H_ --root=chrome/browser => UI_BROWSER_H_ linelength=digits This is the allowed line length for the project. The default value is 80 characters. Examples: --linelength=120 extensions=extension,extension,... The allowed file extensions that cpplint will check Examples: --extensions=hpp,cpp cpplint.py supports per-directory configurations specified in CPPLINT.cfg files. CPPLINT.cfg file can contain a number of key=value pairs. Currently the following options are supported: set noparent filter=+filter1,-filter2,... exclude_files=regex linelength=80 "set noparent" option prevents cpplint from traversing directory tree upwards looking for more .cfg files in parent directories. This option is usually placed in the top-level project directory. The "filter" option is similar in function to --filter flag. It specifies message filters in addition to the |_DEFAULT_FILTERS| and those specified through --filter command-line flag. "exclude_files" allows to specify a regular expression to be matched against a file name. If the expression matches, the file is skipped and not run through liner. "linelength" allows to specify the allowed line length for the project. CPPLINT.cfg has an effect on files in the same directory and all sub-directories, unless overridden by a nested configuration file. Example file: filter=-build/include_order,+build/include_alpha exclude_files=.*\.cc The above example disables build/include_order warning and enables build/include_alpha as well as excludes all .cc from being processed by linter, in the current directory (where the .cfg file is located) and all sub-directories. """ # We categorize each error message we print. Here are the categories. # We want an explicit list so we can list them all in cpplint --filter=. # If you add a new error message with a new category, add it to the list # here! cpplint_unittest.py should tell you if you forget to do this. _ERROR_CATEGORIES = [ 'build/class', 'build/c++11', 'build/deprecated', 'build/endif_comment', 'build/explicit_make_pair', 'build/forward_decl', 'build/header_guard', 'build/include', 'build/include_alpha', 'build/include_order', 'build/include_what_you_use', 'build/namespaces', 'build/printf_format', 'build/storage_class', 'legal/copyright', 'readability/alt_tokens', 'readability/braces', 'readability/casting', 'readability/check', 'readability/constructors', 'readability/fn_size', 'readability/function', 'readability/inheritance', 'readability/multiline_comment', 'readability/multiline_string', 'readability/namespace', 'readability/nolint', 'readability/nul', 'readability/strings', 'readability/todo', 'readability/utf8', 'runtime/arrays', 'runtime/casting', 'runtime/explicit', 'runtime/int', 'runtime/init', 'runtime/invalid_increment', 'runtime/member_string_references', 'runtime/memset', 'runtime/indentation_namespace', 'runtime/operator', 'runtime/printf', 'runtime/printf_format', 'runtime/references', 'runtime/string', 'runtime/threadsafe_fn', 'runtime/vlog', 'whitespace/blank_line', 'whitespace/braces', 'whitespace/comma', 'whitespace/comments', 'whitespace/empty_conditional_body', 'whitespace/empty_loop_body', 'whitespace/end_of_line', 'whitespace/ending_newline', 'whitespace/forcolon', 'whitespace/indent', 'whitespace/line_length', 'whitespace/newline', 'whitespace/operators', 'whitespace/parens', 'whitespace/semicolon', 'whitespace/tab', 'whitespace/todo', ] # These error categories are no longer enforced by cpplint, but for backwards- # compatibility they may still appear in NOLINT comments. _LEGACY_ERROR_CATEGORIES = [ 'readability/streams', ] # The default state of the category filter. This is overridden by the --filter= # flag. By default all errors are on, so only add here categories that should be # off by default (i.e., categories that must be enabled by the --filter= flags). # All entries here should start with a '-' or '+', as in the --filter= flag. _DEFAULT_FILTERS = ['-build/include_alpha'] # We used to check for high-bit characters, but after much discussion we # decided those were OK, as long as they were in UTF-8 and didn't represent # hard-coded international strings, which belong in a separate i18n file. # C++ headers _CPP_HEADERS = frozenset([ # Legacy 'algobase.h', 'algo.h', 'alloc.h', 'builtinbuf.h', 'bvector.h', 'complex.h', 'defalloc.h', 'deque.h', 'editbuf.h', 'fstream.h', 'function.h', 'hash_map', 'hash_map.h', 'hash_set', 'hash_set.h', 'hashtable.h', 'heap.h', 'indstream.h', 'iomanip.h', 'iostream.h', 'istream.h', 'iterator.h', 'list.h', 'map.h', 'multimap.h', 'multiset.h', 'ostream.h', 'pair.h', 'parsestream.h', 'pfstream.h', 'procbuf.h', 'pthread_alloc', 'pthread_alloc.h', 'rope', 'rope.h', 'ropeimpl.h', 'set.h', 'slist', 'slist.h', 'stack.h', 'stdiostream.h', 'stl_alloc.h', 'stl_relops.h', 'streambuf.h', 'stream.h', 'strfile.h', 'strstream.h', 'tempbuf.h', 'tree.h', 'type_traits.h', 'vector.h', # 17.6.1.2 C++ library headers 'algorithm', 'array', 'atomic', 'bitset', 'chrono', 'codecvt', 'complex', 'condition_variable', 'deque', 'exception', 'forward_list', 'fstream', 'functional', 'future', 'initializer_list', 'iomanip', 'ios', 'iosfwd', 'iostream', 'istream', 'iterator', 'limits', 'list', 'locale', 'map', 'memory', 'mutex', 'new', 'numeric', 'ostream', 'queue', 'random', 'ratio', 'regex', 'set', 'sstream', 'stack', 'stdexcept', 'streambuf', 'string', 'strstream', 'system_error', 'thread', 'tuple', 'typeindex', 'typeinfo', 'type_traits', 'unordered_map', 'unordered_set', 'utility', 'valarray', 'vector', # 17.6.1.2 C++ headers for C library facilities 'cassert', 'ccomplex', 'cctype', 'cerrno', 'cfenv', 'cfloat', 'cinttypes', 'ciso646', 'climits', 'clocale', 'cmath', 'csetjmp', 'csignal', 'cstdalign', 'cstdarg', 'cstdbool', 'cstddef', 'cstdint', 'cstdio', 'cstdlib', 'cstring', 'ctgmath', 'ctime', 'cuchar', 'cwchar', 'cwctype', ]) # These headers are excluded from [build/include] and [build/include_order] # checks: # - Anything not following google file name conventions (containing an # uppercase character, such as Python.h or nsStringAPI.h, for example). # - Lua headers. _THIRD_PARTY_HEADERS_PATTERN = re.compile( r'^(?:[^/]*[A-Z][^/]*\.h|lua\.h|lauxlib\.h|lualib\.h)$') # Assertion macros. These are defined in base/logging.h and # testing/base/gunit.h. Note that the _M versions need to come first # for substring matching to work. _CHECK_MACROS = [ 'DCHECK', 'CHECK', 'EXPECT_TRUE_M', 'EXPECT_TRUE', 'ASSERT_TRUE_M', 'ASSERT_TRUE', 'EXPECT_FALSE_M', 'EXPECT_FALSE', 'ASSERT_FALSE_M', 'ASSERT_FALSE', ] # Replacement macros for CHECK/DCHECK/EXPECT_TRUE/EXPECT_FALSE _CHECK_REPLACEMENT = dict([(m, {}) for m in _CHECK_MACROS]) for op, replacement in [('==', 'EQ'), ('!=', 'NE'), ('>=', 'GE'), ('>', 'GT'), ('<=', 'LE'), ('<', 'LT')]: _CHECK_REPLACEMENT['DCHECK'][op] = 'DCHECK_%s' % replacement _CHECK_REPLACEMENT['CHECK'][op] = 'CHECK_%s' % replacement _CHECK_REPLACEMENT['EXPECT_TRUE'][op] = 'EXPECT_%s' % replacement _CHECK_REPLACEMENT['ASSERT_TRUE'][op] = 'ASSERT_%s' % replacement _CHECK_REPLACEMENT['EXPECT_TRUE_M'][op] = 'EXPECT_%s_M' % replacement _CHECK_REPLACEMENT['ASSERT_TRUE_M'][op] = 'ASSERT_%s_M' % replacement for op, inv_replacement in [('==', 'NE'), ('!=', 'EQ'), ('>=', 'LT'), ('>', 'LE'), ('<=', 'GT'), ('<', 'GE')]: _CHECK_REPLACEMENT['EXPECT_FALSE'][op] = 'EXPECT_%s' % inv_replacement _CHECK_REPLACEMENT['ASSERT_FALSE'][op] = 'ASSERT_%s' % inv_replacement _CHECK_REPLACEMENT['EXPECT_FALSE_M'][op] = 'EXPECT_%s_M' % inv_replacement _CHECK_REPLACEMENT['ASSERT_FALSE_M'][op] = 'ASSERT_%s_M' % inv_replacement # Alternative tokens and their replacements. For full list, see section 2.5 # Alternative tokens [lex.digraph] in the C++ standard. # # Digraphs (such as '%:') are not included here since it's a mess to # match those on a word boundary. _ALT_TOKEN_REPLACEMENT = { 'and': '&&', 'bitor': '|', 'or': '||', 'xor': '^', 'compl': '~', 'bitand': '&', 'and_eq': '&=', 'or_eq': '|=', 'xor_eq': '^=', 'not': '!', 'not_eq': '!=' } # Compile regular expression that matches all the above keywords. The "[ =()]" # bit is meant to avoid matching these keywords outside of boolean expressions. # # False positives include C-style multi-line comments and multi-line strings # but those have always been troublesome for cpplint. _ALT_TOKEN_REPLACEMENT_PATTERN = re.compile( r'[ =()](' + ('|'.join(_ALT_TOKEN_REPLACEMENT.keys())) + r')(?=[ (]|$)') # These constants define types of headers for use with # _IncludeState.CheckNextIncludeOrder(). _C_SYS_HEADER = 1 _CPP_SYS_HEADER = 2 _LIKELY_MY_HEADER = 3 _POSSIBLE_MY_HEADER = 4 _OTHER_HEADER = 5 # These constants define the current inline assembly state _NO_ASM = 0 # Outside of inline assembly block _INSIDE_ASM = 1 # Inside inline assembly block _END_ASM = 2 # Last line of inline assembly block _BLOCK_ASM = 3 # The whole block is an inline assembly block # Match start of assembly blocks _MATCH_ASM = re.compile(r'^\s*(?:asm|_asm|__asm|__asm__)' r'(?:\s+(volatile|__volatile__))?' r'\s*[{(]') _regexp_compile_cache = {} # {str, set(int)}: a map from error categories to sets of linenumbers # on which those errors are expected and should be suppressed. _error_suppressions = {} # The root directory used for deriving header guard CPP variable. # This is set by --root flag. _root = None # The allowed line length of files. # This is set by --linelength flag. _line_length = 80 # The allowed extensions for file names # This is set by --extensions flag. _valid_extensions = set(['cc', 'h', 'cpp', 'cu', 'cuh', 'c', 'hpp']) def ParseNolintSuppressions(filename, raw_line, linenum, error): """Updates the global list of error-suppressions. Parses any NOLINT comments on the current line, updating the global error_suppressions store. Reports an error if the NOLINT comment was malformed. Args: filename: str, the name of the input file. raw_line: str, the line of input text, with comments. linenum: int, the number of the current line. error: function, an error handler. """ matched = Search(r'\bNOLINT(NEXTLINE)?\b(\([^)]+\))?', raw_line) if matched: if matched.group(1): suppressed_line = linenum + 1 else: suppressed_line = linenum category = matched.group(2) if category in (None, '(*)'): # => "suppress all" _error_suppressions.setdefault(None, set()).add(suppressed_line) else: if category.startswith('(') and category.endswith(')'): category = category[1:-1] if category in _ERROR_CATEGORIES: _error_suppressions.setdefault(category, set()).add(suppressed_line) elif category not in _LEGACY_ERROR_CATEGORIES: error(filename, linenum, 'readability/nolint', 5, 'Unknown NOLINT error category: %s' % category) def ResetNolintSuppressions(): """Resets the set of NOLINT suppressions to empty.""" _error_suppressions.clear() def IsErrorSuppressedByNolint(category, linenum): """Returns true if the specified error category is suppressed on this line. Consults the global error_suppressions map populated by ParseNolintSuppressions/ResetNolintSuppressions. Args: category: str, the category of the error. linenum: int, the current line number. Returns: bool, True iff the error should be suppressed due to a NOLINT comment. """ return (linenum in _error_suppressions.get(category, set()) or linenum in _error_suppressions.get(None, set())) def Match(pattern, s): """Matches the string with the pattern, caching the compiled regexp.""" # The regexp compilation caching is inlined in both Match and Search for # performance reasons; factoring it out into a separate function turns out # to be noticeably expensive. if pattern not in _regexp_compile_cache: _regexp_compile_cache[pattern] = sre_compile.compile(pattern) return _regexp_compile_cache[pattern].match(s) def ReplaceAll(pattern, rep, s): """Replaces instances of pattern in a string with a replacement. The compiled regex is kept in a cache shared by Match and Search. Args: pattern: regex pattern rep: replacement text s: search string Returns: string with replacements made (or original string if no replacements) """ if pattern not in _regexp_compile_cache: _regexp_compile_cache[pattern] = sre_compile.compile(pattern) return _regexp_compile_cache[pattern].sub(rep, s) def Search(pattern, s): """Searches the string for the pattern, caching the compiled regexp.""" if pattern not in _regexp_compile_cache: _regexp_compile_cache[pattern] = sre_compile.compile(pattern) return _regexp_compile_cache[pattern].search(s) class _IncludeState(object): """Tracks line numbers for includes, and the order in which includes appear. include_list contains list of lists of (header, line number) pairs. It's a lists of lists rather than just one flat list to make it easier to update across preprocessor boundaries. Call CheckNextIncludeOrder() once for each header in the file, passing in the type constants defined above. Calls in an illegal order will raise an _IncludeError with an appropriate error message. """ # self._section will move monotonically through this set. If it ever # needs to move backwards, CheckNextIncludeOrder will raise an error. _INITIAL_SECTION = 0 _MY_H_SECTION = 1 _C_SECTION = 2 _CPP_SECTION = 3 _OTHER_H_SECTION = 4 _TYPE_NAMES = { _C_SYS_HEADER: 'C system header', _CPP_SYS_HEADER: 'C++ system header', _LIKELY_MY_HEADER: 'header this file implements', _POSSIBLE_MY_HEADER: 'header this file may implement', _OTHER_HEADER: 'other header', } _SECTION_NAMES = { _INITIAL_SECTION: "... nothing. (This can't be an error.)", _MY_H_SECTION: 'a header this file implements', _C_SECTION: 'C system header', _CPP_SECTION: 'C++ system header', _OTHER_H_SECTION: 'other header', } def __init__(self): self.include_list = [[]] self.ResetSection('') def FindHeader(self, header): """Check if a header has already been included. Args: header: header to check. Returns: Line number of previous occurrence, or -1 if the header has not been seen before. """ for section_list in self.include_list: for f in section_list: if f[0] == header: return f[1] return -1 def ResetSection(self, directive): """Reset section checking for preprocessor directive. Args: directive: preprocessor directive (e.g. "if", "else"). """ # The name of the current section. self._section = self._INITIAL_SECTION # The path of last found header. self._last_header = '' # Update list of includes. Note that we never pop from the # include list. if directive in ('if', 'ifdef', 'ifndef'): self.include_list.append([]) elif directive in ('else', 'elif'): self.include_list[-1] = [] def SetLastHeader(self, header_path): self._last_header = header_path def CanonicalizeAlphabeticalOrder(self, header_path): """Returns a path canonicalized for alphabetical comparison. - replaces "-" with "_" so they both cmp the same. - removes '-inl' since we don't require them to be after the main header. - lowercase everything, just in case. Args: header_path: Path to be canonicalized. Returns: Canonicalized path. """ return header_path.replace('-inl.h', '.h').replace('-', '_').lower() def IsInAlphabeticalOrder(self, clean_lines, linenum, header_path): """Check if a header is in alphabetical order with the previous header. Args: clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. header_path: Canonicalized header to be checked. Returns: Returns true if the header is in alphabetical order. """ # If previous section is different from current section, _last_header will # be reset to empty string, so it's always less than current header. # # If previous line was a blank line, assume that the headers are # intentionally sorted the way they are. if (self._last_header > header_path and Match(r'^\s*#\s*include\b', clean_lines.elided[linenum - 1])): return False return True def CheckNextIncludeOrder(self, header_type): """Returns a non-empty error message if the next header is out of order. This function also updates the internal state to be ready to check the next include. Args: header_type: One of the _XXX_HEADER constants defined above. Returns: The empty string if the header is in the right order, or an error message describing what's wrong. """ error_message = ('Found %s after %s' % (self._TYPE_NAMES[header_type], self._SECTION_NAMES[self._section])) last_section = self._section if header_type == _C_SYS_HEADER: if self._section <= self._C_SECTION: self._section = self._C_SECTION else: self._last_header = '' return error_message elif header_type == _CPP_SYS_HEADER: if self._section <= self._CPP_SECTION: self._section = self._CPP_SECTION else: self._last_header = '' return error_message elif header_type == _LIKELY_MY_HEADER: if self._section <= self._MY_H_SECTION: self._section = self._MY_H_SECTION else: self._section = self._OTHER_H_SECTION elif header_type == _POSSIBLE_MY_HEADER: if self._section <= self._MY_H_SECTION: self._section = self._MY_H_SECTION else: # This will always be the fallback because we're not sure # enough that the header is associated with this file. self._section = self._OTHER_H_SECTION else: assert header_type == _OTHER_HEADER self._section = self._OTHER_H_SECTION if last_section != self._section: self._last_header = '' return '' class _CppLintState(object): """Maintains module-wide state..""" def __init__(self): self.verbose_level = 1 # global setting. self.error_count = 0 # global count of reported errors # filters to apply when emitting error messages self.filters = _DEFAULT_FILTERS[:] # backup of filter list. Used to restore the state after each file. self._filters_backup = self.filters[:] self.counting = 'total' # In what way are we counting errors? self.errors_by_category = {} # string to int dict storing error counts # output format: # "emacs" - format that emacs can parse (default) # "vs7" - format that Microsoft Visual Studio 7 can parse self.output_format = 'emacs' def SetOutputFormat(self, output_format): """Sets the output format for errors.""" self.output_format = output_format def SetVerboseLevel(self, level): """Sets the module's verbosity, and returns the previous setting.""" last_verbose_level = self.verbose_level self.verbose_level = level return last_verbose_level def SetCountingStyle(self, counting_style): """Sets the module's counting options.""" self.counting = counting_style def SetFilters(self, filters): """Sets the error-message filters. These filters are applied when deciding whether to emit a given error message. Args: filters: A string of comma-separated filters (eg "+whitespace/indent"). Each filter should start with + or -; else we die. Raises: ValueError: The comma-separated filters did not all start with '+' or '-'. E.g. "-,+whitespace,-whitespace/indent,whitespace/badfilter" """ # Default filters always have less priority than the flag ones. self.filters = _DEFAULT_FILTERS[:] self.AddFilters(filters) def AddFilters(self, filters): """ Adds more filters to the existing list of error-message filters. """ for filt in filters.split(','): clean_filt = filt.strip() if clean_filt: self.filters.append(clean_filt) for filt in self.filters: if not (filt.startswith('+') or filt.startswith('-')): raise ValueError('Every filter in --filters must start with + or -' ' (%s does not)' % filt) def BackupFilters(self): """ Saves the current filter list to backup storage.""" self._filters_backup = self.filters[:] def RestoreFilters(self): """ Restores filters previously backed up.""" self.filters = self._filters_backup[:] def ResetErrorCounts(self): """Sets the module's error statistic back to zero.""" self.error_count = 0 self.errors_by_category = {} def IncrementErrorCount(self, category): """Bumps the module's error statistic.""" self.error_count += 1 if self.counting in ('toplevel', 'detailed'): if self.counting != 'detailed': category = category.split('/')[0] if category not in self.errors_by_category: self.errors_by_category[category] = 0 self.errors_by_category[category] += 1 def PrintErrorCounts(self): """Print a summary of errors by category, and the total.""" for category, count in self.errors_by_category.iteritems(): sys.stderr.write('Category \'%s\' errors found: %d\n' % (category, count)) sys.stderr.write('Total errors found: %d\n' % self.error_count) _cpplint_state = _CppLintState() def _OutputFormat(): """Gets the module's output format.""" return _cpplint_state.output_format def _SetOutputFormat(output_format): """Sets the module's output format.""" _cpplint_state.SetOutputFormat(output_format) def _VerboseLevel(): """Returns the module's verbosity setting.""" return _cpplint_state.verbose_level def _SetVerboseLevel(level): """Sets the module's verbosity, and returns the previous setting.""" return _cpplint_state.SetVerboseLevel(level) def _SetCountingStyle(level): """Sets the module's counting options.""" _cpplint_state.SetCountingStyle(level) def _Filters(): """Returns the module's list of output filters, as a list.""" return _cpplint_state.filters def _SetFilters(filters): """Sets the module's error-message filters. These filters are applied when deciding whether to emit a given error message. Args: filters: A string of comma-separated filters (eg "whitespace/indent"). Each filter should start with + or -; else we die. """ _cpplint_state.SetFilters(filters) def _AddFilters(filters): """Adds more filter overrides. Unlike _SetFilters, this function does not reset the current list of filters available. Args: filters: A string of comma-separated filters (eg "whitespace/indent"). Each filter should start with + or -; else we die. """ _cpplint_state.AddFilters(filters) def _BackupFilters(): """ Saves the current filter list to backup storage.""" _cpplint_state.BackupFilters() def _RestoreFilters(): """ Restores filters previously backed up.""" _cpplint_state.RestoreFilters() class _FunctionState(object): """Tracks current function name and the number of lines in its body.""" _NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc. _TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER. def __init__(self): self.in_a_function = False self.lines_in_function = 0 self.current_function = '' def Begin(self, function_name): """Start analyzing function body. Args: function_name: The name of the function being tracked. """ self.in_a_function = True self.lines_in_function = 0 self.current_function = function_name def Count(self): """Count line in current function body.""" if self.in_a_function: self.lines_in_function += 1 def Check(self, error, filename, linenum): """Report if too many lines in function body. Args: error: The function to call with any errors found. filename: The name of the current file. linenum: The number of the line to check. """ if Match(r'T(EST|est)', self.current_function): base_trigger = self._TEST_TRIGGER else: base_trigger = self._NORMAL_TRIGGER trigger = base_trigger * 2**_VerboseLevel() if self.lines_in_function > trigger: error_level = int(math.log(self.lines_in_function / base_trigger, 2)) # 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ... if error_level > 5: error_level = 5 error(filename, linenum, 'readability/fn_size', error_level, 'Small and focused functions are preferred:' ' %s has %d non-comment lines' ' (error triggered by exceeding %d lines).' % ( self.current_function, self.lines_in_function, trigger)) def End(self): """Stop analyzing function body.""" self.in_a_function = False class _IncludeError(Exception): """Indicates a problem with the include order in a file.""" pass class FileInfo(object): """Provides utility functions for filenames. FileInfo provides easy access to the components of a file's path relative to the project root. """ def __init__(self, filename): self._filename = filename def FullName(self): """Make Windows paths like Unix.""" return os.path.abspath(self._filename).replace('\\', '/') def RepositoryName(self): """FullName after removing the local path to the repository. If we have a real absolute path name here we can try to do something smart: detecting the root of the checkout and truncating /path/to/checkout from the name so that we get header guards that don't include things like "C:\Documents and Settings\..." or "/home/username/..." in them and thus people on different computers who have checked the source out to different locations won't see bogus errors. """ fullname = self.FullName() if os.path.exists(fullname): project_dir = os.path.dirname(fullname) if os.path.exists(os.path.join(project_dir, ".svn")): # If there's a .svn file in the current directory, we recursively look # up the directory tree for the top of the SVN checkout root_dir = project_dir one_up_dir = os.path.dirname(root_dir) while os.path.exists(os.path.join(one_up_dir, ".svn")): root_dir = os.path.dirname(root_dir) one_up_dir = os.path.dirname(one_up_dir) prefix = os.path.commonprefix([root_dir, project_dir]) return fullname[len(prefix) + 1:] # Not SVN <= 1.6? Try to find a git, hg, or svn top level directory by # searching up from the current path. root_dir = os.path.dirname(fullname) while (root_dir != os.path.dirname(root_dir) and not os.path.exists(os.path.join(root_dir, ".git")) and not os.path.exists(os.path.join(root_dir, ".hg")) and not os.path.exists(os.path.join(root_dir, ".svn"))): root_dir = os.path.dirname(root_dir) if (os.path.exists(os.path.join(root_dir, ".git")) or os.path.exists(os.path.join(root_dir, ".hg")) or os.path.exists(os.path.join(root_dir, ".svn"))): prefix = os.path.commonprefix([root_dir, project_dir]) return fullname[len(prefix) + 1:] # Don't know what to do; header guard warnings may be wrong... return fullname def Split(self): """Splits the file into the directory, basename, and extension. For 'chrome/browser/browser.cc', Split() would return ('chrome/browser', 'browser', '.cc') Returns: A tuple of (directory, basename, extension). """ googlename = self.RepositoryName() project, rest = os.path.split(googlename) return (project,) + os.path.splitext(rest) def BaseName(self): """File base name - text after the final slash, before the final period.""" return self.Split()[1] def Extension(self): """File extension - text following the final period.""" return self.Split()[2] def NoExtension(self): """File has no source file extension.""" return '/'.join(self.Split()[0:2]) def IsSource(self): """File has a source file extension.""" return self.Extension()[1:] in ('c', 'cc', 'cpp', 'cxx') def _ShouldPrintError(category, confidence, linenum): """If confidence >= verbose, category passes filter and is not suppressed.""" # There are three ways we might decide not to print an error message: # a "NOLINT(category)" comment appears in the source, # the verbosity level isn't high enough, or the filters filter it out. if IsErrorSuppressedByNolint(category, linenum): return False if confidence < _cpplint_state.verbose_level: return False is_filtered = False for one_filter in _Filters(): if one_filter.startswith('-'): if category.startswith(one_filter[1:]): is_filtered = True elif one_filter.startswith('+'): if category.startswith(one_filter[1:]): is_filtered = False else: assert False # should have been checked for in SetFilter. if is_filtered: return False return True def Error(filename, linenum, category, confidence, message): """Logs the fact we've found a lint error. We log where the error was found, and also our confidence in the error, that is, how certain we are this is a legitimate style regression, and not a misidentification or a use that's sometimes justified. False positives can be suppressed by the use of "cpplint(category)" comments on the offending line. These are parsed into _error_suppressions. Args: filename: The name of the file containing the error. linenum: The number of the line containing the error. category: A string used to describe the "category" this bug falls under: "whitespace", say, or "runtime". Categories may have a hierarchy separated by slashes: "whitespace/indent". confidence: A number from 1-5 representing a confidence score for the error, with 5 meaning that we are certain of the problem, and 1 meaning that it could be a legitimate construct. message: The error message. """ if _ShouldPrintError(category, confidence, linenum): _cpplint_state.IncrementErrorCount(category) if _cpplint_state.output_format == 'vs7': sys.stderr.write('%s(%s): %s [%s] [%d]\n' % ( filename, linenum, message, category, confidence)) elif _cpplint_state.output_format == 'eclipse': sys.stderr.write('%s:%s: warning: %s [%s] [%d]\n' % ( filename, linenum, message, category, confidence)) else: sys.stderr.write('%s:%s: %s [%s] [%d]\n' % ( filename, linenum, message, category, confidence)) # Matches standard C++ escape sequences per 2.13.2.3 of the C++ standard. _RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile( r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)') # Match a single C style comment on the same line. _RE_PATTERN_C_COMMENTS = r'/\*(?:[^*]|\*(?!/))*\*/' # Matches multi-line C style comments. # This RE is a little bit more complicated than one might expect, because we # have to take care of space removals tools so we can handle comments inside # statements better. # The current rule is: We only clear spaces from both sides when we're at the # end of the line. Otherwise, we try to remove spaces from the right side, # if this doesn't work we try on left side but only if there's a non-character # on the right. _RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile( r'(\s*' + _RE_PATTERN_C_COMMENTS + r'\s*$|' + _RE_PATTERN_C_COMMENTS + r'\s+|' + r'\s+' + _RE_PATTERN_C_COMMENTS + r'(?=\W)|' + _RE_PATTERN_C_COMMENTS + r')') def IsCppString(line): """Does line terminate so, that the next symbol is in string constant. This function does not consider single-line nor multi-line comments. Args: line: is a partial line of code starting from the 0..n. Returns: True, if next character appended to 'line' is inside a string constant. """ line = line.replace(r'\\', 'XX') # after this, \\" does not match to \" return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1 def CleanseRawStrings(raw_lines): """Removes C++11 raw strings from lines. Before: static const char kData[] = R"( multi-line string )"; After: static const char kData[] = "" (replaced by blank line) ""; Args: raw_lines: list of raw lines. Returns: list of lines with C++11 raw strings replaced by empty strings. """ delimiter = None lines_without_raw_strings = [] for line in raw_lines: if delimiter: # Inside a raw string, look for the end end = line.find(delimiter) if end >= 0: # Found the end of the string, match leading space for this # line and resume copying the original lines, and also insert # a "" on the last line. leading_space = Match(r'^(\s*)\S', line) line = leading_space.group(1) + '""' + line[end + len(delimiter):] delimiter = None else: # Haven't found the end yet, append a blank line. line = '""' # Look for beginning of a raw string, and replace them with # empty strings. This is done in a loop to handle multiple raw # strings on the same line. while delimiter is None: # Look for beginning of a raw string. # See 2.14.15 [lex.string] for syntax. matched = Match(r'^(.*)\b(?:R|u8R|uR|UR|LR)"([^\s\\()]*)\((.*)$', line) if matched: delimiter = ')' + matched.group(2) + '"' end = matched.group(3).find(delimiter) if end >= 0: # Raw string ended on same line line = (matched.group(1) + '""' + matched.group(3)[end + len(delimiter):]) delimiter = None else: # Start of a multi-line raw string line = matched.group(1) + '""' else: break lines_without_raw_strings.append(line) # TODO(unknown): if delimiter is not None here, we might want to # emit a warning for unterminated string. return lines_without_raw_strings def FindNextMultiLineCommentStart(lines, lineix): """Find the beginning marker for a multiline comment.""" while lineix < len(lines): if lines[lineix].strip().startswith('/*'): # Only return this marker if the comment goes beyond this line if lines[lineix].strip().find('*/', 2) < 0: return lineix lineix += 1 return len(lines) def FindNextMultiLineCommentEnd(lines, lineix): """We are inside a comment, find the end marker.""" while lineix < len(lines): if lines[lineix].strip().endswith('*/'): return lineix lineix += 1 return len(lines) def RemoveMultiLineCommentsFromRange(lines, begin, end): """Clears a range of lines for multi-line comments.""" # Having // dummy comments makes the lines non-empty, so we will not get # unnecessary blank line warnings later in the code. for i in range(begin, end): lines[i] = '/**/' def RemoveMultiLineComments(filename, lines, error): """Removes multiline (c-style) comments from lines.""" lineix = 0 while lineix < len(lines): lineix_begin = FindNextMultiLineCommentStart(lines, lineix) if lineix_begin >= len(lines): return lineix_end = FindNextMultiLineCommentEnd(lines, lineix_begin) if lineix_end >= len(lines): error(filename, lineix_begin + 1, 'readability/multiline_comment', 5, 'Could not find end of multi-line comment') return RemoveMultiLineCommentsFromRange(lines, lineix_begin, lineix_end + 1) lineix = lineix_end + 1 def CleanseComments(line): """Removes //-comments and single-line C-style /* */ comments. Args: line: A line of C++ source. Returns: The line with single-line comments removed. """ commentpos = line.find('//') if commentpos != -1 and not IsCppString(line[:commentpos]): line = line[:commentpos].rstrip() # get rid of /* ... */ return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line) class CleansedLines(object): """Holds 4 copies of all lines with different preprocessing applied to them. 1) elided member contains lines without strings and comments. 2) lines member contains lines without comments. 3) raw_lines member contains all the lines without processing. 4) lines_without_raw_strings member is same as raw_lines, but with C++11 raw strings removed. All these members are of , and of the same length. """ def __init__(self, lines): self.elided = [] self.lines = [] self.raw_lines = lines self.num_lines = len(lines) self.lines_without_raw_strings = CleanseRawStrings(lines) for linenum in range(len(self.lines_without_raw_strings)): self.lines.append(CleanseComments( self.lines_without_raw_strings[linenum])) elided = self._CollapseStrings(self.lines_without_raw_strings[linenum]) self.elided.append(CleanseComments(elided)) def NumLines(self): """Returns the number of lines represented.""" return self.num_lines @staticmethod def _CollapseStrings(elided): """Collapses strings and chars on a line to simple "" or '' blocks. We nix strings first so we're not fooled by text like '"http://"' Args: elided: The line being processed. Returns: The line with collapsed strings. """ if _RE_PATTERN_INCLUDE.match(elided): return elided # Remove escaped characters first to make quote/single quote collapsing # basic. Things that look like escaped characters shouldn't occur # outside of strings and chars. elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub('', elided) # Replace quoted strings and digit separators. Both single quotes # and double quotes are processed in the same loop, otherwise # nested quotes wouldn't work. collapsed = '' while True: # Find the first quote character match = Match(r'^([^\'"]*)([\'"])(.*)$', elided) if not match: collapsed += elided break head, quote, tail = match.groups() if quote == '"': # Collapse double quoted strings second_quote = tail.find('"') if second_quote >= 0: collapsed += head + '""' elided = tail[second_quote + 1:] else: # Unmatched double quote, don't bother processing the rest # of the line since this is probably a multiline string. collapsed += elided break else: # Found single quote, check nearby text to eliminate digit separators. # # There is no special handling for floating point here, because # the integer/fractional/exponent parts would all be parsed # correctly as long as there are digits on both sides of the # separator. So we are fine as long as we don't see something # like "0.'3" (gcc 4.9.0 will not allow this literal). if Search(r'\b(?:0[bBxX]?|[1-9])[0-9a-fA-F]*$', head): match_literal = Match(r'^((?:\'?[0-9a-zA-Z_])*)(.*)$', "'" + tail) collapsed += head + match_literal.group(1).replace("'", '') elided = match_literal.group(2) else: second_quote = tail.find('\'') if second_quote >= 0: collapsed += head + "''" elided = tail[second_quote + 1:] else: # Unmatched single quote collapsed += elided break return collapsed def FindEndOfExpressionInLine(line, startpos, stack): """Find the position just after the end of current parenthesized expression. Args: line: a CleansedLines line. startpos: start searching at this position. stack: nesting stack at startpos. Returns: On finding matching end: (index just after matching end, None) On finding an unclosed expression: (-1, None) Otherwise: (-1, new stack at end of this line) """ for i in xrange(startpos, len(line)): char = line[i] if char in '([{': # Found start of parenthesized expression, push to expression stack stack.append(char) elif char == '<': # Found potential start of template argument list if i > 0 and line[i - 1] == '<': # Left shift operator if stack and stack[-1] == '<': stack.pop() if not stack: return (-1, None) elif i > 0 and Search(r'\boperator\s*$', line[0:i]): # operator<, don't add to stack continue else: # Tentative start of template argument list stack.append('<') elif char in ')]}': # Found end of parenthesized expression. # # If we are currently expecting a matching '>', the pending '<' # must have been an operator. Remove them from expression stack. while stack and stack[-1] == '<': stack.pop() if not stack: return (-1, None) if ((stack[-1] == '(' and char == ')') or (stack[-1] == '[' and char == ']') or (stack[-1] == '{' and char == '}')): stack.pop() if not stack: return (i + 1, None) else: # Mismatched parentheses return (-1, None) elif char == '>': # Found potential end of template argument list. # Ignore "->" and operator functions if (i > 0 and (line[i - 1] == '-' or Search(r'\boperator\s*$', line[0:i - 1]))): continue # Pop the stack if there is a matching '<'. Otherwise, ignore # this '>' since it must be an operator. if stack: if stack[-1] == '<': stack.pop() if not stack: return (i + 1, None) elif char == ';': # Found something that look like end of statements. If we are currently # expecting a '>', the matching '<' must have been an operator, since # template argument list should not contain statements. while stack and stack[-1] == '<': stack.pop() if not stack: return (-1, None) # Did not find end of expression or unbalanced parentheses on this line return (-1, stack) def CloseExpression(clean_lines, linenum, pos): """If input points to ( or { or [ or <, finds the position that closes it. If lines[linenum][pos] points to a '(' or '{' or '[' or '<', finds the linenum/pos that correspond to the closing of the expression. TODO(unknown): cpplint spends a fair bit of time matching parentheses. Ideally we would want to index all opening and closing parentheses once and have CloseExpression be just a simple lookup, but due to preprocessor tricks, this is not so easy. Args: clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. pos: A position on the line. Returns: A tuple (line, linenum, pos) pointer *past* the closing brace, or (line, len(lines), -1) if we never find a close. Note we ignore strings and comments when matching; and the line we return is the 'cleansed' line at linenum. """ line = clean_lines.elided[linenum] if (line[pos] not in '({[<') or Match(r'<[<=]', line[pos:]): return (line, clean_lines.NumLines(), -1) # Check first line (end_pos, stack) = FindEndOfExpressionInLine(line, pos, []) if end_pos > -1: return (line, linenum, end_pos) # Continue scanning forward while stack and linenum < clean_lines.NumLines() - 1: linenum += 1 line = clean_lines.elided[linenum] (end_pos, stack) = FindEndOfExpressionInLine(line, 0, stack) if end_pos > -1: return (line, linenum, end_pos) # Did not find end of expression before end of file, give up return (line, clean_lines.NumLines(), -1) def FindStartOfExpressionInLine(line, endpos, stack): """Find position at the matching start of current expression. This is almost the reverse of FindEndOfExpressionInLine, but note that the input position and returned position differs by 1. Args: line: a CleansedLines line. endpos: start searching at this position. stack: nesting stack at endpos. Returns: On finding matching start: (index at matching start, None) On finding an unclosed expression: (-1, None) Otherwise: (-1, new stack at beginning of this line) """ i = endpos while i >= 0: char = line[i] if char in ')]}': # Found end of expression, push to expression stack stack.append(char) elif char == '>': # Found potential end of template argument list. # # Ignore it if it's a "->" or ">=" or "operator>" if (i > 0 and (line[i - 1] == '-' or Match(r'\s>=\s', line[i - 1:]) or Search(r'\boperator\s*$', line[0:i]))): i -= 1 else: stack.append('>') elif char == '<': # Found potential start of template argument list if i > 0 and line[i - 1] == '<': # Left shift operator i -= 1 else: # If there is a matching '>', we can pop the expression stack. # Otherwise, ignore this '<' since it must be an operator. if stack and stack[-1] == '>': stack.pop() if not stack: return (i, None) elif char in '([{': # Found start of expression. # # If there are any unmatched '>' on the stack, they must be # operators. Remove those. while stack and stack[-1] == '>': stack.pop() if not stack: return (-1, None) if ((char == '(' and stack[-1] == ')') or (char == '[' and stack[-1] == ']') or (char == '{' and stack[-1] == '}')): stack.pop() if not stack: return (i, None) else: # Mismatched parentheses return (-1, None) elif char == ';': # Found something that look like end of statements. If we are currently # expecting a '<', the matching '>' must have been an operator, since # template argument list should not contain statements. while stack and stack[-1] == '>': stack.pop() if not stack: return (-1, None) i -= 1 return (-1, stack) def ReverseCloseExpression(clean_lines, linenum, pos): """If input points to ) or } or ] or >, finds the position that opens it. If lines[linenum][pos] points to a ')' or '}' or ']' or '>', finds the linenum/pos that correspond to the opening of the expression. Args: clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. pos: A position on the line. Returns: A tuple (line, linenum, pos) pointer *at* the opening brace, or (line, 0, -1) if we never find the matching opening brace. Note we ignore strings and comments when matching; and the line we return is the 'cleansed' line at linenum. """ line = clean_lines.elided[linenum] if line[pos] not in ')}]>': return (line, 0, -1) # Check last line (start_pos, stack) = FindStartOfExpressionInLine(line, pos, []) if start_pos > -1: return (line, linenum, start_pos) # Continue scanning backward while stack and linenum > 0: linenum -= 1 line = clean_lines.elided[linenum] (start_pos, stack) = FindStartOfExpressionInLine(line, len(line) - 1, stack) if start_pos > -1: return (line, linenum, start_pos) # Did not find start of expression before beginning of file, give up return (line, 0, -1) def CheckForCopyright(filename, lines, error): """Logs an error if no Copyright message appears at the top of the file.""" # We'll say it should occur by line 10. Don't forget there's a # dummy line at the front. for line in xrange(1, min(len(lines), 11)): if re.search(r'Copyright', lines[line], re.I): break else: # means no copyright line was found error(filename, 0, 'legal/copyright', 5, 'No copyright message found. ' 'You should have a line: "Copyright [year] "') def GetIndentLevel(line): """Return the number of leading spaces in line. Args: line: A string to check. Returns: An integer count of leading spaces, possibly zero. """ indent = Match(r'^( *)\S', line) if indent: return len(indent.group(1)) else: return 0 def GetHeaderGuardCPPVariable(filename): """Returns the CPP variable that should be used as a header guard. Args: filename: The name of a C++ header file. Returns: The CPP variable that should be used as a header guard in the named file. """ # Restores original filename in case that cpplint is invoked from Emacs's # flymake. filename = re.sub(r'_flymake\.h$', '.h', filename) filename = re.sub(r'/\.flymake/([^/]*)$', r'/\1', filename) # Replace 'c++' with 'cpp'. filename = filename.replace('C++', 'cpp').replace('c++', 'cpp') fileinfo = FileInfo(filename) file_path_from_root = fileinfo.RepositoryName() if _root: file_path_from_root = re.sub('^' + _root + os.sep, '', file_path_from_root) return re.sub(r'[^a-zA-Z0-9]', '_', file_path_from_root).upper() + '_' def CheckForHeaderGuard(filename, clean_lines, error): """Checks that the file contains a header guard. Logs an error if no #ifndef header guard is present. For other headers, checks that the full pathname is used. Args: filename: The name of the C++ header file. clean_lines: A CleansedLines instance containing the file. error: The function to call with any errors found. """ # Don't check for header guards if there are error suppression # comments somewhere in this file. # # Because this is silencing a warning for a nonexistent line, we # only support the very specific NOLINT(build/header_guard) syntax, # and not the general NOLINT or NOLINT(*) syntax. raw_lines = clean_lines.lines_without_raw_strings for i in raw_lines: if Search(r'//\s*NOLINT\(build/header_guard\)', i): return cppvar = GetHeaderGuardCPPVariable(filename) ifndef = '' ifndef_linenum = 0 define = '' endif = '' endif_linenum = 0 for linenum, line in enumerate(raw_lines): linesplit = line.split() if len(linesplit) >= 2: # find the first occurrence of #ifndef and #define, save arg if not ifndef and linesplit[0] == '#ifndef': # set ifndef to the header guard presented on the #ifndef line. ifndef = linesplit[1] ifndef_linenum = linenum if not define and linesplit[0] == '#define': define = linesplit[1] # find the last occurrence of #endif, save entire line if line.startswith('#endif'): endif = line endif_linenum = linenum if not ifndef or not define or ifndef != define: error(filename, 0, 'build/header_guard', 5, 'No #ifndef header guard found, suggested CPP variable is: %s' % cppvar) return # The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__ # for backward compatibility. if ifndef != cppvar: error_level = 0 if ifndef != cppvar + '_': error_level = 5 ParseNolintSuppressions(filename, raw_lines[ifndef_linenum], ifndef_linenum, error) error(filename, ifndef_linenum, 'build/header_guard', error_level, '#ifndef header guard has wrong style, please use: %s' % cppvar) # Check for "//" comments on endif line. ParseNolintSuppressions(filename, raw_lines[endif_linenum], endif_linenum, error) match = Match(r'#endif\s*//\s*' + cppvar + r'(_)?\b', endif) if match: if match.group(1) == '_': # Issue low severity warning for deprecated double trailing underscore error(filename, endif_linenum, 'build/header_guard', 0, '#endif line should be "#endif // %s"' % cppvar) return # Didn't find the corresponding "//" comment. If this file does not # contain any "//" comments at all, it could be that the compiler # only wants "/**/" comments, look for those instead. no_single_line_comments = True for i in xrange(1, len(raw_lines) - 1): line = raw_lines[i] if Match(r'^(?:(?:\'(?:\.|[^\'])*\')|(?:"(?:\.|[^"])*")|[^\'"])*//', line): no_single_line_comments = False break if no_single_line_comments: match = Match(r'#endif\s*/\*\s*' + cppvar + r'(_)?\s*\*/', endif) if match: if match.group(1) == '_': # Low severity warning for double trailing underscore error(filename, endif_linenum, 'build/header_guard', 0, '#endif line should be "#endif /* %s */"' % cppvar) return # Didn't find anything error(filename, endif_linenum, 'build/header_guard', 5, '#endif line should be "#endif // %s"' % cppvar) def CheckHeaderFileIncluded(filename, include_state, error): """Logs an error if a .cc file does not include its header.""" # Do not check test files if filename.endswith('_test.cc') or filename.endswith('_unittest.cc'): return fileinfo = FileInfo(filename) headerfile = filename[0:len(filename) - 2] + 'h' if not os.path.exists(headerfile): return headername = FileInfo(headerfile).RepositoryName() first_include = 0 for section_list in include_state.include_list: for f in section_list: if headername in f[0] or f[0] in headername: return if not first_include: first_include = f[1] error(filename, first_include, 'build/include', 5, '%s should include its header file %s' % (fileinfo.RepositoryName(), headername)) def CheckForBadCharacters(filename, lines, error): """Logs an error for each line containing bad characters. Two kinds of bad characters: 1. Unicode replacement characters: These indicate that either the file contained invalid UTF-8 (likely) or Unicode replacement characters (which it shouldn't). Note that it's possible for this to throw off line numbering if the invalid UTF-8 occurred adjacent to a newline. 2. NUL bytes. These are problematic for some tools. Args: filename: The name of the current file. lines: An array of strings, each representing a line of the file. error: The function to call with any errors found. """ for linenum, line in enumerate(lines): if u'\ufffd' in line: error(filename, linenum, 'readability/utf8', 5, 'Line contains invalid UTF-8 (or Unicode replacement character).') if '\0' in line: error(filename, linenum, 'readability/nul', 5, 'Line contains NUL byte.') def CheckForNewlineAtEOF(filename, lines, error): """Logs an error if there is no newline char at the end of the file. Args: filename: The name of the current file. lines: An array of strings, each representing a line of the file. error: The function to call with any errors found. """ # The array lines() was created by adding two newlines to the # original file (go figure), then splitting on \n. # To verify that the file ends in \n, we just have to make sure the # last-but-two element of lines() exists and is empty. if len(lines) < 3 or lines[-2]: error(filename, len(lines) - 2, 'whitespace/ending_newline', 5, 'Could not find a newline character at the end of the file.') def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error): """Logs an error if we see /* ... */ or "..." that extend past one line. /* ... */ comments are legit inside macros, for one line. Otherwise, we prefer // comments, so it's ok to warn about the other. Likewise, it's ok for strings to extend across multiple lines, as long as a line continuation character (backslash) terminates each line. Although not currently prohibited by the C++ style guide, it's ugly and unnecessary. We don't do well with either in this lint program, so we warn about both. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] # Remove all \\ (escaped backslashes) from the line. They are OK, and the # second (escaped) slash may trigger later \" detection erroneously. line = line.replace('\\\\', '') if line.count('/*') > line.count('*/'): error(filename, linenum, 'readability/multiline_comment', 5, 'Complex multi-line /*...*/-style comment found. ' 'Lint may give bogus warnings. ' 'Consider replacing these with //-style comments, ' 'with #if 0...#endif, ' 'or with more clearly structured multi-line comments.') if (line.count('"') - line.count('\\"')) % 2: error(filename, linenum, 'readability/multiline_string', 5, 'Multi-line string ("...") found. This lint script doesn\'t ' 'do well with such strings, and may give bogus warnings. ' 'Use C++11 raw strings or concatenation instead.') # (non-threadsafe name, thread-safe alternative, validation pattern) # # The validation pattern is used to eliminate false positives such as: # _rand(); // false positive due to substring match. # ->rand(); // some member function rand(). # ACMRandom rand(seed); // some variable named rand. # ISAACRandom rand(); // another variable named rand. # # Basically we require the return value of these functions to be used # in some expression context on the same line by matching on some # operator before the function name. This eliminates constructors and # member function calls. _UNSAFE_FUNC_PREFIX = r'(?:[-+*/=%^&|(<]\s*|>\s+)' _THREADING_LIST = ( ('asctime(', 'asctime_r(', _UNSAFE_FUNC_PREFIX + r'asctime\([^)]+\)'), ('ctime(', 'ctime_r(', _UNSAFE_FUNC_PREFIX + r'ctime\([^)]+\)'), ('getgrgid(', 'getgrgid_r(', _UNSAFE_FUNC_PREFIX + r'getgrgid\([^)]+\)'), ('getgrnam(', 'getgrnam_r(', _UNSAFE_FUNC_PREFIX + r'getgrnam\([^)]+\)'), ('getlogin(', 'getlogin_r(', _UNSAFE_FUNC_PREFIX + r'getlogin\(\)'), ('getpwnam(', 'getpwnam_r(', _UNSAFE_FUNC_PREFIX + r'getpwnam\([^)]+\)'), ('getpwuid(', 'getpwuid_r(', _UNSAFE_FUNC_PREFIX + r'getpwuid\([^)]+\)'), ('gmtime(', 'gmtime_r(', _UNSAFE_FUNC_PREFIX + r'gmtime\([^)]+\)'), ('localtime(', 'localtime_r(', _UNSAFE_FUNC_PREFIX + r'localtime\([^)]+\)'), ('rand(', 'rand_r(', _UNSAFE_FUNC_PREFIX + r'rand\(\)'), ('strtok(', 'strtok_r(', _UNSAFE_FUNC_PREFIX + r'strtok\([^)]+\)'), ('ttyname(', 'ttyname_r(', _UNSAFE_FUNC_PREFIX + r'ttyname\([^)]+\)'), ) def CheckPosixThreading(filename, clean_lines, linenum, error): """Checks for calls to thread-unsafe functions. Much code has been originally written without consideration of multi-threading. Also, engineers are relying on their old experience; they have learned posix before threading extensions were added. These tests guide the engineers to use thread-safe functions (when using posix directly). Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] for single_thread_func, multithread_safe_func, pattern in _THREADING_LIST: # Additional pattern matching check to confirm that this is the # function we are looking for if Search(pattern, line): error(filename, linenum, 'runtime/threadsafe_fn', 2, 'Consider using ' + multithread_safe_func + '...) instead of ' + single_thread_func + '...) for improved thread safety.') def CheckVlogArguments(filename, clean_lines, linenum, error): """Checks that VLOG() is only used for defining a logging level. For example, VLOG(2) is correct. VLOG(INFO), VLOG(WARNING), VLOG(ERROR), and VLOG(FATAL) are not. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] if Search(r'\bVLOG\((INFO|ERROR|WARNING|DFATAL|FATAL)\)', line): error(filename, linenum, 'runtime/vlog', 5, 'VLOG() should be used with numeric verbosity level. ' 'Use LOG() if you want symbolic severity levels.') # Matches invalid increment: *count++, which moves pointer instead of # incrementing a value. _RE_PATTERN_INVALID_INCREMENT = re.compile( r'^\s*\*\w+(\+\+|--);') def CheckInvalidIncrement(filename, clean_lines, linenum, error): """Checks for invalid increment *count++. For example following function: void increment_counter(int* count) { *count++; } is invalid, because it effectively does count++, moving pointer, and should be replaced with ++*count, (*count)++ or *count += 1. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] if _RE_PATTERN_INVALID_INCREMENT.match(line): error(filename, linenum, 'runtime/invalid_increment', 5, 'Changing pointer instead of value (or unused value of operator*).') def IsMacroDefinition(clean_lines, linenum): if Search(r'^#define', clean_lines[linenum]): return True if linenum > 0 and Search(r'\\$', clean_lines[linenum - 1]): return True return False def IsForwardClassDeclaration(clean_lines, linenum): return Match(r'^\s*(\btemplate\b)*.*class\s+\w+;\s*$', clean_lines[linenum]) class _BlockInfo(object): """Stores information about a generic block of code.""" def __init__(self, seen_open_brace): self.seen_open_brace = seen_open_brace self.open_parentheses = 0 self.inline_asm = _NO_ASM self.check_namespace_indentation = False def CheckBegin(self, filename, clean_lines, linenum, error): """Run checks that applies to text up to the opening brace. This is mostly for checking the text after the class identifier and the "{", usually where the base class is specified. For other blocks, there isn't much to check, so we always pass. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ pass def CheckEnd(self, filename, clean_lines, linenum, error): """Run checks that applies to text after the closing brace. This is mostly used for checking end of namespace comments. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ pass def IsBlockInfo(self): """Returns true if this block is a _BlockInfo. This is convenient for verifying that an object is an instance of a _BlockInfo, but not an instance of any of the derived classes. Returns: True for this class, False for derived classes. """ return self.__class__ == _BlockInfo class _ExternCInfo(_BlockInfo): """Stores information about an 'extern "C"' block.""" def __init__(self): _BlockInfo.__init__(self, True) class _ClassInfo(_BlockInfo): """Stores information about a class.""" def __init__(self, name, class_or_struct, clean_lines, linenum): _BlockInfo.__init__(self, False) self.name = name self.starting_linenum = linenum self.is_derived = False self.check_namespace_indentation = True if class_or_struct == 'struct': self.access = 'public' self.is_struct = True else: self.access = 'private' self.is_struct = False # Remember initial indentation level for this class. Using raw_lines here # instead of elided to account for leading comments. self.class_indent = GetIndentLevel(clean_lines.raw_lines[linenum]) # Try to find the end of the class. This will be confused by things like: # class A { # } *x = { ... # # But it's still good enough for CheckSectionSpacing. self.last_line = 0 depth = 0 for i in range(linenum, clean_lines.NumLines()): line = clean_lines.elided[i] depth += line.count('{') - line.count('}') if not depth: self.last_line = i break def CheckBegin(self, filename, clean_lines, linenum, error): # Look for a bare ':' if Search('(^|[^:]):($|[^:])', clean_lines.elided[linenum]): self.is_derived = True def CheckEnd(self, filename, clean_lines, linenum, error): # If there is a DISALLOW macro, it should appear near the end of # the class. seen_last_thing_in_class = False for i in xrange(linenum - 1, self.starting_linenum, -1): match = Search( r'\b(DISALLOW_COPY_AND_ASSIGN|DISALLOW_IMPLICIT_CONSTRUCTORS)\(' + self.name + r'\)', clean_lines.elided[i]) if match: if seen_last_thing_in_class: error(filename, i, 'readability/constructors', 3, match.group(1) + ' should be the last thing in the class') break if not Match(r'^\s*$', clean_lines.elided[i]): seen_last_thing_in_class = True # Check that closing brace is aligned with beginning of the class. # Only do this if the closing brace is indented by only whitespaces. # This means we will not check single-line class definitions. indent = Match(r'^( *)\}', clean_lines.elided[linenum]) if indent and len(indent.group(1)) != self.class_indent: if self.is_struct: parent = 'struct ' + self.name else: parent = 'class ' + self.name error(filename, linenum, 'whitespace/indent', 3, 'Closing brace should be aligned with beginning of %s' % parent) class _NamespaceInfo(_BlockInfo): """Stores information about a namespace.""" def __init__(self, name, linenum): _BlockInfo.__init__(self, False) self.name = name or '' self.starting_linenum = linenum self.check_namespace_indentation = True def CheckEnd(self, filename, clean_lines, linenum, error): """Check end of namespace comments.""" line = clean_lines.raw_lines[linenum] # Check how many lines is enclosed in this namespace. Don't issue # warning for missing namespace comments if there aren't enough # lines. However, do apply checks if there is already an end of # namespace comment and it's incorrect. # # TODO(unknown): We always want to check end of namespace comments # if a namespace is large, but sometimes we also want to apply the # check if a short namespace contained nontrivial things (something # other than forward declarations). There is currently no logic on # deciding what these nontrivial things are, so this check is # triggered by namespace size only, which works most of the time. if (linenum - self.starting_linenum < 10 and not Match(r'};*\s*(//|/\*).*\bnamespace\b', line)): return # Look for matching comment at end of namespace. # # Note that we accept C style "/* */" comments for terminating # namespaces, so that code that terminate namespaces inside # preprocessor macros can be cpplint clean. # # We also accept stuff like "// end of namespace ." with the # period at the end. # # Besides these, we don't accept anything else, otherwise we might # get false negatives when existing comment is a substring of the # expected namespace. if self.name: # Named namespace if not Match((r'};*\s*(//|/\*).*\bnamespace\s+' + re.escape(self.name) + r'[\*/\.\\\s]*$'), line): error(filename, linenum, 'readability/namespace', 5, 'Namespace should be terminated with "// namespace %s"' % self.name) else: # Anonymous namespace if not Match(r'};*\s*(//|/\*).*\bnamespace[\*/\.\\\s]*$', line): # If "// namespace anonymous" or "// anonymous namespace (more text)", # mention "// anonymous namespace" as an acceptable form if Match(r'}.*\b(namespace anonymous|anonymous namespace)\b', line): error(filename, linenum, 'readability/namespace', 5, 'Anonymous namespace should be terminated with "// namespace"' ' or "// anonymous namespace"') else: error(filename, linenum, 'readability/namespace', 5, 'Anonymous namespace should be terminated with "// namespace"') class _PreprocessorInfo(object): """Stores checkpoints of nesting stacks when #if/#else is seen.""" def __init__(self, stack_before_if): # The entire nesting stack before #if self.stack_before_if = stack_before_if # The entire nesting stack up to #else self.stack_before_else = [] # Whether we have already seen #else or #elif self.seen_else = False class NestingState(object): """Holds states related to parsing braces.""" def __init__(self): # Stack for tracking all braces. An object is pushed whenever we # see a "{", and popped when we see a "}". Only 3 types of # objects are possible: # - _ClassInfo: a class or struct. # - _NamespaceInfo: a namespace. # - _BlockInfo: some other type of block. self.stack = [] # Top of the previous stack before each Update(). # # Because the nesting_stack is updated at the end of each line, we # had to do some convoluted checks to find out what is the current # scope at the beginning of the line. This check is simplified by # saving the previous top of nesting stack. # # We could save the full stack, but we only need the top. Copying # the full nesting stack would slow down cpplint by ~10%. self.previous_stack_top = [] # Stack of _PreprocessorInfo objects. self.pp_stack = [] def SeenOpenBrace(self): """Check if we have seen the opening brace for the innermost block. Returns: True if we have seen the opening brace, False if the innermost block is still expecting an opening brace. """ return (not self.stack) or self.stack[-1].seen_open_brace def InNamespaceBody(self): """Check if we are currently one level inside a namespace body. Returns: True if top of the stack is a namespace block, False otherwise. """ return self.stack and isinstance(self.stack[-1], _NamespaceInfo) def InExternC(self): """Check if we are currently one level inside an 'extern "C"' block. Returns: True if top of the stack is an extern block, False otherwise. """ return self.stack and isinstance(self.stack[-1], _ExternCInfo) def InClassDeclaration(self): """Check if we are currently one level inside a class or struct declaration. Returns: True if top of the stack is a class/struct, False otherwise. """ return self.stack and isinstance(self.stack[-1], _ClassInfo) def InAsmBlock(self): """Check if we are currently one level inside an inline ASM block. Returns: True if the top of the stack is a block containing inline ASM. """ return self.stack and self.stack[-1].inline_asm != _NO_ASM def InTemplateArgumentList(self, clean_lines, linenum, pos): """Check if current position is inside template argument list. Args: clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. pos: position just after the suspected template argument. Returns: True if (linenum, pos) is inside template arguments. """ while linenum < clean_lines.NumLines(): # Find the earliest character that might indicate a template argument line = clean_lines.elided[linenum] match = Match(r'^[^{};=\[\]\.<>]*(.)', line[pos:]) if not match: linenum += 1 pos = 0 continue token = match.group(1) pos += len(match.group(0)) # These things do not look like template argument list: # class Suspect { # class Suspect x; } if token in ('{', '}', ';'): return False # These things look like template argument list: # template # template # template # template if token in ('>', '=', '[', ']', '.'): return True # Check if token is an unmatched '<'. # If not, move on to the next character. if token != '<': pos += 1 if pos >= len(line): linenum += 1 pos = 0 continue # We can't be sure if we just find a single '<', and need to # find the matching '>'. (_, end_line, end_pos) = CloseExpression(clean_lines, linenum, pos - 1) if end_pos < 0: # Not sure if template argument list or syntax error in file return False linenum = end_line pos = end_pos return False def UpdatePreprocessor(self, line): """Update preprocessor stack. We need to handle preprocessors due to classes like this: #ifdef SWIG struct ResultDetailsPageElementExtensionPoint { #else struct ResultDetailsPageElementExtensionPoint : public Extension { #endif We make the following assumptions (good enough for most files): - Preprocessor condition evaluates to true from #if up to first #else/#elif/#endif. - Preprocessor condition evaluates to false from #else/#elif up to #endif. We still perform lint checks on these lines, but these do not affect nesting stack. Args: line: current line to check. """ if Match(r'^\s*#\s*(if|ifdef|ifndef)\b', line): # Beginning of #if block, save the nesting stack here. The saved # stack will allow us to restore the parsing state in the #else case. self.pp_stack.append(_PreprocessorInfo(copy.deepcopy(self.stack))) elif Match(r'^\s*#\s*(else|elif)\b', line): # Beginning of #else block if self.pp_stack: if not self.pp_stack[-1].seen_else: # This is the first #else or #elif block. Remember the # whole nesting stack up to this point. This is what we # keep after the #endif. self.pp_stack[-1].seen_else = True self.pp_stack[-1].stack_before_else = copy.deepcopy(self.stack) # Restore the stack to how it was before the #if self.stack = copy.deepcopy(self.pp_stack[-1].stack_before_if) else: # TODO(unknown): unexpected #else, issue warning? pass elif Match(r'^\s*#\s*endif\b', line): # End of #if or #else blocks. if self.pp_stack: # If we saw an #else, we will need to restore the nesting # stack to its former state before the #else, otherwise we # will just continue from where we left off. if self.pp_stack[-1].seen_else: # Here we can just use a shallow copy since we are the last # reference to it. self.stack = self.pp_stack[-1].stack_before_else # Drop the corresponding #if self.pp_stack.pop() else: # TODO(unknown): unexpected #endif, issue warning? pass # TODO(unknown): Update() is too long, but we will refactor later. def Update(self, filename, clean_lines, linenum, error): """Update nesting state with current line. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] # Remember top of the previous nesting stack. # # The stack is always pushed/popped and not modified in place, so # we can just do a shallow copy instead of copy.deepcopy. Using # deepcopy would slow down cpplint by ~28%. if self.stack: self.previous_stack_top = self.stack[-1] else: self.previous_stack_top = None # Update pp_stack self.UpdatePreprocessor(line) # Count parentheses. This is to avoid adding struct arguments to # the nesting stack. if self.stack: inner_block = self.stack[-1] depth_change = line.count('(') - line.count(')') inner_block.open_parentheses += depth_change # Also check if we are starting or ending an inline assembly block. if inner_block.inline_asm in (_NO_ASM, _END_ASM): if (depth_change != 0 and inner_block.open_parentheses == 1 and _MATCH_ASM.match(line)): # Enter assembly block inner_block.inline_asm = _INSIDE_ASM else: # Not entering assembly block. If previous line was _END_ASM, # we will now shift to _NO_ASM state. inner_block.inline_asm = _NO_ASM elif (inner_block.inline_asm == _INSIDE_ASM and inner_block.open_parentheses == 0): # Exit assembly block inner_block.inline_asm = _END_ASM # Consume namespace declaration at the beginning of the line. Do # this in a loop so that we catch same line declarations like this: # namespace proto2 { namespace bridge { class MessageSet; } } while True: # Match start of namespace. The "\b\s*" below catches namespace # declarations even if it weren't followed by a whitespace, this # is so that we don't confuse our namespace checker. The # missing spaces will be flagged by CheckSpacing. namespace_decl_match = Match(r'^\s*namespace\b\s*([:\w]+)?(.*)$', line) if not namespace_decl_match: break new_namespace = _NamespaceInfo(namespace_decl_match.group(1), linenum) self.stack.append(new_namespace) line = namespace_decl_match.group(2) if line.find('{') != -1: new_namespace.seen_open_brace = True line = line[line.find('{') + 1:] # Look for a class declaration in whatever is left of the line # after parsing namespaces. The regexp accounts for decorated classes # such as in: # class LOCKABLE API Object { # }; class_decl_match = Match( r'^(\s*(?:template\s*<[\w\s<>,:]*>\s*)?' r'(class|struct)\s+(?:[A-Z_]+\s+)*(\w+(?:::\w+)*))' r'(.*)$', line) if (class_decl_match and (not self.stack or self.stack[-1].open_parentheses == 0)): # We do not want to accept classes that are actually template arguments: # template , # template class Ignore3> # void Function() {}; # # To avoid template argument cases, we scan forward and look for # an unmatched '>'. If we see one, assume we are inside a # template argument list. end_declaration = len(class_decl_match.group(1)) if not self.InTemplateArgumentList(clean_lines, linenum, end_declaration): self.stack.append(_ClassInfo( class_decl_match.group(3), class_decl_match.group(2), clean_lines, linenum)) line = class_decl_match.group(4) # If we have not yet seen the opening brace for the innermost block, # run checks here. if not self.SeenOpenBrace(): self.stack[-1].CheckBegin(filename, clean_lines, linenum, error) # Update access control if we are inside a class/struct if self.stack and isinstance(self.stack[-1], _ClassInfo): classinfo = self.stack[-1] access_match = Match( r'^(.*)\b(public|private|protected|signals)(\s+(?:slots\s*)?)?' r':(?:[^:]|$)', line) if access_match: classinfo.access = access_match.group(2) # Check that access keywords are indented +1 space. Skip this # check if the keywords are not preceded by whitespaces. indent = access_match.group(1) if (len(indent) != classinfo.class_indent + 1 and Match(r'^\s*$', indent)): if classinfo.is_struct: parent = 'struct ' + classinfo.name else: parent = 'class ' + classinfo.name slots = '' if access_match.group(3): slots = access_match.group(3) error(filename, linenum, 'whitespace/indent', 3, '%s%s: should be indented +1 space inside %s' % ( access_match.group(2), slots, parent)) # Consume braces or semicolons from what's left of the line while True: # Match first brace, semicolon, or closed parenthesis. matched = Match(r'^[^{;)}]*([{;)}])(.*)$', line) if not matched: break token = matched.group(1) if token == '{': # If namespace or class hasn't seen a opening brace yet, mark # namespace/class head as complete. Push a new block onto the # stack otherwise. if not self.SeenOpenBrace(): self.stack[-1].seen_open_brace = True elif Match(r'^extern\s*"[^"]*"\s*\{', line): self.stack.append(_ExternCInfo()) else: self.stack.append(_BlockInfo(True)) if _MATCH_ASM.match(line): self.stack[-1].inline_asm = _BLOCK_ASM elif token == ';' or token == ')': # If we haven't seen an opening brace yet, but we already saw # a semicolon, this is probably a forward declaration. Pop # the stack for these. # # Similarly, if we haven't seen an opening brace yet, but we # already saw a closing parenthesis, then these are probably # function arguments with extra "class" or "struct" keywords. # Also pop these stack for these. if not self.SeenOpenBrace(): self.stack.pop() else: # token == '}' # Perform end of block checks and pop the stack. if self.stack: self.stack[-1].CheckEnd(filename, clean_lines, linenum, error) self.stack.pop() line = matched.group(2) def InnermostClass(self): """Get class info on the top of the stack. Returns: A _ClassInfo object if we are inside a class, or None otherwise. """ for i in range(len(self.stack), 0, -1): classinfo = self.stack[i - 1] if isinstance(classinfo, _ClassInfo): return classinfo return None def CheckCompletedBlocks(self, filename, error): """Checks that all classes and namespaces have been completely parsed. Call this when all lines in a file have been processed. Args: filename: The name of the current file. error: The function to call with any errors found. """ # Note: This test can result in false positives if #ifdef constructs # get in the way of brace matching. See the testBuildClass test in # cpplint_unittest.py for an example of this. for obj in self.stack: if isinstance(obj, _ClassInfo): error(filename, obj.starting_linenum, 'build/class', 5, 'Failed to find complete declaration of class %s' % obj.name) elif isinstance(obj, _NamespaceInfo): error(filename, obj.starting_linenum, 'build/namespaces', 5, 'Failed to find complete declaration of namespace %s' % obj.name) def CheckForNonStandardConstructs(filename, clean_lines, linenum, nesting_state, error): r"""Logs an error if we see certain non-ANSI constructs ignored by gcc-2. Complain about several constructs which gcc-2 accepts, but which are not standard C++. Warning about these in lint is one way to ease the transition to new compilers. - put storage class first (e.g. "static const" instead of "const static"). - "%lld" instead of %qd" in printf-type functions. - "%1$d" is non-standard in printf-type functions. - "\%" is an undefined character escape sequence. - text after #endif is not allowed. - invalid inner-style forward declaration. - >? and ?= and )\?=?\s*(\w+|[+-]?\d+)(\.\d*)?', line): error(filename, linenum, 'build/deprecated', 3, '>? and ))?' # r'\s*const\s*' + type_name + '\s*&\s*\w+\s*;' error(filename, linenum, 'runtime/member_string_references', 2, 'const string& members are dangerous. It is much better to use ' 'alternatives, such as pointers or simple constants.') # Everything else in this function operates on class declarations. # Return early if the top of the nesting stack is not a class, or if # the class head is not completed yet. classinfo = nesting_state.InnermostClass() if not classinfo or not classinfo.seen_open_brace: return # The class may have been declared with namespace or classname qualifiers. # The constructor and destructor will not have those qualifiers. base_classname = classinfo.name.split('::')[-1] # Look for single-argument constructors that aren't marked explicit. # Technically a valid construct, but against style. Also look for # non-single-argument constructors which are also technically valid, but # strongly suggest something is wrong. explicit_constructor_match = Match( r'\s+(?:inline\s+)?(explicit\s+)?(?:inline\s+)?%s\s*' r'\(((?:[^()]|\([^()]*\))*)\)' % re.escape(base_classname), line) if explicit_constructor_match: is_marked_explicit = explicit_constructor_match.group(1) if not explicit_constructor_match.group(2): constructor_args = [] else: constructor_args = explicit_constructor_match.group(2).split(',') # collapse arguments so that commas in template parameter lists and function # argument parameter lists don't split arguments in two i = 0 while i < len(constructor_args): constructor_arg = constructor_args[i] while (constructor_arg.count('<') > constructor_arg.count('>') or constructor_arg.count('(') > constructor_arg.count(')')): constructor_arg += ',' + constructor_args[i + 1] del constructor_args[i + 1] constructor_args[i] = constructor_arg i += 1 defaulted_args = [arg for arg in constructor_args if '=' in arg] noarg_constructor = (not constructor_args or # empty arg list # 'void' arg specifier (len(constructor_args) == 1 and constructor_args[0].strip() == 'void')) onearg_constructor = ((len(constructor_args) == 1 and # exactly one arg not noarg_constructor) or # all but at most one arg defaulted (len(constructor_args) >= 1 and not noarg_constructor and len(defaulted_args) >= len(constructor_args) - 1)) initializer_list_constructor = bool( onearg_constructor and Search(r'\bstd\s*::\s*initializer_list\b', constructor_args[0])) copy_constructor = bool( onearg_constructor and Match(r'(const\s+)?%s(\s*<[^>]*>)?(\s+const)?\s*(?:<\w+>\s*)?&' % re.escape(base_classname), constructor_args[0].strip())) if (not is_marked_explicit and onearg_constructor and not initializer_list_constructor and not copy_constructor): if defaulted_args: error(filename, linenum, 'runtime/explicit', 5, 'Constructors callable with one argument ' 'should be marked explicit.') else: error(filename, linenum, 'runtime/explicit', 5, 'Single-parameter constructors should be marked explicit.') elif is_marked_explicit and not onearg_constructor: if noarg_constructor: error(filename, linenum, 'runtime/explicit', 5, 'Zero-parameter constructors should not be marked explicit.') else: error(filename, linenum, 'runtime/explicit', 0, 'Constructors that require multiple arguments ' 'should not be marked explicit.') def CheckSpacingForFunctionCall(filename, clean_lines, linenum, error): """Checks for the correctness of various spacing around function calls. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] # Since function calls often occur inside if/for/while/switch # expressions - which have their own, more liberal conventions - we # first see if we should be looking inside such an expression for a # function call, to which we can apply more strict standards. fncall = line # if there's no control flow construct, look at whole line for pattern in (r'\bif\s*\((.*)\)\s*{', r'\bfor\s*\((.*)\)\s*{', r'\bwhile\s*\((.*)\)\s*[{;]', r'\bswitch\s*\((.*)\)\s*{'): match = Search(pattern, line) if match: fncall = match.group(1) # look inside the parens for function calls break # Except in if/for/while/switch, there should never be space # immediately inside parens (eg "f( 3, 4 )"). We make an exception # for nested parens ( (a+b) + c ). Likewise, there should never be # a space before a ( when it's a function argument. I assume it's a # function argument when the char before the whitespace is legal in # a function name (alnum + _) and we're not starting a macro. Also ignore # pointers and references to arrays and functions coz they're too tricky: # we use a very simple way to recognize these: # " (something)(maybe-something)" or # " (something)(maybe-something," or # " (something)[something]" # Note that we assume the contents of [] to be short enough that # they'll never need to wrap. if ( # Ignore control structures. not Search(r'\b(if|for|while|switch|return|new|delete|catch|sizeof)\b', fncall) and # Ignore pointers/references to functions. not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and # Ignore pointers/references to arrays. not Search(r' \([^)]+\)\[[^\]]+\]', fncall)): if Search(r'\w\s*\(\s(?!\s*\\$)', fncall): # a ( used for a fn call error(filename, linenum, 'whitespace/parens', 4, 'Extra space after ( in function call') elif Search(r'\(\s+(?!(\s*\\)|\()', fncall): error(filename, linenum, 'whitespace/parens', 2, 'Extra space after (') if (Search(r'\w\s+\(', fncall) and not Search(r'#\s*define|typedef|using\s+\w+\s*=', fncall) and not Search(r'\w\s+\((\w+::)*\*\w+\)\(', fncall) and not Search(r'\bcase\s+\(', fncall)): # TODO(unknown): Space after an operator function seem to be a common # error, silence those for now by restricting them to highest verbosity. if Search(r'\boperator_*\b', line): error(filename, linenum, 'whitespace/parens', 0, 'Extra space before ( in function call') else: error(filename, linenum, 'whitespace/parens', 4, 'Extra space before ( in function call') # If the ) is followed only by a newline or a { + newline, assume it's # part of a control statement (if/while/etc), and don't complain if Search(r'[^)]\s+\)\s*[^{\s]', fncall): # If the closing parenthesis is preceded by only whitespaces, # try to give a more descriptive error message. if Search(r'^\s+\)', fncall): error(filename, linenum, 'whitespace/parens', 2, 'Closing ) should be moved to the previous line') else: error(filename, linenum, 'whitespace/parens', 2, 'Extra space before )') def IsBlankLine(line): """Returns true if the given line is blank. We consider a line to be blank if the line is empty or consists of only white spaces. Args: line: A line of a string. Returns: True, if the given line is blank. """ return not line or line.isspace() def CheckForNamespaceIndentation(filename, nesting_state, clean_lines, line, error): is_namespace_indent_item = ( len(nesting_state.stack) > 1 and nesting_state.stack[-1].check_namespace_indentation and isinstance(nesting_state.previous_stack_top, _NamespaceInfo) and nesting_state.previous_stack_top == nesting_state.stack[-2]) if ShouldCheckNamespaceIndentation(nesting_state, is_namespace_indent_item, clean_lines.elided, line): CheckItemIndentationInNamespace(filename, clean_lines.elided, line, error) def CheckForFunctionLengths(filename, clean_lines, linenum, function_state, error): """Reports for long function bodies. For an overview why this is done, see: http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Write_Short_Functions Uses a simplistic algorithm assuming other style guidelines (especially spacing) are followed. Only checks unindented functions, so class members are unchecked. Trivial bodies are unchecked, so constructors with huge initializer lists may be missed. Blank/comment lines are not counted so as to avoid encouraging the removal of vertical space and comments just to get through a lint check. NOLINT *on the last line of a function* disables this check. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. function_state: Current function name and lines in body so far. error: The function to call with any errors found. """ lines = clean_lines.lines line = lines[linenum] joined_line = '' starting_func = False regexp = r'(\w(\w|::|\*|\&|\s)*)\(' # decls * & space::name( ... match_result = Match(regexp, line) if match_result: # If the name is all caps and underscores, figure it's a macro and # ignore it, unless it's TEST or TEST_F. function_name = match_result.group(1).split()[-1] if function_name == 'TEST' or function_name == 'TEST_F' or ( not Match(r'[A-Z_]+$', function_name)): starting_func = True if starting_func: body_found = False for start_linenum in xrange(linenum, clean_lines.NumLines()): start_line = lines[start_linenum] joined_line += ' ' + start_line.lstrip() if Search(r'(;|})', start_line): # Declarations and trivial functions body_found = True break # ... ignore elif Search(r'{', start_line): body_found = True function = Search(r'((\w|:)*)\(', line).group(1) if Match(r'TEST', function): # Handle TEST... macros parameter_regexp = Search(r'(\(.*\))', joined_line) if parameter_regexp: # Ignore bad syntax function += parameter_regexp.group(1) else: function += '()' function_state.Begin(function) break if not body_found: # No body for the function (or evidence of a non-function) was found. error(filename, linenum, 'readability/fn_size', 5, 'Lint failed to find start of function body.') elif Match(r'^\}\s*$', line): # function end function_state.Check(error, filename, linenum) function_state.End() elif not Match(r'^\s*$', line): function_state.Count() # Count non-blank/non-comment lines. _RE_PATTERN_TODO = re.compile(r'^//(\s*)TODO(\(.+?\))?:?(\s|$)?') def CheckComment(line, filename, linenum, next_line_start, error): """Checks for common mistakes in comments. Args: line: The line in question. filename: The name of the current file. linenum: The number of the line to check. next_line_start: The first non-whitespace column of the next line. error: The function to call with any errors found. """ commentpos = line.find('//') if commentpos != -1: # Check if the // may be in quotes. If so, ignore it # Comparisons made explicit for clarity -- pylint: disable=g-explicit-bool-comparison if (line.count('"', 0, commentpos) - line.count('\\"', 0, commentpos)) % 2 == 0: # not in quotes # Allow one space for new scopes, two spaces otherwise: if (not (Match(r'^.*{ *//', line) and next_line_start == commentpos) and ((commentpos >= 1 and line[commentpos-1] not in string.whitespace) or (commentpos >= 2 and line[commentpos-2] not in string.whitespace))): error(filename, linenum, 'whitespace/comments', 2, 'At least two spaces is best between code and comments') # Checks for common mistakes in TODO comments. comment = line[commentpos:] match = _RE_PATTERN_TODO.match(comment) if match: # One whitespace is correct; zero whitespace is handled elsewhere. leading_whitespace = match.group(1) if len(leading_whitespace) > 1: error(filename, linenum, 'whitespace/todo', 2, 'Too many spaces before TODO') username = match.group(2) if not username: error(filename, linenum, 'readability/todo', 2, 'Missing username in TODO; it should look like ' '"// TODO(my_username): Stuff."') middle_whitespace = match.group(3) # Comparisons made explicit for correctness -- pylint: disable=g-explicit-bool-comparison if middle_whitespace != ' ' and middle_whitespace != '': error(filename, linenum, 'whitespace/todo', 2, 'TODO(my_username) should be followed by a space') # If the comment contains an alphanumeric character, there # should be a space somewhere between it and the // unless # it's a /// or //! Doxygen comment. if (Match(r'//[^ ]*\w', comment) and not Match(r'(///|//\!)(\s+|$)', comment)): error(filename, linenum, 'whitespace/comments', 4, 'Should have a space between // and comment') def CheckAccess(filename, clean_lines, linenum, nesting_state, error): """Checks for improper use of DISALLOW* macros. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. nesting_state: A NestingState instance which maintains information about the current stack of nested blocks being parsed. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] # get rid of comments and strings matched = Match((r'\s*(DISALLOW_COPY_AND_ASSIGN|' r'DISALLOW_IMPLICIT_CONSTRUCTORS)'), line) if not matched: return if nesting_state.stack and isinstance(nesting_state.stack[-1], _ClassInfo): if nesting_state.stack[-1].access != 'private': error(filename, linenum, 'readability/constructors', 3, '%s must be in the private: section' % matched.group(1)) else: # Found DISALLOW* macro outside a class declaration, or perhaps it # was used inside a function when it should have been part of the # class declaration. We could issue a warning here, but it # probably resulted in a compiler error already. pass def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): """Checks for the correctness of various spacing issues in the code. Things we check for: spaces around operators, spaces after if/for/while/switch, no spaces around parens in function calls, two spaces between code and comment, don't start a block with a blank line, don't end a function with a blank line, don't add a blank line after public/protected/private, don't have too many blank lines in a row. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. nesting_state: A NestingState instance which maintains information about the current stack of nested blocks being parsed. error: The function to call with any errors found. """ # Don't use "elided" lines here, otherwise we can't check commented lines. # Don't want to use "raw" either, because we don't want to check inside C++11 # raw strings, raw = clean_lines.lines_without_raw_strings line = raw[linenum] # Before nixing comments, check if the line is blank for no good # reason. This includes the first line after a block is opened, and # blank lines at the end of a function (ie, right before a line like '}' # # Skip all the blank line checks if we are immediately inside a # namespace body. In other words, don't issue blank line warnings # for this block: # namespace { # # } # # A warning about missing end of namespace comments will be issued instead. # # Also skip blank line checks for 'extern "C"' blocks, which are formatted # like namespaces. if (IsBlankLine(line) and not nesting_state.InNamespaceBody() and not nesting_state.InExternC()): elided = clean_lines.elided prev_line = elided[linenum - 1] prevbrace = prev_line.rfind('{') # TODO(unknown): Don't complain if line before blank line, and line after, # both start with alnums and are indented the same amount. # This ignores whitespace at the start of a namespace block # because those are not usually indented. if prevbrace != -1 and prev_line[prevbrace:].find('}') == -1: # OK, we have a blank line at the start of a code block. Before we # complain, we check if it is an exception to the rule: The previous # non-empty line has the parameters of a function header that are indented # 4 spaces (because they did not fit in a 80 column line when placed on # the same line as the function name). We also check for the case where # the previous line is indented 6 spaces, which may happen when the # initializers of a constructor do not fit into a 80 column line. exception = False if Match(r' {6}\w', prev_line): # Initializer list? # We are looking for the opening column of initializer list, which # should be indented 4 spaces to cause 6 space indentation afterwards. search_position = linenum-2 while (search_position >= 0 and Match(r' {6}\w', elided[search_position])): search_position -= 1 exception = (search_position >= 0 and elided[search_position][:5] == ' :') else: # Search for the function arguments or an initializer list. We use a # simple heuristic here: If the line is indented 4 spaces; and we have a # closing paren, without the opening paren, followed by an opening brace # or colon (for initializer lists) we assume that it is the last line of # a function header. If we have a colon indented 4 spaces, it is an # initializer list. exception = (Match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)', prev_line) or Match(r' {4}:', prev_line)) if not exception: error(filename, linenum, 'whitespace/blank_line', 2, 'Redundant blank line at the start of a code block ' 'should be deleted.') # Ignore blank lines at the end of a block in a long if-else # chain, like this: # if (condition1) { # // Something followed by a blank line # # } else if (condition2) { # // Something else # } if linenum + 1 < clean_lines.NumLines(): next_line = raw[linenum + 1] if (next_line and Match(r'\s*}', next_line) and next_line.find('} else ') == -1): error(filename, linenum, 'whitespace/blank_line', 3, 'Redundant blank line at the end of a code block ' 'should be deleted.') matched = Match(r'\s*(public|protected|private):', prev_line) if matched: error(filename, linenum, 'whitespace/blank_line', 3, 'Do not leave a blank line after "%s:"' % matched.group(1)) # Next, check comments next_line_start = 0 if linenum + 1 < clean_lines.NumLines(): next_line = raw[linenum + 1] next_line_start = len(next_line) - len(next_line.lstrip()) CheckComment(line, filename, linenum, next_line_start, error) # get rid of comments and strings line = clean_lines.elided[linenum] # You shouldn't have spaces before your brackets, except maybe after # 'delete []' or 'return []() {};' if Search(r'\w\s+\[', line) and not Search(r'(?:delete|return)\s+\[', line): error(filename, linenum, 'whitespace/braces', 5, 'Extra space before [') # In range-based for, we wanted spaces before and after the colon, but # not around "::" tokens that might appear. if (Search(r'for *\(.*[^:]:[^: ]', line) or Search(r'for *\(.*[^: ]:[^:]', line)): error(filename, linenum, 'whitespace/forcolon', 2, 'Missing space around colon in range-based for loop') def CheckOperatorSpacing(filename, clean_lines, linenum, error): """Checks for horizontal spacing around operators. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] # Don't try to do spacing checks for operator methods. Do this by # replacing the troublesome characters with something else, # preserving column position for all other characters. # # The replacement is done repeatedly to avoid false positives from # operators that call operators. while True: match = Match(r'^(.*\boperator\b)(\S+)(\s*\(.*)$', line) if match: line = match.group(1) + ('_' * len(match.group(2))) + match.group(3) else: break # We allow no-spaces around = within an if: "if ( (a=Foo()) == 0 )". # Otherwise not. Note we only check for non-spaces on *both* sides; # sometimes people put non-spaces on one side when aligning ='s among # many lines (not that this is behavior that I approve of...) if ((Search(r'[\w.]=', line) or Search(r'=[\w.]', line)) and not Search(r'\b(if|while|for) ', line) # Operators taken from [lex.operators] in C++11 standard. and not Search(r'(>=|<=|==|!=|&=|\^=|\|=|\+=|\*=|\/=|\%=)', line) and not Search(r'operator=', line)): error(filename, linenum, 'whitespace/operators', 4, 'Missing spaces around =') # It's ok not to have spaces around binary operators like + - * /, but if # there's too little whitespace, we get concerned. It's hard to tell, # though, so we punt on this one for now. TODO. # You should always have whitespace around binary operators. # # Check <= and >= first to avoid false positives with < and >, then # check non-include lines for spacing around < and >. # # If the operator is followed by a comma, assume it's be used in a # macro context and don't do any checks. This avoids false # positives. # # Note that && is not included here. Those are checked separately # in CheckRValueReference match = Search(r'[^<>=!\s](==|!=|<=|>=|\|\|)[^<>=!\s,;\)]', line) if match: error(filename, linenum, 'whitespace/operators', 3, 'Missing spaces around %s' % match.group(1)) elif not Match(r'#.*include', line): # Look for < that is not surrounded by spaces. This is only # triggered if both sides are missing spaces, even though # technically should should flag if at least one side is missing a # space. This is done to avoid some false positives with shifts. match = Match(r'^(.*[^\s<])<[^\s=<,]', line) if match: (_, _, end_pos) = CloseExpression( clean_lines, linenum, len(match.group(1))) if end_pos <= -1: error(filename, linenum, 'whitespace/operators', 3, 'Missing spaces around <') # Look for > that is not surrounded by spaces. Similar to the # above, we only trigger if both sides are missing spaces to avoid # false positives with shifts. match = Match(r'^(.*[^-\s>])>[^\s=>,]', line) if match: (_, _, start_pos) = ReverseCloseExpression( clean_lines, linenum, len(match.group(1))) if start_pos <= -1: error(filename, linenum, 'whitespace/operators', 3, 'Missing spaces around >') # We allow no-spaces around << when used like this: 10<<20, but # not otherwise (particularly, not when used as streams) # # We also allow operators following an opening parenthesis, since # those tend to be macros that deal with operators. match = Search(r'(operator|[^\s(<])(?:L|UL|ULL|l|ul|ull)?<<([^\s,=<])', line) if (match and not (match.group(1).isdigit() and match.group(2).isdigit()) and not (match.group(1) == 'operator' and match.group(2) == ';')): error(filename, linenum, 'whitespace/operators', 3, 'Missing spaces around <<') # We allow no-spaces around >> for almost anything. This is because # C++11 allows ">>" to close nested templates, which accounts for # most cases when ">>" is not followed by a space. # # We still warn on ">>" followed by alpha character, because that is # likely due to ">>" being used for right shifts, e.g.: # value >> alpha # # When ">>" is used to close templates, the alphanumeric letter that # follows would be part of an identifier, and there should still be # a space separating the template type and the identifier. # type> alpha match = Search(r'>>[a-zA-Z_]', line) if match: error(filename, linenum, 'whitespace/operators', 3, 'Missing spaces around >>') # There shouldn't be space around unary operators match = Search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line) if match: error(filename, linenum, 'whitespace/operators', 4, 'Extra space for operator %s' % match.group(1)) def CheckParenthesisSpacing(filename, clean_lines, linenum, error): """Checks for horizontal spacing around parentheses. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] # No spaces after an if, while, switch, or for match = Search(r' (if\(|for\(|while\(|switch\()', line) if match: error(filename, linenum, 'whitespace/parens', 5, 'Missing space before ( in %s' % match.group(1)) # For if/for/while/switch, the left and right parens should be # consistent about how many spaces are inside the parens, and # there should either be zero or one spaces inside the parens. # We don't want: "if ( foo)" or "if ( foo )". # Exception: "for ( ; foo; bar)" and "for (foo; bar; )" are allowed. match = Search(r'\b(if|for|while|switch)\s*' r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$', line) if match: if len(match.group(2)) != len(match.group(4)): if not (match.group(3) == ';' and len(match.group(2)) == 1 + len(match.group(4)) or not match.group(2) and Search(r'\bfor\s*\(.*; \)', line)): error(filename, linenum, 'whitespace/parens', 5, 'Mismatching spaces inside () in %s' % match.group(1)) if len(match.group(2)) not in [0, 1]: error(filename, linenum, 'whitespace/parens', 5, 'Should have zero or one spaces inside ( and ) in %s' % match.group(1)) def CheckCommaSpacing(filename, clean_lines, linenum, error): """Checks for horizontal spacing near commas and semicolons. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ raw = clean_lines.lines_without_raw_strings line = clean_lines.elided[linenum] # You should always have a space after a comma (either as fn arg or operator) # # This does not apply when the non-space character following the # comma is another comma, since the only time when that happens is # for empty macro arguments. # # We run this check in two passes: first pass on elided lines to # verify that lines contain missing whitespaces, second pass on raw # lines to confirm that those missing whitespaces are not due to # elided comments. if (Search(r',[^,\s]', ReplaceAll(r'\boperator\s*,\s*\(', 'F(', line)) and Search(r',[^,\s]', raw[linenum])): error(filename, linenum, 'whitespace/comma', 3, 'Missing space after ,') # You should always have a space after a semicolon # except for few corner cases # TODO(unknown): clarify if 'if (1) { return 1;}' is requires one more # space after ; if Search(r';[^\s};\\)/]', line): error(filename, linenum, 'whitespace/semicolon', 3, 'Missing space after ;') def CheckBracesSpacing(filename, clean_lines, linenum, error): """Checks for horizontal spacing near commas. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] # Except after an opening paren, or after another opening brace (in case of # an initializer list, for instance), you should have spaces before your # braces. And since you should never have braces at the beginning of a line, # this is an easy test. match = Match(r'^(.*[^ ({>]){', line) if match: # Try a bit harder to check for brace initialization. This # happens in one of the following forms: # Constructor() : initializer_list_{} { ... } # Constructor{}.MemberFunction() # Type variable{}; # FunctionCall(type{}, ...); # LastArgument(..., type{}); # LOG(INFO) << type{} << " ..."; # map_of_type[{...}] = ...; # ternary = expr ? new type{} : nullptr; # OuterTemplate{}> # # We check for the character following the closing brace, and # silence the warning if it's one of those listed above, i.e. # "{.;,)<>]:". # # To account for nested initializer list, we allow any number of # closing braces up to "{;,)<". We can't simply silence the # warning on first sight of closing brace, because that would # cause false negatives for things that are not initializer lists. # Silence this: But not this: # Outer{ if (...) { # Inner{...} if (...){ // Missing space before { # }; } # # There is a false negative with this approach if people inserted # spurious semicolons, e.g. "if (cond){};", but we will catch the # spurious semicolon with a separate check. (endline, endlinenum, endpos) = CloseExpression( clean_lines, linenum, len(match.group(1))) trailing_text = '' if endpos > -1: trailing_text = endline[endpos:] for offset in xrange(endlinenum + 1, min(endlinenum + 3, clean_lines.NumLines() - 1)): trailing_text += clean_lines.elided[offset] if not Match(r'^[\s}]*[{.;,)<>\]:]', trailing_text): error(filename, linenum, 'whitespace/braces', 5, 'Missing space before {') # Make sure '} else {' has spaces. if Search(r'}else', line): error(filename, linenum, 'whitespace/braces', 5, 'Missing space before else') # You shouldn't have a space before a semicolon at the end of the line. # There's a special case for "for" since the style guide allows space before # the semicolon there. if Search(r':\s*;\s*$', line): error(filename, linenum, 'whitespace/semicolon', 5, 'Semicolon defining empty statement. Use {} instead.') elif Search(r'^\s*;\s*$', line): error(filename, linenum, 'whitespace/semicolon', 5, 'Line contains only semicolon. If this should be an empty statement, ' 'use {} instead.') elif (Search(r'\s+;\s*$', line) and not Search(r'\bfor\b', line)): error(filename, linenum, 'whitespace/semicolon', 5, 'Extra space before last semicolon. If this should be an empty ' 'statement, use {} instead.') def IsDecltype(clean_lines, linenum, column): """Check if the token ending on (linenum, column) is decltype(). Args: clean_lines: A CleansedLines instance containing the file. linenum: the number of the line to check. column: end column of the token to check. Returns: True if this token is decltype() expression, False otherwise. """ (text, _, start_col) = ReverseCloseExpression(clean_lines, linenum, column) if start_col < 0: return False if Search(r'\bdecltype\s*$', text[0:start_col]): return True return False def IsTemplateParameterList(clean_lines, linenum, column): """Check if the token ending on (linenum, column) is the end of template<>. Args: clean_lines: A CleansedLines instance containing the file. linenum: the number of the line to check. column: end column of the token to check. Returns: True if this token is end of a template parameter list, False otherwise. """ (_, startline, startpos) = ReverseCloseExpression( clean_lines, linenum, column) if (startpos > -1 and Search(r'\btemplate\s*$', clean_lines.elided[startline][0:startpos])): return True return False def IsRValueType(typenames, clean_lines, nesting_state, linenum, column): """Check if the token ending on (linenum, column) is a type. Assumes that text to the right of the column is "&&" or a function name. Args: typenames: set of type names from template-argument-list. clean_lines: A CleansedLines instance containing the file. nesting_state: A NestingState instance which maintains information about the current stack of nested blocks being parsed. linenum: the number of the line to check. column: end column of the token to check. Returns: True if this token is a type, False if we are not sure. """ prefix = clean_lines.elided[linenum][0:column] # Get one word to the left. If we failed to do so, this is most # likely not a type, since it's unlikely that the type name and "&&" # would be split across multiple lines. match = Match(r'^(.*)(\b\w+|[>*)&])\s*$', prefix) if not match: return False # Check text following the token. If it's "&&>" or "&&," or "&&...", it's # most likely a rvalue reference used inside a template. suffix = clean_lines.elided[linenum][column:] if Match(r'&&\s*(?:[>,]|\.\.\.)', suffix): return True # Check for known types and end of templates: # int&& variable # vector&& variable # # Because this function is called recursively, we also need to # recognize pointer and reference types: # int* Function() # int& Function() if (match.group(2) in typenames or match.group(2) in ['char', 'char16_t', 'char32_t', 'wchar_t', 'bool', 'short', 'int', 'long', 'signed', 'unsigned', 'float', 'double', 'void', 'auto', '>', '*', '&']): return True # If we see a close parenthesis, look for decltype on the other side. # decltype would unambiguously identify a type, anything else is # probably a parenthesized expression and not a type. if match.group(2) == ')': return IsDecltype( clean_lines, linenum, len(match.group(1)) + len(match.group(2)) - 1) # Check for casts and cv-qualifiers. # match.group(1) remainder # -------------- --------- # const_cast< type&& # const type&& # type const&& if Search(r'\b(?:const_cast\s*<|static_cast\s*<|dynamic_cast\s*<|' r'reinterpret_cast\s*<|\w+\s)\s*$', match.group(1)): return True # Look for a preceding symbol that might help differentiate the context. # These are the cases that would be ambiguous: # match.group(1) remainder # -------------- --------- # Call ( expression && # Declaration ( type&& # sizeof ( type&& # if ( expression && # while ( expression && # for ( type&& # for( ; expression && # statement ; type&& # block { type&& # constructor { expression && start = linenum line = match.group(1) match_symbol = None while start >= 0: # We want to skip over identifiers and commas to get to a symbol. # Commas are skipped so that we can find the opening parenthesis # for function parameter lists. match_symbol = Match(r'^(.*)([^\w\s,])[\w\s,]*$', line) if match_symbol: break start -= 1 line = clean_lines.elided[start] if not match_symbol: # Probably the first statement in the file is an rvalue reference return True if match_symbol.group(2) == '}': # Found closing brace, probably an indicate of this: # block{} type&& return True if match_symbol.group(2) == ';': # Found semicolon, probably one of these: # for(; expression && # statement; type&& # Look for the previous 'for(' in the previous lines. before_text = match_symbol.group(1) for i in xrange(start - 1, max(start - 6, 0), -1): before_text = clean_lines.elided[i] + before_text if Search(r'for\s*\([^{};]*$', before_text): # This is the condition inside a for-loop return False # Did not find a for-init-statement before this semicolon, so this # is probably a new statement and not a condition. return True if match_symbol.group(2) == '{': # Found opening brace, probably one of these: # block{ type&& = ... ; } # constructor{ expression && expression } # Look for a closing brace or a semicolon. If we see a semicolon # first, this is probably a rvalue reference. line = clean_lines.elided[start][0:len(match_symbol.group(1)) + 1] end = start depth = 1 while True: for ch in line: if ch == ';': return True elif ch == '{': depth += 1 elif ch == '}': depth -= 1 if depth == 0: return False end += 1 if end >= clean_lines.NumLines(): break line = clean_lines.elided[end] # Incomplete program? return False if match_symbol.group(2) == '(': # Opening parenthesis. Need to check what's to the left of the # parenthesis. Look back one extra line for additional context. before_text = match_symbol.group(1) if linenum > 1: before_text = clean_lines.elided[linenum - 1] + before_text before_text = match_symbol.group(1) # Patterns that are likely to be types: # [](type&& # for (type&& # sizeof(type&& # operator=(type&& # if Search(r'(?:\]|\bfor|\bsizeof|\boperator\s*\S+\s*)\s*$', before_text): return True # Patterns that are likely to be expressions: # if (expression && # while (expression && # : initializer(expression && # , initializer(expression && # ( FunctionCall(expression && # + FunctionCall(expression && # + (expression && # # The last '+' represents operators such as '+' and '-'. if Search(r'(?:\bif|\bwhile|[-+=%^(]*>)?\s*$', match_symbol.group(1)) if match_func: # Check for constructors, which don't have return types. if Search(r'\b(?:explicit|inline)$', match_func.group(1)): return True implicit_constructor = Match(r'\s*(\w+)\((?:const\s+)?(\w+)', prefix) if (implicit_constructor and implicit_constructor.group(1) == implicit_constructor.group(2)): return True return IsRValueType(typenames, clean_lines, nesting_state, linenum, len(match_func.group(1))) # Nothing before the function name. If this is inside a block scope, # this is probably a function call. return not (nesting_state.previous_stack_top and nesting_state.previous_stack_top.IsBlockInfo()) if match_symbol.group(2) == '>': # Possibly a closing bracket, check that what's on the other side # looks like the start of a template. return IsTemplateParameterList( clean_lines, start, len(match_symbol.group(1))) # Some other symbol, usually something like "a=b&&c". This is most # likely not a type. return False def IsDeletedOrDefault(clean_lines, linenum): """Check if current constructor or operator is deleted or default. Args: clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. Returns: True if this is a deleted or default constructor. """ open_paren = clean_lines.elided[linenum].find('(') if open_paren < 0: return False (close_line, _, close_paren) = CloseExpression( clean_lines, linenum, open_paren) if close_paren < 0: return False return Match(r'\s*=\s*(?:delete|default)\b', close_line[close_paren:]) def IsRValueAllowed(clean_lines, linenum, typenames): """Check if RValue reference is allowed on a particular line. Args: clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. typenames: set of type names from template-argument-list. Returns: True if line is within the region where RValue references are allowed. """ # Allow region marked by PUSH/POP macros for i in xrange(linenum, 0, -1): line = clean_lines.elided[i] if Match(r'GOOGLE_ALLOW_RVALUE_REFERENCES_(?:PUSH|POP)', line): if not line.endswith('PUSH'): return False for j in xrange(linenum, clean_lines.NumLines(), 1): line = clean_lines.elided[j] if Match(r'GOOGLE_ALLOW_RVALUE_REFERENCES_(?:PUSH|POP)', line): return line.endswith('POP') # Allow operator= line = clean_lines.elided[linenum] if Search(r'\boperator\s*=\s*\(', line): return IsDeletedOrDefault(clean_lines, linenum) # Allow constructors match = Match(r'\s*(?:[\w<>]+::)*([\w<>]+)\s*::\s*([\w<>]+)\s*\(', line) if match and match.group(1) == match.group(2): return IsDeletedOrDefault(clean_lines, linenum) if Search(r'\b(?:explicit|inline)\s+[\w<>]+\s*\(', line): return IsDeletedOrDefault(clean_lines, linenum) if Match(r'\s*[\w<>]+\s*\(', line): previous_line = 'ReturnType' if linenum > 0: previous_line = clean_lines.elided[linenum - 1] if Match(r'^\s*$', previous_line) or Search(r'[{}:;]\s*$', previous_line): return IsDeletedOrDefault(clean_lines, linenum) # Reject types not mentioned in template-argument-list while line: match = Match(r'^.*?(\w+)\s*&&(.*)$', line) if not match: break if match.group(1) not in typenames: return False line = match.group(2) # All RValue types that were in template-argument-list should have # been removed by now. Those were allowed, assuming that they will # be forwarded. # # If there are no remaining RValue types left (i.e. types that were # not found in template-argument-list), flag those as not allowed. return line.find('&&') < 0 def GetTemplateArgs(clean_lines, linenum): """Find list of template arguments associated with this function declaration. Args: clean_lines: A CleansedLines instance containing the file. linenum: Line number containing the start of the function declaration, usually one line after the end of the template-argument-list. Returns: Set of type names, or empty set if this does not appear to have any template parameters. """ # Find start of function func_line = linenum while func_line > 0: line = clean_lines.elided[func_line] if Match(r'^\s*$', line): return set() if line.find('(') >= 0: break func_line -= 1 if func_line == 0: return set() # Collapse template-argument-list into a single string argument_list = '' match = Match(r'^(\s*template\s*)<', clean_lines.elided[func_line]) if match: # template-argument-list on the same line as function name start_col = len(match.group(1)) _, end_line, end_col = CloseExpression(clean_lines, func_line, start_col) if end_col > -1 and end_line == func_line: start_col += 1 # Skip the opening bracket argument_list = clean_lines.elided[func_line][start_col:end_col] elif func_line > 1: # template-argument-list one line before function name match = Match(r'^(.*)>\s*$', clean_lines.elided[func_line - 1]) if match: end_col = len(match.group(1)) _, start_line, start_col = ReverseCloseExpression( clean_lines, func_line - 1, end_col) if start_col > -1: start_col += 1 # Skip the opening bracket while start_line < func_line - 1: argument_list += clean_lines.elided[start_line][start_col:] start_col = 0 start_line += 1 argument_list += clean_lines.elided[func_line - 1][start_col:end_col] if not argument_list: return set() # Extract type names typenames = set() while True: match = Match(r'^[,\s]*(?:typename|class)(?:\.\.\.)?\s+(\w+)(.*)$', argument_list) if not match: break typenames.add(match.group(1)) argument_list = match.group(2) return typenames def CheckRValueReference(filename, clean_lines, linenum, nesting_state, error): """Check for rvalue references. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. nesting_state: A NestingState instance which maintains information about the current stack of nested blocks being parsed. error: The function to call with any errors found. """ # Find lines missing spaces around &&. # TODO(unknown): currently we don't check for rvalue references # with spaces surrounding the && to avoid false positives with # boolean expressions. line = clean_lines.elided[linenum] match = Match(r'^(.*\S)&&', line) if not match: match = Match(r'(.*)&&\S', line) if (not match) or '(&&)' in line or Search(r'\boperator\s*$', match.group(1)): return # Either poorly formed && or an rvalue reference, check the context # to get a more accurate error message. Mostly we want to determine # if what's to the left of "&&" is a type or not. typenames = GetTemplateArgs(clean_lines, linenum) and_pos = len(match.group(1)) if IsRValueType(typenames, clean_lines, nesting_state, linenum, and_pos): if not IsRValueAllowed(clean_lines, linenum, typenames): error(filename, linenum, 'build/c++11', 3, 'RValue references are an unapproved C++ feature.') else: error(filename, linenum, 'whitespace/operators', 3, 'Missing spaces around &&') def CheckSectionSpacing(filename, clean_lines, class_info, linenum, error): """Checks for additional blank line issues related to sections. Currently the only thing checked here is blank line before protected/private. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. class_info: A _ClassInfo objects. linenum: The number of the line to check. error: The function to call with any errors found. """ # Skip checks if the class is small, where small means 25 lines or less. # 25 lines seems like a good cutoff since that's the usual height of # terminals, and any class that can't fit in one screen can't really # be considered "small". # # Also skip checks if we are on the first line. This accounts for # classes that look like # class Foo { public: ... }; # # If we didn't find the end of the class, last_line would be zero, # and the check will be skipped by the first condition. if (class_info.last_line - class_info.starting_linenum <= 24 or linenum <= class_info.starting_linenum): return matched = Match(r'\s*(public|protected|private):', clean_lines.lines[linenum]) if matched: # Issue warning if the line before public/protected/private was # not a blank line, but don't do this if the previous line contains # "class" or "struct". This can happen two ways: # - We are at the beginning of the class. # - We are forward-declaring an inner class that is semantically # private, but needed to be public for implementation reasons. # Also ignores cases where the previous line ends with a backslash as can be # common when defining classes in C macros. prev_line = clean_lines.lines[linenum - 1] if (not IsBlankLine(prev_line) and not Search(r'\b(class|struct)\b', prev_line) and not Search(r'\\$', prev_line)): # Try a bit harder to find the beginning of the class. This is to # account for multi-line base-specifier lists, e.g.: # class Derived # : public Base { end_class_head = class_info.starting_linenum for i in range(class_info.starting_linenum, linenum): if Search(r'\{\s*$', clean_lines.lines[i]): end_class_head = i break if end_class_head < linenum - 1: error(filename, linenum, 'whitespace/blank_line', 3, '"%s:" should be preceded by a blank line' % matched.group(1)) def GetPreviousNonBlankLine(clean_lines, linenum): """Return the most recent non-blank line and its line number. Args: clean_lines: A CleansedLines instance containing the file contents. linenum: The number of the line to check. Returns: A tuple with two elements. The first element is the contents of the last non-blank line before the current line, or the empty string if this is the first non-blank line. The second is the line number of that line, or -1 if this is the first non-blank line. """ prevlinenum = linenum - 1 while prevlinenum >= 0: prevline = clean_lines.elided[prevlinenum] if not IsBlankLine(prevline): # if not a blank line... return (prevline, prevlinenum) prevlinenum -= 1 return ('', -1) def CheckBraces(filename, clean_lines, linenum, error): """Looks for misplaced braces (e.g. at the end of line). Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] # get rid of comments and strings if Match(r'\s*{\s*$', line): # We allow an open brace to start a line in the case where someone is using # braces in a block to explicitly create a new scope, which is commonly used # to control the lifetime of stack-allocated variables. Braces are also # used for brace initializers inside function calls. We don't detect this # perfectly: we just don't complain if the last non-whitespace character on # the previous non-blank line is ',', ';', ':', '(', '{', or '}', or if the # previous line starts a preprocessor block. prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] if (not Search(r'[,;:}{(]\s*$', prevline) and not Match(r'\s*#', prevline)): error(filename, linenum, 'whitespace/braces', 4, '{ should almost always be at the end of the previous line') # An else clause should be on the same line as the preceding closing brace. if Match(r'\s*else\b\s*(?:if\b|\{|$)', line): prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] if Match(r'\s*}\s*$', prevline): error(filename, linenum, 'whitespace/newline', 4, 'An else should appear on the same line as the preceding }') # If braces come on one side of an else, they should be on both. # However, we have to worry about "else if" that spans multiple lines! if Search(r'else if\s*\(', line): # could be multi-line if brace_on_left = bool(Search(r'}\s*else if\s*\(', line)) # find the ( after the if pos = line.find('else if') pos = line.find('(', pos) if pos > 0: (endline, _, endpos) = CloseExpression(clean_lines, linenum, pos) brace_on_right = endline[endpos:].find('{') != -1 if brace_on_left != brace_on_right: # must be brace after if error(filename, linenum, 'readability/braces', 5, 'If an else has a brace on one side, it should have it on both') elif Search(r'}\s*else[^{]*$', line) or Match(r'[^}]*else\s*{', line): error(filename, linenum, 'readability/braces', 5, 'If an else has a brace on one side, it should have it on both') # Likewise, an else should never have the else clause on the same line if Search(r'\belse [^\s{]', line) and not Search(r'\belse if\b', line): error(filename, linenum, 'whitespace/newline', 4, 'Else clause should never be on same line as else (use 2 lines)') # In the same way, a do/while should never be on one line if Match(r'\s*do [^\s{]', line): error(filename, linenum, 'whitespace/newline', 4, 'do/while clauses should not be on a single line') # Check single-line if/else bodies. The style guide says 'curly braces are not # required for single-line statements'. We additionally allow multi-line, # single statements, but we reject anything with more than one semicolon in # it. This means that the first semicolon after the if should be at the end of # its line, and the line after that should have an indent level equal to or # lower than the if. We also check for ambiguous if/else nesting without # braces. if_else_match = Search(r'\b(if\s*\(|else\b)', line) if if_else_match and not Match(r'\s*#', line): if_indent = GetIndentLevel(line) endline, endlinenum, endpos = line, linenum, if_else_match.end() if_match = Search(r'\bif\s*\(', line) if if_match: # This could be a multiline if condition, so find the end first. pos = if_match.end() - 1 (endline, endlinenum, endpos) = CloseExpression(clean_lines, linenum, pos) # Check for an opening brace, either directly after the if or on the next # line. If found, this isn't a single-statement conditional. if (not Match(r'\s*{', endline[endpos:]) and not (Match(r'\s*$', endline[endpos:]) and endlinenum < (len(clean_lines.elided) - 1) and Match(r'\s*{', clean_lines.elided[endlinenum + 1]))): while (endlinenum < len(clean_lines.elided) and ';' not in clean_lines.elided[endlinenum][endpos:]): endlinenum += 1 endpos = 0 if endlinenum < len(clean_lines.elided): endline = clean_lines.elided[endlinenum] # We allow a mix of whitespace and closing braces (e.g. for one-liner # methods) and a single \ after the semicolon (for macros) endpos = endline.find(';') if not Match(r';[\s}]*(\\?)$', endline[endpos:]): # Semicolon isn't the last character, there's something trailing. # Output a warning if the semicolon is not contained inside # a lambda expression. if not Match(r'^[^{};]*\[[^\[\]]*\][^{}]*\{[^{}]*\}\s*\)*[;,]\s*$', endline): error(filename, linenum, 'readability/braces', 4, 'If/else bodies with multiple statements require braces') elif endlinenum < len(clean_lines.elided) - 1: # Make sure the next line is dedented next_line = clean_lines.elided[endlinenum + 1] next_indent = GetIndentLevel(next_line) # With ambiguous nested if statements, this will error out on the # if that *doesn't* match the else, regardless of whether it's the # inner one or outer one. if (if_match and Match(r'\s*else\b', next_line) and next_indent != if_indent): error(filename, linenum, 'readability/braces', 4, 'Else clause should be indented at the same level as if. ' 'Ambiguous nested if/else chains require braces.') elif next_indent > if_indent: error(filename, linenum, 'readability/braces', 4, 'If/else bodies with multiple statements require braces') def CheckTrailingSemicolon(filename, clean_lines, linenum, error): """Looks for redundant trailing semicolon. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] # Block bodies should not be followed by a semicolon. Due to C++11 # brace initialization, there are more places where semicolons are # required than not, so we use a whitelist approach to check these # rather than a blacklist. These are the places where "};" should # be replaced by just "}": # 1. Some flavor of block following closing parenthesis: # for (;;) {}; # while (...) {}; # switch (...) {}; # Function(...) {}; # if (...) {}; # if (...) else if (...) {}; # # 2. else block: # if (...) else {}; # # 3. const member function: # Function(...) const {}; # # 4. Block following some statement: # x = 42; # {}; # # 5. Block at the beginning of a function: # Function(...) { # {}; # } # # Note that naively checking for the preceding "{" will also match # braces inside multi-dimensional arrays, but this is fine since # that expression will not contain semicolons. # # 6. Block following another block: # while (true) {} # {}; # # 7. End of namespaces: # namespace {}; # # These semicolons seems far more common than other kinds of # redundant semicolons, possibly due to people converting classes # to namespaces. For now we do not warn for this case. # # Try matching case 1 first. match = Match(r'^(.*\)\s*)\{', line) if match: # Matched closing parenthesis (case 1). Check the token before the # matching opening parenthesis, and don't warn if it looks like a # macro. This avoids these false positives: # - macro that defines a base class # - multi-line macro that defines a base class # - macro that defines the whole class-head # # But we still issue warnings for macros that we know are safe to # warn, specifically: # - TEST, TEST_F, TEST_P, MATCHER, MATCHER_P # - TYPED_TEST # - INTERFACE_DEF # - EXCLUSIVE_LOCKS_REQUIRED, SHARED_LOCKS_REQUIRED, LOCKS_EXCLUDED: # # We implement a whitelist of safe macros instead of a blacklist of # unsafe macros, even though the latter appears less frequently in # google code and would have been easier to implement. This is because # the downside for getting the whitelist wrong means some extra # semicolons, while the downside for getting the blacklist wrong # would result in compile errors. # # In addition to macros, we also don't want to warn on # - Compound literals # - Lambdas # - alignas specifier with anonymous structs: closing_brace_pos = match.group(1).rfind(')') opening_parenthesis = ReverseCloseExpression( clean_lines, linenum, closing_brace_pos) if opening_parenthesis[2] > -1: line_prefix = opening_parenthesis[0][0:opening_parenthesis[2]] macro = Search(r'\b([A-Z_]+)\s*$', line_prefix) func = Match(r'^(.*\])\s*$', line_prefix) if ((macro and macro.group(1) not in ( 'TEST', 'TEST_F', 'MATCHER', 'MATCHER_P', 'TYPED_TEST', 'EXCLUSIVE_LOCKS_REQUIRED', 'SHARED_LOCKS_REQUIRED', 'LOCKS_EXCLUDED', 'INTERFACE_DEF')) or (func and not Search(r'\boperator\s*\[\s*\]', func.group(1))) or Search(r'\b(?:struct|union)\s+alignas\s*$', line_prefix) or Search(r'\s+=\s*$', line_prefix)): match = None if (match and opening_parenthesis[1] > 1 and Search(r'\]\s*$', clean_lines.elided[opening_parenthesis[1] - 1])): # Multi-line lambda-expression match = None else: # Try matching cases 2-3. match = Match(r'^(.*(?:else|\)\s*const)\s*)\{', line) if not match: # Try matching cases 4-6. These are always matched on separate lines. # # Note that we can't simply concatenate the previous line to the # current line and do a single match, otherwise we may output # duplicate warnings for the blank line case: # if (cond) { # // blank line # } prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] if prevline and Search(r'[;{}]\s*$', prevline): match = Match(r'^(\s*)\{', line) # Check matching closing brace if match: (endline, endlinenum, endpos) = CloseExpression( clean_lines, linenum, len(match.group(1))) if endpos > -1 and Match(r'^\s*;', endline[endpos:]): # Current {} pair is eligible for semicolon check, and we have found # the redundant semicolon, output warning here. # # Note: because we are scanning forward for opening braces, and # outputting warnings for the matching closing brace, if there are # nested blocks with trailing semicolons, we will get the error # messages in reversed order. error(filename, endlinenum, 'readability/braces', 4, "You don't need a ; after a }") def CheckEmptyBlockBody(filename, clean_lines, linenum, error): """Look for empty loop/conditional body with only a single semicolon. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ # Search for loop keywords at the beginning of the line. Because only # whitespaces are allowed before the keywords, this will also ignore most # do-while-loops, since those lines should start with closing brace. # # We also check "if" blocks here, since an empty conditional block # is likely an error. line = clean_lines.elided[linenum] matched = Match(r'\s*(for|while|if)\s*\(', line) if matched: # Find the end of the conditional expression (end_line, end_linenum, end_pos) = CloseExpression( clean_lines, linenum, line.find('(')) # Output warning if what follows the condition expression is a semicolon. # No warning for all other cases, including whitespace or newline, since we # have a separate check for semicolons preceded by whitespace. if end_pos >= 0 and Match(r';', end_line[end_pos:]): if matched.group(1) == 'if': error(filename, end_linenum, 'whitespace/empty_conditional_body', 5, 'Empty conditional bodies should use {}') else: error(filename, end_linenum, 'whitespace/empty_loop_body', 5, 'Empty loop bodies should use {} or continue') def FindCheckMacro(line): """Find a replaceable CHECK-like macro. Args: line: line to search on. Returns: (macro name, start position), or (None, -1) if no replaceable macro is found. """ for macro in _CHECK_MACROS: i = line.find(macro) if i >= 0: # Find opening parenthesis. Do a regular expression match here # to make sure that we are matching the expected CHECK macro, as # opposed to some other macro that happens to contain the CHECK # substring. matched = Match(r'^(.*\b' + macro + r'\s*)\(', line) if not matched: continue return (macro, len(matched.group(1))) return (None, -1) def CheckCheck(filename, clean_lines, linenum, error): """Checks the use of CHECK and EXPECT macros. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ # Decide the set of replacement macros that should be suggested lines = clean_lines.elided (check_macro, start_pos) = FindCheckMacro(lines[linenum]) if not check_macro: return # Find end of the boolean expression by matching parentheses (last_line, end_line, end_pos) = CloseExpression( clean_lines, linenum, start_pos) if end_pos < 0: return # If the check macro is followed by something other than a # semicolon, assume users will log their own custom error messages # and don't suggest any replacements. if not Match(r'\s*;', last_line[end_pos:]): return if linenum == end_line: expression = lines[linenum][start_pos + 1:end_pos - 1] else: expression = lines[linenum][start_pos + 1:] for i in xrange(linenum + 1, end_line): expression += lines[i] expression += last_line[0:end_pos - 1] # Parse expression so that we can take parentheses into account. # This avoids false positives for inputs like "CHECK((a < 4) == b)", # which is not replaceable by CHECK_LE. lhs = '' rhs = '' operator = None while expression: matched = Match(r'^\s*(<<|<<=|>>|>>=|->\*|->|&&|\|\||' r'==|!=|>=|>|<=|<|\()(.*)$', expression) if matched: token = matched.group(1) if token == '(': # Parenthesized operand expression = matched.group(2) (end, _) = FindEndOfExpressionInLine(expression, 0, ['(']) if end < 0: return # Unmatched parenthesis lhs += '(' + expression[0:end] expression = expression[end:] elif token in ('&&', '||'): # Logical and/or operators. This means the expression # contains more than one term, for example: # CHECK(42 < a && a < b); # # These are not replaceable with CHECK_LE, so bail out early. return elif token in ('<<', '<<=', '>>', '>>=', '->*', '->'): # Non-relational operator lhs += token expression = matched.group(2) else: # Relational operator operator = token rhs = matched.group(2) break else: # Unparenthesized operand. Instead of appending to lhs one character # at a time, we do another regular expression match to consume several # characters at once if possible. Trivial benchmark shows that this # is more efficient when the operands are longer than a single # character, which is generally the case. matched = Match(r'^([^-=!<>()&|]+)(.*)$', expression) if not matched: matched = Match(r'^(\s*\S)(.*)$', expression) if not matched: break lhs += matched.group(1) expression = matched.group(2) # Only apply checks if we got all parts of the boolean expression if not (lhs and operator and rhs): return # Check that rhs do not contain logical operators. We already know # that lhs is fine since the loop above parses out && and ||. if rhs.find('&&') > -1 or rhs.find('||') > -1: return # At least one of the operands must be a constant literal. This is # to avoid suggesting replacements for unprintable things like # CHECK(variable != iterator) # # The following pattern matches decimal, hex integers, strings, and # characters (in that order). lhs = lhs.strip() rhs = rhs.strip() match_constant = r'^([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')$' if Match(match_constant, lhs) or Match(match_constant, rhs): # Note: since we know both lhs and rhs, we can provide a more # descriptive error message like: # Consider using CHECK_EQ(x, 42) instead of CHECK(x == 42) # Instead of: # Consider using CHECK_EQ instead of CHECK(a == b) # # We are still keeping the less descriptive message because if lhs # or rhs gets long, the error message might become unreadable. error(filename, linenum, 'readability/check', 2, 'Consider using %s instead of %s(a %s b)' % ( _CHECK_REPLACEMENT[check_macro][operator], check_macro, operator)) def CheckAltTokens(filename, clean_lines, linenum, error): """Check alternative keywords being used in boolean expressions. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] # Avoid preprocessor lines if Match(r'^\s*#', line): return # Last ditch effort to avoid multi-line comments. This will not help # if the comment started before the current line or ended after the # current line, but it catches most of the false positives. At least, # it provides a way to workaround this warning for people who use # multi-line comments in preprocessor macros. # # TODO(unknown): remove this once cpplint has better support for # multi-line comments. if line.find('/*') >= 0 or line.find('*/') >= 0: return for match in _ALT_TOKEN_REPLACEMENT_PATTERN.finditer(line): error(filename, linenum, 'readability/alt_tokens', 2, 'Use operator %s instead of %s' % ( _ALT_TOKEN_REPLACEMENT[match.group(1)], match.group(1))) def GetLineWidth(line): """Determines the width of the line in column positions. Args: line: A string, which may be a Unicode string. Returns: The width of the line in column positions, accounting for Unicode combining characters and wide characters. """ if isinstance(line, unicode): width = 0 for uc in unicodedata.normalize('NFC', line): if unicodedata.east_asian_width(uc) in ('W', 'F'): width += 2 elif not unicodedata.combining(uc): width += 1 return width else: return len(line) def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state, error): """Checks rules from the 'C++ style rules' section of cppguide.html. Most of these rules are hard to test (naming, comment style), but we do what we can. In particular we check for 2-space indents, line lengths, tab usage, spaces inside code, etc. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. file_extension: The extension (without the dot) of the filename. nesting_state: A NestingState instance which maintains information about the current stack of nested blocks being parsed. error: The function to call with any errors found. """ # Don't use "elided" lines here, otherwise we can't check commented lines. # Don't want to use "raw" either, because we don't want to check inside C++11 # raw strings, raw_lines = clean_lines.lines_without_raw_strings line = raw_lines[linenum] if line.find('\t') != -1: error(filename, linenum, 'whitespace/tab', 1, 'Tab found; better to use spaces') # One or three blank spaces at the beginning of the line is weird; it's # hard to reconcile that with 2-space indents. # NOTE: here are the conditions rob pike used for his tests. Mine aren't # as sophisticated, but it may be worth becoming so: RLENGTH==initial_spaces # if(RLENGTH > 20) complain = 0; # if(match($0, " +(error|private|public|protected):")) complain = 0; # if(match(prev, "&& *$")) complain = 0; # if(match(prev, "\\|\\| *$")) complain = 0; # if(match(prev, "[\",=><] *$")) complain = 0; # if(match($0, " <<")) complain = 0; # if(match(prev, " +for \\(")) complain = 0; # if(prevodd && match(prevprev, " +for \\(")) complain = 0; scope_or_label_pattern = r'\s*\w+\s*:\s*\\?$' classinfo = nesting_state.InnermostClass() initial_spaces = 0 cleansed_line = clean_lines.elided[linenum] while initial_spaces < len(line) and line[initial_spaces] == ' ': initial_spaces += 1 if line and line[-1].isspace(): error(filename, linenum, 'whitespace/end_of_line', 4, 'Line ends in whitespace. Consider deleting these extra spaces.') # There are certain situations we allow one space, notably for # section labels, and also lines containing multi-line raw strings. elif ((initial_spaces == 1 or initial_spaces == 3) and not Match(scope_or_label_pattern, cleansed_line) and not (clean_lines.raw_lines[linenum] != line and Match(r'^\s*""', line))): error(filename, linenum, 'whitespace/indent', 3, 'Weird number of spaces at line-start. ' 'Are you using a 2-space indent?') # Check if the line is a header guard. is_header_guard = False if file_extension == 'h' or file_extension == 'hpp': cppvar = GetHeaderGuardCPPVariable(filename) if (line.startswith('#ifndef %s' % cppvar) or line.startswith('#define %s' % cppvar) or line.startswith('#endif // %s' % cppvar)): is_header_guard = True # #include lines and header guards can be long, since there's no clean way to # split them. # # URLs can be long too. It's possible to split these, but it makes them # harder to cut&paste. # # The "$Id:...$" comment may also get very long without it being the # developers fault. if (not line.startswith('#include') and not is_header_guard and not Match(r'^\s*//.*http(s?)://\S*$', line) and not Match(r'^// \$Id:.*#[0-9]+ \$$', line)): line_width = GetLineWidth(line) extended_length = int((_line_length * 1.25)) if line_width > extended_length: error(filename, linenum, 'whitespace/line_length', 4, 'Lines should very rarely be longer than %i characters' % extended_length) elif line_width > _line_length: error(filename, linenum, 'whitespace/line_length', 2, 'Lines should be <= %i characters long' % _line_length) if (cleansed_line.count(';') > 1 and # for loops are allowed two ;'s (and may run over two lines). cleansed_line.find('for') == -1 and (GetPreviousNonBlankLine(clean_lines, linenum)[0].find('for') == -1 or GetPreviousNonBlankLine(clean_lines, linenum)[0].find(';') != -1) and # It's ok to have many commands in a switch case that fits in 1 line not ((cleansed_line.find('case ') != -1 or cleansed_line.find('default:') != -1) and cleansed_line.find('break;') != -1)): error(filename, linenum, 'whitespace/newline', 0, 'More than one command on the same line') # Some more style checks CheckBraces(filename, clean_lines, linenum, error) CheckTrailingSemicolon(filename, clean_lines, linenum, error) CheckEmptyBlockBody(filename, clean_lines, linenum, error) CheckAccess(filename, clean_lines, linenum, nesting_state, error) CheckSpacing(filename, clean_lines, linenum, nesting_state, error) CheckOperatorSpacing(filename, clean_lines, linenum, error) CheckParenthesisSpacing(filename, clean_lines, linenum, error) CheckCommaSpacing(filename, clean_lines, linenum, error) CheckBracesSpacing(filename, clean_lines, linenum, error) CheckSpacingForFunctionCall(filename, clean_lines, linenum, error) CheckRValueReference(filename, clean_lines, linenum, nesting_state, error) CheckCheck(filename, clean_lines, linenum, error) CheckAltTokens(filename, clean_lines, linenum, error) classinfo = nesting_state.InnermostClass() if classinfo: CheckSectionSpacing(filename, clean_lines, classinfo, linenum, error) _RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*include\s*([<"])([^>"]*)[>"].*$') # Matches the first component of a filename delimited by -s and _s. That is: # _RE_FIRST_COMPONENT.match('foo').group(0) == 'foo' # _RE_FIRST_COMPONENT.match('foo.cc').group(0) == 'foo' # _RE_FIRST_COMPONENT.match('foo-bar_baz.cc').group(0) == 'foo' # _RE_FIRST_COMPONENT.match('foo_bar-baz.cc').group(0) == 'foo' _RE_FIRST_COMPONENT = re.compile(r'^[^-_.]+') def _DropCommonSuffixes(filename): """Drops common suffixes like _test.cc or -inl.h from filename. For example: >>> _DropCommonSuffixes('foo/foo-inl.h') 'foo/foo' >>> _DropCommonSuffixes('foo/bar/foo.cc') 'foo/bar/foo' >>> _DropCommonSuffixes('foo/foo_internal.h') 'foo/foo' >>> _DropCommonSuffixes('foo/foo_unusualinternal.h') 'foo/foo_unusualinternal' Args: filename: The input filename. Returns: The filename with the common suffix removed. """ for suffix in ('test.cc', 'regtest.cc', 'unittest.cc', 'inl.h', 'impl.h', 'internal.h'): if (filename.endswith(suffix) and len(filename) > len(suffix) and filename[-len(suffix) - 1] in ('-', '_')): return filename[:-len(suffix) - 1] return os.path.splitext(filename)[0] def _IsTestFilename(filename): """Determines if the given filename has a suffix that identifies it as a test. Args: filename: The input filename. Returns: True if 'filename' looks like a test, False otherwise. """ if (filename.endswith('_test.cc') or filename.endswith('_unittest.cc') or filename.endswith('_regtest.cc')): return True else: return False def _ClassifyInclude(fileinfo, include, is_system): """Figures out what kind of header 'include' is. Args: fileinfo: The current file cpplint is running over. A FileInfo instance. include: The path to a #included file. is_system: True if the #include used <> rather than "". Returns: One of the _XXX_HEADER constants. For example: >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'stdio.h', True) _C_SYS_HEADER >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'string', True) _CPP_SYS_HEADER >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/foo.h', False) _LIKELY_MY_HEADER >>> _ClassifyInclude(FileInfo('foo/foo_unknown_extension.cc'), ... 'bar/foo_other_ext.h', False) _POSSIBLE_MY_HEADER >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/bar.h', False) _OTHER_HEADER """ # This is a list of all standard c++ header files, except # those already checked for above. is_cpp_h = include in _CPP_HEADERS if is_system: if is_cpp_h: return _CPP_SYS_HEADER else: return _C_SYS_HEADER # If the target file and the include we're checking share a # basename when we drop common extensions, and the include # lives in . , then it's likely to be owned by the target file. target_dir, target_base = ( os.path.split(_DropCommonSuffixes(fileinfo.RepositoryName()))) include_dir, include_base = os.path.split(_DropCommonSuffixes(include)) if target_base == include_base and ( include_dir == target_dir or include_dir == os.path.normpath(target_dir + '/../public')): return _LIKELY_MY_HEADER # If the target and include share some initial basename # component, it's possible the target is implementing the # include, so it's allowed to be first, but we'll never # complain if it's not there. target_first_component = _RE_FIRST_COMPONENT.match(target_base) include_first_component = _RE_FIRST_COMPONENT.match(include_base) if (target_first_component and include_first_component and target_first_component.group(0) == include_first_component.group(0)): return _POSSIBLE_MY_HEADER return _OTHER_HEADER def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): """Check rules that are applicable to #include lines. Strings on #include lines are NOT removed from elided line, to make certain tasks easier. However, to prevent false positives, checks applicable to #include lines in CheckLanguage must be put here. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. include_state: An _IncludeState instance in which the headers are inserted. error: The function to call with any errors found. """ fileinfo = FileInfo(filename) line = clean_lines.lines[linenum] # "include" should use the new style "foo/bar.h" instead of just "bar.h" # Only do this check if the included header follows google naming # conventions. If not, assume that it's a 3rd party API that # requires special include conventions. # # We also make an exception for Lua headers, which follow google # naming convention but not the include convention. match = Match(r'#include\s*"([^/]+\.h)"', line) if match and not _THIRD_PARTY_HEADERS_PATTERN.match(match.group(1)): error(filename, linenum, 'build/include', 4, 'Include the directory when naming .h files') # we shouldn't include a file more than once. actually, there are a # handful of instances where doing so is okay, but in general it's # not. match = _RE_PATTERN_INCLUDE.search(line) if match: include = match.group(2) is_system = (match.group(1) == '<') duplicate_line = include_state.FindHeader(include) if duplicate_line >= 0: error(filename, linenum, 'build/include', 4, '"%s" already included at %s:%s' % (include, filename, duplicate_line)) elif (include.endswith('.cc') and os.path.dirname(fileinfo.RepositoryName()) != os.path.dirname(include)): error(filename, linenum, 'build/include', 4, 'Do not include .cc files from other packages') elif not _THIRD_PARTY_HEADERS_PATTERN.match(include): include_state.include_list[-1].append((include, linenum)) # We want to ensure that headers appear in the right order: # 1) for foo.cc, foo.h (preferred location) # 2) c system files # 3) cpp system files # 4) for foo.cc, foo.h (deprecated location) # 5) other google headers # # We classify each include statement as one of those 5 types # using a number of techniques. The include_state object keeps # track of the highest type seen, and complains if we see a # lower type after that. error_message = include_state.CheckNextIncludeOrder( _ClassifyInclude(fileinfo, include, is_system)) if error_message: error(filename, linenum, 'build/include_order', 4, '%s. Should be: %s.h, c system, c++ system, other.' % (error_message, fileinfo.BaseName())) canonical_include = include_state.CanonicalizeAlphabeticalOrder(include) if not include_state.IsInAlphabeticalOrder( clean_lines, linenum, canonical_include): error(filename, linenum, 'build/include_alpha', 4, 'Include "%s" not in alphabetical order' % include) include_state.SetLastHeader(canonical_include) def _GetTextInside(text, start_pattern): r"""Retrieves all the text between matching open and close parentheses. Given a string of lines and a regular expression string, retrieve all the text following the expression and between opening punctuation symbols like (, [, or {, and the matching close-punctuation symbol. This properly nested occurrences of the punctuations, so for the text like printf(a(), b(c())); a call to _GetTextInside(text, r'printf\(') will return 'a(), b(c())'. start_pattern must match string having an open punctuation symbol at the end. Args: text: The lines to extract text. Its comments and strings must be elided. It can be single line and can span multiple lines. start_pattern: The regexp string indicating where to start extracting the text. Returns: The extracted text. None if either the opening string or ending punctuation could not be found. """ # TODO(unknown): Audit cpplint.py to see what places could be profitably # rewritten to use _GetTextInside (and use inferior regexp matching today). # Give opening punctuations to get the matching close-punctuations. matching_punctuation = {'(': ')', '{': '}', '[': ']'} closing_punctuation = set(matching_punctuation.itervalues()) # Find the position to start extracting text. match = re.search(start_pattern, text, re.M) if not match: # start_pattern not found in text. return None start_position = match.end(0) assert start_position > 0, ( 'start_pattern must ends with an opening punctuation.') assert text[start_position - 1] in matching_punctuation, ( 'start_pattern must ends with an opening punctuation.') # Stack of closing punctuations we expect to have in text after position. punctuation_stack = [matching_punctuation[text[start_position - 1]]] position = start_position while punctuation_stack and position < len(text): if text[position] == punctuation_stack[-1]: punctuation_stack.pop() elif text[position] in closing_punctuation: # A closing punctuation without matching opening punctuations. return None elif text[position] in matching_punctuation: punctuation_stack.append(matching_punctuation[text[position]]) position += 1 if punctuation_stack: # Opening punctuations left without matching close-punctuations. return None # punctuations match. return text[start_position:position - 1] # Patterns for matching call-by-reference parameters. # # Supports nested templates up to 2 levels deep using this messy pattern: # < (?: < (?: < [^<>]* # > # | [^<>] )* # > # | [^<>] )* # > _RE_PATTERN_IDENT = r'[_a-zA-Z]\w*' # =~ [[:alpha:]][[:alnum:]]* _RE_PATTERN_TYPE = ( r'(?:const\s+)?(?:typename\s+|class\s+|struct\s+|union\s+|enum\s+)?' r'(?:\w|' r'\s*<(?:<(?:<[^<>]*>|[^<>])*>|[^<>])*>|' r'::)+') # A call-by-reference parameter ends with '& identifier'. _RE_PATTERN_REF_PARAM = re.compile( r'(' + _RE_PATTERN_TYPE + r'(?:\s*(?:\bconst\b|[*]))*\s*' r'&\s*' + _RE_PATTERN_IDENT + r')\s*(?:=[^,()]+)?[,)]') # A call-by-const-reference parameter either ends with 'const& identifier' # or looks like 'const type& identifier' when 'type' is atomic. _RE_PATTERN_CONST_REF_PARAM = ( r'(?:.*\s*\bconst\s*&\s*' + _RE_PATTERN_IDENT + r'|const\s+' + _RE_PATTERN_TYPE + r'\s*&\s*' + _RE_PATTERN_IDENT + r')') def CheckLanguage(filename, clean_lines, linenum, file_extension, include_state, nesting_state, error): """Checks rules from the 'C++ language rules' section of cppguide.html. Some of these rules are hard to test (function overloading, using uint32 inappropriately), but we do the best we can. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. file_extension: The extension (without the dot) of the filename. include_state: An _IncludeState instance in which the headers are inserted. nesting_state: A NestingState instance which maintains information about the current stack of nested blocks being parsed. error: The function to call with any errors found. """ # If the line is empty or consists of entirely a comment, no need to # check it. line = clean_lines.elided[linenum] if not line: return match = _RE_PATTERN_INCLUDE.search(line) if match: CheckIncludeLine(filename, clean_lines, linenum, include_state, error) return # Reset include state across preprocessor directives. This is meant # to silence warnings for conditional includes. match = Match(r'^\s*#\s*(if|ifdef|ifndef|elif|else|endif)\b', line) if match: include_state.ResetSection(match.group(1)) # Make Windows paths like Unix. fullname = os.path.abspath(filename).replace('\\', '/') # Perform other checks now that we are sure that this is not an include line CheckCasts(filename, clean_lines, linenum, error) CheckGlobalStatic(filename, clean_lines, linenum, error) CheckPrintf(filename, clean_lines, linenum, error) if file_extension == 'h': # TODO(unknown): check that 1-arg constructors are explicit. # How to tell it's a constructor? # (handled in CheckForNonStandardConstructs for now) # TODO(unknown): check that classes declare or disable copy/assign # (level 1 error) pass # Check if people are using the verboten C basic types. The only exception # we regularly allow is "unsigned short port" for port. if Search(r'\bshort port\b', line): if not Search(r'\bunsigned short port\b', line): error(filename, linenum, 'runtime/int', 4, 'Use "unsigned short" for ports, not "short"') else: match = Search(r'\b(short|long(?! +double)|long long)\b', line) if match: error(filename, linenum, 'runtime/int', 4, 'Use int16/int64/etc, rather than the C type %s' % match.group(1)) # Check if some verboten operator overloading is going on # TODO(unknown): catch out-of-line unary operator&: # class X {}; # int operator&(const X& x) { return 42; } // unary operator& # The trick is it's hard to tell apart from binary operator&: # class Y { int operator&(const Y& x) { return 23; } }; // binary operator& if Search(r'\boperator\s*&\s*\(\s*\)', line): error(filename, linenum, 'runtime/operator', 4, 'Unary operator& is dangerous. Do not use it.') # Check for suspicious usage of "if" like # } if (a == b) { if Search(r'\}\s*if\s*\(', line): error(filename, linenum, 'readability/braces', 4, 'Did you mean "else if"? If not, start a new line for "if".') # Check for potential format string bugs like printf(foo). # We constrain the pattern not to pick things like DocidForPrintf(foo). # Not perfect but it can catch printf(foo.c_str()) and printf(foo->c_str()) # TODO(unknown): Catch the following case. Need to change the calling # convention of the whole function to process multiple line to handle it. # printf( # boy_this_is_a_really_long_variable_that_cannot_fit_on_the_prev_line); printf_args = _GetTextInside(line, r'(?i)\b(string)?printf\s*\(') if printf_args: match = Match(r'([\w.\->()]+)$', printf_args) if match and match.group(1) != '__VA_ARGS__': function_name = re.search(r'\b((?:string)?printf)\s*\(', line, re.I).group(1) error(filename, linenum, 'runtime/printf', 4, 'Potential format string bug. Do %s("%%s", %s) instead.' % (function_name, match.group(1))) # Check for potential memset bugs like memset(buf, sizeof(buf), 0). match = Search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line) if match and not Match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", match.group(2)): error(filename, linenum, 'runtime/memset', 4, 'Did you mean "memset(%s, 0, %s)"?' % (match.group(1), match.group(2))) if Search(r'\busing namespace\b', line): error(filename, linenum, 'build/namespaces', 5, 'Do not use namespace using-directives. ' 'Use using-declarations instead.') # Detect variable-length arrays. match = Match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line) if (match and match.group(2) != 'return' and match.group(2) != 'delete' and match.group(3).find(']') == -1): # Split the size using space and arithmetic operators as delimiters. # If any of the resulting tokens are not compile time constants then # report the error. tokens = re.split(r'\s|\+|\-|\*|\/|<<|>>]', match.group(3)) is_const = True skip_next = False for tok in tokens: if skip_next: skip_next = False continue if Search(r'sizeof\(.+\)', tok): continue if Search(r'arraysize\(\w+\)', tok): continue tok = tok.lstrip('(') tok = tok.rstrip(')') if not tok: continue if Match(r'\d+', tok): continue if Match(r'0[xX][0-9a-fA-F]+', tok): continue if Match(r'k[A-Z0-9]\w*', tok): continue if Match(r'(.+::)?k[A-Z0-9]\w*', tok): continue if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue # A catch all for tricky sizeof cases, including 'sizeof expression', # 'sizeof(*type)', 'sizeof(const type)', 'sizeof(struct StructName)' # requires skipping the next token because we split on ' ' and '*'. if tok.startswith('sizeof'): skip_next = True continue is_const = False break if not is_const: error(filename, linenum, 'runtime/arrays', 1, 'Do not use variable-length arrays. Use an appropriately named ' "('k' followed by CamelCase) compile-time constant for the size.") # Check for use of unnamed namespaces in header files. Registration # macros are typically OK, so we allow use of "namespace {" on lines # that end with backslashes. if (file_extension == 'h' and Search(r'\bnamespace\s*{', line) and line[-1] != '\\'): error(filename, linenum, 'build/namespaces', 4, 'Do not use unnamed namespaces in header files. See ' 'http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces' ' for more information.') def CheckGlobalStatic(filename, clean_lines, linenum, error): """Check for unsafe global or static objects. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] # Match two lines at a time to support multiline declarations if linenum + 1 < clean_lines.NumLines() and not Search(r'[;({]', line): line += clean_lines.elided[linenum + 1].strip() # Check for people declaring static/global STL strings at the top level. # This is dangerous because the C++ language does not guarantee that # globals with constructors are initialized before the first access. match = Match( r'((?:|static +)(?:|const +))string +([a-zA-Z0-9_:]+)\b(.*)', line) # Remove false positives: # - String pointers (as opposed to values). # string *pointer # const string *pointer # string const *pointer # string *const pointer # # - Functions and template specializations. # string Function(... # string Class::Method(... # # - Operators. These are matched separately because operator names # cross non-word boundaries, and trying to match both operators # and functions at the same time would decrease accuracy of # matching identifiers. # string Class::operator*() if (match and not Search(r'\bstring\b(\s+const)?\s*\*\s*(const\s+)?\w', line) and not Search(r'\boperator\W', line) and not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)*\s*\(([^"]|$)', match.group(3))): error(filename, linenum, 'runtime/string', 4, 'For a static/global string constant, use a C style string instead: ' '"%schar %s[]".' % (match.group(1), match.group(2))) if Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line): error(filename, linenum, 'runtime/init', 4, 'You seem to be initializing a member variable with itself.') def CheckPrintf(filename, clean_lines, linenum, error): """Check for printf related issues. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] # When snprintf is used, the second argument shouldn't be a literal. match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line) if match and match.group(2) != '0': # If 2nd arg is zero, snprintf is used to calculate size. error(filename, linenum, 'runtime/printf', 3, 'If you can, use sizeof(%s) instead of %s as the 2nd arg ' 'to snprintf.' % (match.group(1), match.group(2))) # Check if some verboten C functions are being used. if Search(r'\bsprintf\s*\(', line): error(filename, linenum, 'runtime/printf', 5, 'Never use sprintf. Use snprintf instead.') match = Search(r'\b(strcpy|strcat)\s*\(', line) if match: error(filename, linenum, 'runtime/printf', 4, 'Almost always, snprintf is better than %s' % match.group(1)) def IsDerivedFunction(clean_lines, linenum): """Check if current line contains an inherited function. Args: clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. Returns: True if current line contains a function with "override" virt-specifier. """ # Scan back a few lines for start of current function for i in xrange(linenum, max(-1, linenum - 10), -1): match = Match(r'^([^()]*\w+)\(', clean_lines.elided[i]) if match: # Look for "override" after the matching closing parenthesis line, _, closing_paren = CloseExpression( clean_lines, i, len(match.group(1))) return (closing_paren >= 0 and Search(r'\boverride\b', line[closing_paren:])) return False def IsOutOfLineMethodDefinition(clean_lines, linenum): """Check if current line contains an out-of-line method definition. Args: clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. Returns: True if current line contains an out-of-line method definition. """ # Scan back a few lines for start of current function for i in xrange(linenum, max(-1, linenum - 10), -1): if Match(r'^([^()]*\w+)\(', clean_lines.elided[i]): return Match(r'^[^()]*\w+::\w+\(', clean_lines.elided[i]) is not None return False def IsInitializerList(clean_lines, linenum): """Check if current line is inside constructor initializer list. Args: clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. Returns: True if current line appears to be inside constructor initializer list, False otherwise. """ for i in xrange(linenum, 1, -1): line = clean_lines.elided[i] if i == linenum: remove_function_body = Match(r'^(.*)\{\s*$', line) if remove_function_body: line = remove_function_body.group(1) if Search(r'\s:\s*\w+[({]', line): # A lone colon tend to indicate the start of a constructor # initializer list. It could also be a ternary operator, which # also tend to appear in constructor initializer lists as # opposed to parameter lists. return True if Search(r'\}\s*,\s*$', line): # A closing brace followed by a comma is probably the end of a # brace-initialized member in constructor initializer list. return True if Search(r'[{};]\s*$', line): # Found one of the following: # - A closing brace or semicolon, probably the end of the previous # function. # - An opening brace, probably the start of current class or namespace. # # Current line is probably not inside an initializer list since # we saw one of those things without seeing the starting colon. return False # Got to the beginning of the file without seeing the start of # constructor initializer list. return False def CheckForNonConstReference(filename, clean_lines, linenum, nesting_state, error): """Check for non-const references. Separate from CheckLanguage since it scans backwards from current line, instead of scanning forward. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. nesting_state: A NestingState instance which maintains information about the current stack of nested blocks being parsed. error: The function to call with any errors found. """ # Do nothing if there is no '&' on current line. line = clean_lines.elided[linenum] if '&' not in line: return # If a function is inherited, current function doesn't have much of # a choice, so any non-const references should not be blamed on # derived function. if IsDerivedFunction(clean_lines, linenum): return # Don't warn on out-of-line method definitions, as we would warn on the # in-line declaration, if it isn't marked with 'override'. if IsOutOfLineMethodDefinition(clean_lines, linenum): return # Long type names may be broken across multiple lines, usually in one # of these forms: # LongType # ::LongTypeContinued &identifier # LongType:: # LongTypeContinued &identifier # LongType< # ...>::LongTypeContinued &identifier # # If we detected a type split across two lines, join the previous # line to current line so that we can match const references # accordingly. # # Note that this only scans back one line, since scanning back # arbitrary number of lines would be expensive. If you have a type # that spans more than 2 lines, please use a typedef. if linenum > 1: previous = None if Match(r'\s*::(?:[\w<>]|::)+\s*&\s*\S', line): # previous_line\n + ::current_line previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+[\w<>])\s*$', clean_lines.elided[linenum - 1]) elif Match(r'\s*[a-zA-Z_]([\w<>]|::)+\s*&\s*\S', line): # previous_line::\n + current_line previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+::)\s*$', clean_lines.elided[linenum - 1]) if previous: line = previous.group(1) + line.lstrip() else: # Check for templated parameter that is split across multiple lines endpos = line.rfind('>') if endpos > -1: (_, startline, startpos) = ReverseCloseExpression( clean_lines, linenum, endpos) if startpos > -1 and startline < linenum: # Found the matching < on an earlier line, collect all # pieces up to current line. line = '' for i in xrange(startline, linenum + 1): line += clean_lines.elided[i].strip() # Check for non-const references in function parameters. A single '&' may # found in the following places: # inside expression: binary & for bitwise AND # inside expression: unary & for taking the address of something # inside declarators: reference parameter # We will exclude the first two cases by checking that we are not inside a # function body, including one that was just introduced by a trailing '{'. # TODO(unknown): Doesn't account for 'catch(Exception& e)' [rare]. if (nesting_state.previous_stack_top and not (isinstance(nesting_state.previous_stack_top, _ClassInfo) or isinstance(nesting_state.previous_stack_top, _NamespaceInfo))): # Not at toplevel, not within a class, and not within a namespace return # Avoid initializer lists. We only need to scan back from the # current line for something that starts with ':'. # # We don't need to check the current line, since the '&' would # appear inside the second set of parentheses on the current line as # opposed to the first set. if linenum > 0: for i in xrange(linenum - 1, max(0, linenum - 10), -1): previous_line = clean_lines.elided[i] if not Search(r'[),]\s*$', previous_line): break if Match(r'^\s*:\s+\S', previous_line): return # Avoid preprocessors if Search(r'\\\s*$', line): return # Avoid constructor initializer lists if IsInitializerList(clean_lines, linenum): return # We allow non-const references in a few standard places, like functions # called "swap()" or iostream operators like "<<" or ">>". Do not check # those function parameters. # # We also accept & in static_assert, which looks like a function but # it's actually a declaration expression. whitelisted_functions = (r'(?:[sS]wap(?:<\w:+>)?|' r'operator\s*[<>][<>]|' r'static_assert|COMPILE_ASSERT' r')\s*\(') if Search(whitelisted_functions, line): return elif not Search(r'\S+\([^)]*$', line): # Don't see a whitelisted function on this line. Actually we # didn't see any function name on this line, so this is likely a # multi-line parameter list. Try a bit harder to catch this case. for i in xrange(2): if (linenum > i and Search(whitelisted_functions, clean_lines.elided[linenum - i - 1])): return decls = ReplaceAll(r'{[^}]*}', ' ', line) # exclude function body for parameter in re.findall(_RE_PATTERN_REF_PARAM, decls): if not Match(_RE_PATTERN_CONST_REF_PARAM, parameter): error(filename, linenum, 'runtime/references', 2, 'Is this a non-const reference? ' 'If so, make const or use a pointer: ' + ReplaceAll(' *<', '<', parameter)) def CheckCasts(filename, clean_lines, linenum, error): """Various cast related checks. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] # Check to see if they're using an conversion function cast. # I just try to capture the most common basic types, though there are more. # Parameterless conversion functions, such as bool(), are allowed as they are # probably a member operator declaration or default constructor. match = Search( r'(\bnew\s+|\S<\s*(?:const\s+)?)?\b' r'(int|float|double|bool|char|int32|uint32|int64|uint64)' r'(\([^)].*)', line) expecting_function = ExpectingFunctionArgs(clean_lines, linenum) if match and not expecting_function: matched_type = match.group(2) # matched_new_or_template is used to silence two false positives: # - New operators # - Template arguments with function types # # For template arguments, we match on types immediately following # an opening bracket without any spaces. This is a fast way to # silence the common case where the function type is the first # template argument. False negative with less-than comparison is # avoided because those operators are usually followed by a space. # # function // bracket + no space = false positive # value < double(42) // bracket + space = true positive matched_new_or_template = match.group(1) # Avoid arrays by looking for brackets that come after the closing # parenthesis. if Match(r'\([^()]+\)\s*\[', match.group(3)): return # Other things to ignore: # - Function pointers # - Casts to pointer types # - Placement new # - Alias declarations matched_funcptr = match.group(3) if (matched_new_or_template is None and not (matched_funcptr and (Match(r'\((?:[^() ]+::\s*\*\s*)?[^() ]+\)\s*\(', matched_funcptr) or matched_funcptr.startswith('(*)'))) and not Match(r'\s*using\s+\S+\s*=\s*' + matched_type, line) and not Search(r'new\(\S+\)\s*' + matched_type, line)): error(filename, linenum, 'readability/casting', 4, 'Using deprecated casting style. ' 'Use static_cast<%s>(...) instead' % matched_type) if not expecting_function: CheckCStyleCast(filename, clean_lines, linenum, 'static_cast', r'\((int|float|double|bool|char|u?int(16|32|64))\)', error) # This doesn't catch all cases. Consider (const char * const)"hello". # # (char *) "foo" should always be a const_cast (reinterpret_cast won't # compile). if CheckCStyleCast(filename, clean_lines, linenum, 'const_cast', r'\((char\s?\*+\s?)\)\s*"', error): pass else: # Check pointer casts for other than string constants CheckCStyleCast(filename, clean_lines, linenum, 'reinterpret_cast', r'\((\w+\s?\*+\s?)\)', error) # In addition, we look for people taking the address of a cast. This # is dangerous -- casts can assign to temporaries, so the pointer doesn't # point where you think. # # Some non-identifier character is required before the '&' for the # expression to be recognized as a cast. These are casts: # expression = &static_cast(temporary()); # function(&(int*)(temporary())); # # This is not a cast: # reference_type&(int* function_param); match = Search( r'(?:[^\w]&\(([^)*][^)]*)\)[\w(])|' r'(?:[^\w]&(static|dynamic|down|reinterpret)_cast\b)', line) if match: # Try a better error message when the & is bound to something # dereferenced by the casted pointer, as opposed to the casted # pointer itself. parenthesis_error = False match = Match(r'^(.*&(?:static|dynamic|down|reinterpret)_cast\b)<', line) if match: _, y1, x1 = CloseExpression(clean_lines, linenum, len(match.group(1))) if x1 >= 0 and clean_lines.elided[y1][x1] == '(': _, y2, x2 = CloseExpression(clean_lines, y1, x1) if x2 >= 0: extended_line = clean_lines.elided[y2][x2:] if y2 < clean_lines.NumLines() - 1: extended_line += clean_lines.elided[y2 + 1] if Match(r'\s*(?:->|\[)', extended_line): parenthesis_error = True if parenthesis_error: error(filename, linenum, 'readability/casting', 4, ('Are you taking an address of something dereferenced ' 'from a cast? Wrapping the dereferenced expression in ' 'parentheses will make the binding more obvious')) else: error(filename, linenum, 'runtime/casting', 4, ('Are you taking an address of a cast? ' 'This is dangerous: could be a temp var. ' 'Take the address before doing the cast, rather than after')) def CheckCStyleCast(filename, clean_lines, linenum, cast_type, pattern, error): """Checks for a C-style cast by looking for the pattern. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. cast_type: The string for the C++ cast to recommend. This is either reinterpret_cast, static_cast, or const_cast, depending. pattern: The regular expression used to find C-style casts. error: The function to call with any errors found. Returns: True if an error was emitted. False otherwise. """ line = clean_lines.elided[linenum] match = Search(pattern, line) if not match: return False # Exclude lines with keywords that tend to look like casts context = line[0:match.start(1) - 1] if Match(r'.*\b(?:sizeof|alignof|alignas|[_A-Z][_A-Z0-9]*)\s*$', context): return False # Try expanding current context to see if we one level of # parentheses inside a macro. if linenum > 0: for i in xrange(linenum - 1, max(0, linenum - 5), -1): context = clean_lines.elided[i] + context if Match(r'.*\b[_A-Z][_A-Z0-9]*\s*\((?:\([^()]*\)|[^()])*$', context): return False # operator++(int) and operator--(int) if context.endswith(' operator++') or context.endswith(' operator--'): return False # A single unnamed argument for a function tends to look like old # style cast. If we see those, don't issue warnings for deprecated # casts, instead issue warnings for unnamed arguments where # appropriate. # # These are things that we want warnings for, since the style guide # explicitly require all parameters to be named: # Function(int); # Function(int) { # ConstMember(int) const; # ConstMember(int) const { # ExceptionMember(int) throw (...); # ExceptionMember(int) throw (...) { # PureVirtual(int) = 0; # [](int) -> bool { # # These are functions of some sort, where the compiler would be fine # if they had named parameters, but people often omit those # identifiers to reduce clutter: # (FunctionPointer)(int); # (FunctionPointer)(int) = value; # Function((function_pointer_arg)(int)) # Function((function_pointer_arg)(int), int param) # ; # <(FunctionPointerTemplateArgument)(int)>; remainder = line[match.end(0):] if Match(r'^\s*(?:;|const\b|throw\b|final\b|override\b|[=>{),]|->)', remainder): # Looks like an unnamed parameter. # Don't warn on any kind of template arguments. if Match(r'^\s*>', remainder): return False # Don't warn on assignments to function pointers, but keep warnings for # unnamed parameters to pure virtual functions. Note that this pattern # will also pass on assignments of "0" to function pointers, but the # preferred values for those would be "nullptr" or "NULL". matched_zero = Match(r'^\s=\s*(\S+)\s*;', remainder) if matched_zero and matched_zero.group(1) != '0': return False # Don't warn on function pointer declarations. For this we need # to check what came before the "(type)" string. if Match(r'.*\)\s*$', line[0:match.start(0)]): return False # Don't warn if the parameter is named with block comments, e.g.: # Function(int /*unused_param*/); raw_line = clean_lines.raw_lines[linenum] if '/*' in raw_line: return False # Passed all filters, issue warning here. error(filename, linenum, 'readability/function', 3, 'All parameters should be named in a function') return True # At this point, all that should be left is actual casts. error(filename, linenum, 'readability/casting', 4, 'Using C-style cast. Use %s<%s>(...) instead' % (cast_type, match.group(1))) return True def ExpectingFunctionArgs(clean_lines, linenum): """Checks whether where function type arguments are expected. Args: clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. Returns: True if the line at 'linenum' is inside something that expects arguments of function types. """ line = clean_lines.elided[linenum] return (Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line) or (linenum >= 2 and (Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\((?:\S+,)?\s*$', clean_lines.elided[linenum - 1]) or Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\(\s*$', clean_lines.elided[linenum - 2]) or Search(r'\bstd::m?function\s*\<\s*$', clean_lines.elided[linenum - 1])))) _HEADERS_CONTAINING_TEMPLATES = ( ('', ('deque',)), ('', ('unary_function', 'binary_function', 'plus', 'minus', 'multiplies', 'divides', 'modulus', 'negate', 'equal_to', 'not_equal_to', 'greater', 'less', 'greater_equal', 'less_equal', 'logical_and', 'logical_or', 'logical_not', 'unary_negate', 'not1', 'binary_negate', 'not2', 'bind1st', 'bind2nd', 'pointer_to_unary_function', 'pointer_to_binary_function', 'ptr_fun', 'mem_fun_t', 'mem_fun', 'mem_fun1_t', 'mem_fun1_ref_t', 'mem_fun_ref_t', 'const_mem_fun_t', 'const_mem_fun1_t', 'const_mem_fun_ref_t', 'const_mem_fun1_ref_t', 'mem_fun_ref', )), ('', ('numeric_limits',)), ('', ('list',)), ('', ('map', 'multimap',)), ('', ('allocator',)), ('', ('queue', 'priority_queue',)), ('', ('set', 'multiset',)), ('', ('stack',)), ('', ('char_traits', 'basic_string',)), ('', ('tuple',)), ('', ('pair',)), ('', ('vector',)), # gcc extensions. # Note: std::hash is their hash, ::hash is our hash ('', ('hash_map', 'hash_multimap',)), ('', ('hash_set', 'hash_multiset',)), ('', ('slist',)), ) _RE_PATTERN_STRING = re.compile(r'\bstring\b') _re_pattern_algorithm_header = [] for _template in ('copy', 'max', 'min', 'min_element', 'sort', 'swap', 'transform'): # Match max(..., ...), max(..., ...), but not foo->max, foo.max or # type::max(). _re_pattern_algorithm_header.append( (re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'), _template, '')) _re_pattern_templates = [] for _header, _templates in _HEADERS_CONTAINING_TEMPLATES: for _template in _templates: _re_pattern_templates.append( (re.compile(r'(\<|\b)' + _template + r'\s*\<'), _template + '<>', _header)) def FilesBelongToSameModule(filename_cc, filename_h): """Check if these two filenames belong to the same module. The concept of a 'module' here is a as follows: foo.h, foo-inl.h, foo.cc, foo_test.cc and foo_unittest.cc belong to the same 'module' if they are in the same directory. some/path/public/xyzzy and some/path/internal/xyzzy are also considered to belong to the same module here. If the filename_cc contains a longer path than the filename_h, for example, '/absolute/path/to/base/sysinfo.cc', and this file would include 'base/sysinfo.h', this function also produces the prefix needed to open the header. This is used by the caller of this function to more robustly open the header file. We don't have access to the real include paths in this context, so we need this guesswork here. Known bugs: tools/base/bar.cc and base/bar.h belong to the same module according to this implementation. Because of this, this function gives some false positives. This should be sufficiently rare in practice. Args: filename_cc: is the path for the .cc file filename_h: is the path for the header path Returns: Tuple with a bool and a string: bool: True if filename_cc and filename_h belong to the same module. string: the additional prefix needed to open the header file. """ if not filename_cc.endswith('.cc'): return (False, '') filename_cc = filename_cc[:-len('.cc')] if filename_cc.endswith('_unittest'): filename_cc = filename_cc[:-len('_unittest')] elif filename_cc.endswith('_test'): filename_cc = filename_cc[:-len('_test')] filename_cc = filename_cc.replace('/public/', '/') filename_cc = filename_cc.replace('/internal/', '/') if not filename_h.endswith('.h'): return (False, '') filename_h = filename_h[:-len('.h')] if filename_h.endswith('-inl'): filename_h = filename_h[:-len('-inl')] filename_h = filename_h.replace('/public/', '/') filename_h = filename_h.replace('/internal/', '/') files_belong_to_same_module = filename_cc.endswith(filename_h) common_path = '' if files_belong_to_same_module: common_path = filename_cc[:-len(filename_h)] return files_belong_to_same_module, common_path def UpdateIncludeState(filename, include_dict, io=codecs): """Fill up the include_dict with new includes found from the file. Args: filename: the name of the header to read. include_dict: a dictionary in which the headers are inserted. io: The io factory to use to read the file. Provided for testability. Returns: True if a header was successfully added. False otherwise. """ headerfile = None try: headerfile = io.open(filename, 'r', 'utf8', 'replace') except IOError: return False linenum = 0 for line in headerfile: linenum += 1 clean_line = CleanseComments(line) match = _RE_PATTERN_INCLUDE.search(clean_line) if match: include = match.group(2) include_dict.setdefault(include, linenum) return True def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, io=codecs): """Reports for missing stl includes. This function will output warnings to make sure you are including the headers necessary for the stl containers and functions that you use. We only give one reason to include a header. For example, if you use both equal_to<> and less<> in a .h file, only one (the latter in the file) of these will be reported as a reason to include the . Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. include_state: An _IncludeState instance. error: The function to call with any errors found. io: The IO factory to use to read the header file. Provided for unittest injection. """ required = {} # A map of header name to linenumber and the template entity. # Example of required: { '': (1219, 'less<>') } for linenum in xrange(clean_lines.NumLines()): line = clean_lines.elided[linenum] if not line or line[0] == '#': continue # String is special -- it is a non-templatized type in STL. matched = _RE_PATTERN_STRING.search(line) if matched: # Don't warn about strings in non-STL namespaces: # (We check only the first match per line; good enough.) prefix = line[:matched.start()] if prefix.endswith('std::') or not prefix.endswith('::'): required[''] = (linenum, 'string') for pattern, template, header in _re_pattern_algorithm_header: if pattern.search(line): required[header] = (linenum, template) # The following function is just a speed up, no semantics are changed. if not '<' in line: # Reduces the cpu time usage by skipping lines. continue for pattern, template, header in _re_pattern_templates: if pattern.search(line): required[header] = (linenum, template) # The policy is that if you #include something in foo.h you don't need to # include it again in foo.cc. Here, we will look at possible includes. # Let's flatten the include_state include_list and copy it into a dictionary. include_dict = dict([item for sublist in include_state.include_list for item in sublist]) # Did we find the header for this file (if any) and successfully load it? header_found = False # Use the absolute path so that matching works properly. abs_filename = FileInfo(filename).FullName() # For Emacs's flymake. # If cpplint is invoked from Emacs's flymake, a temporary file is generated # by flymake and that file name might end with '_flymake.cc'. In that case, # restore original file name here so that the corresponding header file can be # found. # e.g. If the file name is 'foo_flymake.cc', we should search for 'foo.h' # instead of 'foo_flymake.h' abs_filename = re.sub(r'_flymake\.cc$', '.cc', abs_filename) # include_dict is modified during iteration, so we iterate over a copy of # the keys. header_keys = include_dict.keys() for header in header_keys: (same_module, common_path) = FilesBelongToSameModule(abs_filename, header) fullpath = common_path + header if same_module and UpdateIncludeState(fullpath, include_dict, io): header_found = True # If we can't find the header file for a .cc, assume it's because we don't # know where to look. In that case we'll give up as we're not sure they # didn't include it in the .h file. # TODO(unknown): Do a better job of finding .h files so we are confident that # not having the .h file means there isn't one. if filename.endswith('.cc') and not header_found: return # All the lines have been processed, report the errors found. for required_header_unstripped in required: template = required[required_header_unstripped][1] if required_header_unstripped.strip('<>"') not in include_dict: error(filename, required[required_header_unstripped][0], 'build/include_what_you_use', 4, 'Add #include ' + required_header_unstripped + ' for ' + template) _RE_PATTERN_EXPLICIT_MAKEPAIR = re.compile(r'\bmake_pair\s*<') def CheckMakePairUsesDeduction(filename, clean_lines, linenum, error): """Check that make_pair's template arguments are deduced. G++ 4.6 in C++11 mode fails badly if make_pair's template arguments are specified explicitly, and such use isn't intended in any case. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] match = _RE_PATTERN_EXPLICIT_MAKEPAIR.search(line) if match: error(filename, linenum, 'build/explicit_make_pair', 4, # 4 = high confidence 'For C++11-compatibility, omit template arguments from make_pair' ' OR use pair directly OR if appropriate, construct a pair directly') def CheckDefaultLambdaCaptures(filename, clean_lines, linenum, error): """Check that default lambda captures are not used. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] # A lambda introducer specifies a default capture if it starts with "[=" # or if it starts with "[&" _not_ followed by an identifier. match = Match(r'^(.*)\[\s*(?:=|&[^\w])', line) if match: # Found a potential error, check what comes after the lambda-introducer. # If it's not open parenthesis (for lambda-declarator) or open brace # (for compound-statement), it's not a lambda. line, _, pos = CloseExpression(clean_lines, linenum, len(match.group(1))) if pos >= 0 and Match(r'^\s*[{(]', line[pos:]): error(filename, linenum, 'build/c++11', 4, # 4 = high confidence 'Default lambda captures are an unapproved C++ feature.') def CheckRedundantVirtual(filename, clean_lines, linenum, error): """Check if line contains a redundant "virtual" function-specifier. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ # Look for "virtual" on current line. line = clean_lines.elided[linenum] virtual = Match(r'^(.*)(\bvirtual\b)(.*)$', line) if not virtual: return # Ignore "virtual" keywords that are near access-specifiers. These # are only used in class base-specifier and do not apply to member # functions. if (Search(r'\b(public|protected|private)\s+$', virtual.group(1)) or Match(r'^\s+(public|protected|private)\b', virtual.group(3))): return # Ignore the "virtual" keyword from virtual base classes. Usually # there is a column on the same line in these cases (virtual base # classes are rare in google3 because multiple inheritance is rare). if Match(r'^.*[^:]:[^:].*$', line): return # Look for the next opening parenthesis. This is the start of the # parameter list (possibly on the next line shortly after virtual). # TODO(unknown): doesn't work if there are virtual functions with # decltype() or other things that use parentheses, but csearch suggests # that this is rare. end_col = -1 end_line = -1 start_col = len(virtual.group(2)) for start_line in xrange(linenum, min(linenum + 3, clean_lines.NumLines())): line = clean_lines.elided[start_line][start_col:] parameter_list = Match(r'^([^(]*)\(', line) if parameter_list: # Match parentheses to find the end of the parameter list (_, end_line, end_col) = CloseExpression( clean_lines, start_line, start_col + len(parameter_list.group(1))) break start_col = 0 if end_col < 0: return # Couldn't find end of parameter list, give up # Look for "override" or "final" after the parameter list # (possibly on the next few lines). for i in xrange(end_line, min(end_line + 3, clean_lines.NumLines())): line = clean_lines.elided[i][end_col:] match = Search(r'\b(override|final)\b', line) if match: error(filename, linenum, 'readability/inheritance', 4, ('"virtual" is redundant since function is ' 'already declared as "%s"' % match.group(1))) # Set end_col to check whole lines after we are done with the # first line. end_col = 0 if Search(r'[^\w]\s*$', line): break def CheckRedundantOverrideOrFinal(filename, clean_lines, linenum, error): """Check if line contains a redundant "override" or "final" virt-specifier. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ # Look for closing parenthesis nearby. We need one to confirm where # the declarator ends and where the virt-specifier starts to avoid # false positives. line = clean_lines.elided[linenum] declarator_end = line.rfind(')') if declarator_end >= 0: fragment = line[declarator_end:] else: if linenum > 1 and clean_lines.elided[linenum - 1].rfind(')') >= 0: fragment = line else: return # Check that at most one of "override" or "final" is present, not both if Search(r'\boverride\b', fragment) and Search(r'\bfinal\b', fragment): error(filename, linenum, 'readability/inheritance', 4, ('"override" is redundant since function is ' 'already declared as "final"')) # Returns true if we are at a new block, and it is directly # inside of a namespace. def IsBlockInNameSpace(nesting_state, is_forward_declaration): """Checks that the new block is directly in a namespace. Args: nesting_state: The _NestingState object that contains info about our state. is_forward_declaration: If the class is a forward declared class. Returns: Whether or not the new block is directly in a namespace. """ if is_forward_declaration: if len(nesting_state.stack) >= 1 and ( isinstance(nesting_state.stack[-1], _NamespaceInfo)): return True else: return False return (len(nesting_state.stack) > 1 and nesting_state.stack[-1].check_namespace_indentation and isinstance(nesting_state.stack[-2], _NamespaceInfo)) def ShouldCheckNamespaceIndentation(nesting_state, is_namespace_indent_item, raw_lines_no_comments, linenum): """This method determines if we should apply our namespace indentation check. Args: nesting_state: The current nesting state. is_namespace_indent_item: If we just put a new class on the stack, True. If the top of the stack is not a class, or we did not recently add the class, False. raw_lines_no_comments: The lines without the comments. linenum: The current line number we are processing. Returns: True if we should apply our namespace indentation check. Currently, it only works for classes and namespaces inside of a namespace. """ is_forward_declaration = IsForwardClassDeclaration(raw_lines_no_comments, linenum) if not (is_namespace_indent_item or is_forward_declaration): return False # If we are in a macro, we do not want to check the namespace indentation. if IsMacroDefinition(raw_lines_no_comments, linenum): return False return IsBlockInNameSpace(nesting_state, is_forward_declaration) # Call this method if the line is directly inside of a namespace. # If the line above is blank (excluding comments) or the start of # an inner namespace, it cannot be indented. def CheckItemIndentationInNamespace(filename, raw_lines_no_comments, linenum, error): line = raw_lines_no_comments[linenum] if Match(r'^\s+', line): error(filename, linenum, 'runtime/indentation_namespace', 4, 'Do not indent within a namespace') def ProcessLine(filename, file_extension, clean_lines, line, include_state, function_state, nesting_state, error, extra_check_functions=[]): """Processes a single line in the file. Args: filename: Filename of the file that is being processed. file_extension: The extension (dot not included) of the file. clean_lines: An array of strings, each representing a line of the file, with comments stripped. line: Number of line being processed. include_state: An _IncludeState instance in which the headers are inserted. function_state: A _FunctionState instance which counts function lines, etc. nesting_state: A NestingState instance which maintains information about the current stack of nested blocks being parsed. error: A callable to which errors are reported, which takes 4 arguments: filename, line number, error level, and message extra_check_functions: An array of additional check functions that will be run on each source line. Each function takes 4 arguments: filename, clean_lines, line, error """ raw_lines = clean_lines.raw_lines ParseNolintSuppressions(filename, raw_lines[line], line, error) nesting_state.Update(filename, clean_lines, line, error) CheckForNamespaceIndentation(filename, nesting_state, clean_lines, line, error) if nesting_state.InAsmBlock(): return CheckForFunctionLengths(filename, clean_lines, line, function_state, error) CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error) CheckStyle(filename, clean_lines, line, file_extension, nesting_state, error) CheckLanguage(filename, clean_lines, line, file_extension, include_state, nesting_state, error) CheckForNonConstReference(filename, clean_lines, line, nesting_state, error) CheckForNonStandardConstructs(filename, clean_lines, line, nesting_state, error) CheckVlogArguments(filename, clean_lines, line, error) CheckPosixThreading(filename, clean_lines, line, error) CheckInvalidIncrement(filename, clean_lines, line, error) CheckMakePairUsesDeduction(filename, clean_lines, line, error) CheckDefaultLambdaCaptures(filename, clean_lines, line, error) CheckRedundantVirtual(filename, clean_lines, line, error) CheckRedundantOverrideOrFinal(filename, clean_lines, line, error) for check_fn in extra_check_functions: check_fn(filename, clean_lines, line, error) def FlagCxx11Features(filename, clean_lines, linenum, error): """Flag those c++11 features that we only allow in certain places. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] # Flag unapproved C++11 headers. include = Match(r'\s*#\s*include\s+[<"]([^<"]+)[">]', line) if include and include.group(1) in ('cfenv', 'condition_variable', 'fenv.h', 'future', 'mutex', 'thread', 'chrono', 'ratio', 'regex', 'system_error', ): error(filename, linenum, 'build/c++11', 5, ('<%s> is an unapproved C++11 header.') % include.group(1)) # The only place where we need to worry about C++11 keywords and library # features in preprocessor directives is in macro definitions. if Match(r'\s*#', line) and not Match(r'\s*#\s*define\b', line): return # These are classes and free functions. The classes are always # mentioned as std::*, but we only catch the free functions if # they're not found by ADL. They're alphabetical by header. for top_name in ( # type_traits 'alignment_of', 'aligned_union', ): if Search(r'\bstd::%s\b' % top_name, line): error(filename, linenum, 'build/c++11', 5, ('std::%s is an unapproved C++11 class or function. Send c-style ' 'an example of where it would make your code more readable, and ' 'they may let you use it.') % top_name) def ProcessFileData(filename, file_extension, lines, error, extra_check_functions=[]): """Performs lint checks and reports any errors to the given error function. Args: filename: Filename of the file that is being processed. file_extension: The extension (dot not included) of the file. lines: An array of strings, each representing a line of the file, with the last element being empty if the file is terminated with a newline. error: A callable to which errors are reported, which takes 4 arguments: filename, line number, error level, and message extra_check_functions: An array of additional check functions that will be run on each source line. Each function takes 4 arguments: filename, clean_lines, line, error """ lines = (['// marker so line numbers and indices both start at 1'] + lines + ['// marker so line numbers end in a known way']) include_state = _IncludeState() function_state = _FunctionState() nesting_state = NestingState() ResetNolintSuppressions() CheckForCopyright(filename, lines, error) RemoveMultiLineComments(filename, lines, error) clean_lines = CleansedLines(lines) if file_extension == 'h': CheckForHeaderGuard(filename, clean_lines, error) for line in xrange(clean_lines.NumLines()): ProcessLine(filename, file_extension, clean_lines, line, include_state, function_state, nesting_state, error, extra_check_functions) FlagCxx11Features(filename, clean_lines, line, error) nesting_state.CheckCompletedBlocks(filename, error) CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error) # Check that the .cc file has included its header if it exists. if file_extension == 'cc': CheckHeaderFileIncluded(filename, include_state, error) # We check here rather than inside ProcessLine so that we see raw # lines rather than "cleaned" lines. CheckForBadCharacters(filename, lines, error) CheckForNewlineAtEOF(filename, lines, error) def ProcessConfigOverrides(filename): """ Loads the configuration files and processes the config overrides. Args: filename: The name of the file being processed by the linter. Returns: False if the current |filename| should not be processed further. """ abs_filename = os.path.abspath(filename) cfg_filters = [] keep_looking = True while keep_looking: abs_path, base_name = os.path.split(abs_filename) if not base_name: break # Reached the root directory. cfg_file = os.path.join(abs_path, "CPPLINT.cfg") abs_filename = abs_path if not os.path.isfile(cfg_file): continue try: with open(cfg_file) as file_handle: for line in file_handle: line, _, _ = line.partition('#') # Remove comments. if not line.strip(): continue name, _, val = line.partition('=') name = name.strip() val = val.strip() if name == 'set noparent': keep_looking = False elif name == 'filter': cfg_filters.append(val) elif name == 'exclude_files': # When matching exclude_files pattern, use the base_name of # the current file name or the directory name we are processing. # For example, if we are checking for lint errors in /foo/bar/baz.cc # and we found the .cfg file at /foo/CPPLINT.cfg, then the config # file's "exclude_files" filter is meant to be checked against "bar" # and not "baz" nor "bar/baz.cc". if base_name: pattern = re.compile(val) if pattern.match(base_name): sys.stderr.write('Ignoring "%s": file excluded by "%s". ' 'File path component "%s" matches ' 'pattern "%s"\n' % (filename, cfg_file, base_name, val)) return False elif name == 'linelength': global _line_length try: _line_length = int(val) except ValueError: sys.stderr.write('Line length must be numeric.') else: sys.stderr.write( 'Invalid configuration option (%s) in file %s\n' % (name, cfg_file)) except IOError: sys.stderr.write( "Skipping config file '%s': Can't open for reading\n" % cfg_file) keep_looking = False # Apply all the accumulated filters in reverse order (top-level directory # config options having the least priority). for filter in reversed(cfg_filters): _AddFilters(filter) return True def ProcessFile(filename, vlevel, extra_check_functions=[]): """Does google-lint on a single file. Args: filename: The name of the file to parse. vlevel: The level of errors to report. Every error of confidence >= verbose_level will be reported. 0 is a good default. extra_check_functions: An array of additional check functions that will be run on each source line. Each function takes 4 arguments: filename, clean_lines, line, error """ _SetVerboseLevel(vlevel) _BackupFilters() if not ProcessConfigOverrides(filename): _RestoreFilters() return lf_lines = [] crlf_lines = [] try: # Support the UNIX convention of using "-" for stdin. Note that # we are not opening the file with universal newline support # (which codecs doesn't support anyway), so the resulting lines do # contain trailing '\r' characters if we are reading a file that # has CRLF endings. # If after the split a trailing '\r' is present, it is removed # below. if filename == '-': lines = codecs.StreamReaderWriter(sys.stdin, codecs.getreader('utf8'), codecs.getwriter('utf8'), 'replace').read().split('\n') else: lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n') # Remove trailing '\r'. # The -1 accounts for the extra trailing blank line we get from split() for linenum in range(len(lines) - 1): if lines[linenum].endswith('\r'): lines[linenum] = lines[linenum].rstrip('\r') crlf_lines.append(linenum + 1) else: lf_lines.append(linenum + 1) except IOError: sys.stderr.write( "Skipping input '%s': Can't open for reading\n" % filename) _RestoreFilters() return # Note, if no dot is found, this will give the entire filename as the ext. file_extension = filename[filename.rfind('.') + 1:] # When reading from stdin, the extension is unknown, so no cpplint tests # should rely on the extension. if filename != '-' and file_extension not in _valid_extensions: sys.stderr.write('Ignoring %s; not a valid file name ' '(%s)\n' % (filename, ', '.join(_valid_extensions))) else: ProcessFileData(filename, file_extension, lines, Error, extra_check_functions) # If end-of-line sequences are a mix of LF and CR-LF, issue # warnings on the lines with CR. # # Don't issue any warnings if all lines are uniformly LF or CR-LF, # since critique can handle these just fine, and the style guide # doesn't dictate a particular end of line sequence. # # We can't depend on os.linesep to determine what the desired # end-of-line sequence should be, since that will return the # server-side end-of-line sequence. if lf_lines and crlf_lines: # Warn on every line with CR. An alternative approach might be to # check whether the file is mostly CRLF or just LF, and warn on the # minority, we bias toward LF here since most tools prefer LF. for linenum in crlf_lines: Error(filename, linenum, 'whitespace/newline', 1, 'Unexpected \\r (^M) found; better to use only \\n') sys.stderr.write('Done processing %s\n' % filename) _RestoreFilters() def PrintUsage(message): """Prints a brief usage string and exits, optionally with an error message. Args: message: The optional error message. """ sys.stderr.write(_USAGE) if message: sys.exit('\nFATAL ERROR: ' + message) else: sys.exit(1) def PrintCategories(): """Prints a list of all the error-categories used by error messages. These are the categories used to filter messages via --filter. """ sys.stderr.write(''.join(' %s\n' % cat for cat in _ERROR_CATEGORIES)) sys.exit(0) def ParseArguments(args): """Parses the command line arguments. This may set the output format and verbosity level as side-effects. Args: args: The command line arguments: Returns: The list of filenames to lint. """ try: (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=', 'counting=', 'filter=', 'root=', 'linelength=', 'extensions=']) except getopt.GetoptError: PrintUsage('Invalid arguments.') verbosity = _VerboseLevel() output_format = _OutputFormat() filters = '' counting_style = '' for (opt, val) in opts: if opt == '--help': PrintUsage(None) elif opt == '--output': if val not in ('emacs', 'vs7', 'eclipse'): PrintUsage('The only allowed output formats are emacs, vs7 and eclipse.') output_format = val elif opt == '--verbose': verbosity = int(val) elif opt == '--filter': filters = val if not filters: PrintCategories() elif opt == '--counting': if val not in ('total', 'toplevel', 'detailed'): PrintUsage('Valid counting options are total, toplevel, and detailed') counting_style = val elif opt == '--root': global _root _root = val elif opt == '--linelength': global _line_length try: _line_length = int(val) except ValueError: PrintUsage('Line length must be digits.') elif opt == '--extensions': global _valid_extensions try: _valid_extensions = set(val.split(',')) except ValueError: PrintUsage('Extensions must be comma seperated list.') if not filenames: PrintUsage('No files were specified.') _SetOutputFormat(output_format) _SetVerboseLevel(verbosity) _SetFilters(filters) _SetCountingStyle(counting_style) return filenames def main(): filenames = ParseArguments(sys.argv[1:]) # Change stderr to write with replacement characters so we don't die # if we try to print something containing non-ASCII characters. sys.stderr = codecs.StreamReaderWriter(sys.stderr, codecs.getreader('utf8'), codecs.getwriter('utf8'), 'replace') _cpplint_state.ResetErrorCounts() for filename in filenames: ProcessFile(filename, _cpplint_state.verbose_level) _cpplint_state.PrintErrorCounts() sys.exit(_cpplint_state.error_count > 0) if __name__ == '__main__': main() osm2pgrouting-2.3.8/tools/data/000077500000000000000000000000001405641420700164175ustar00rootroot00000000000000osm2pgrouting-2.3.8/tools/data/getdata.sh000066400000000000000000000040731405641420700203700ustar00rootroot00000000000000#!sh exit(0); # https://github.com/GeographicaGS/osm-itinera/blob/master/itinera/const.py#L43 BBOX="1.97180,41.26684,2.26478,41.55818" wget --progress=dot:mega -O "bcn.osm" "http://www.overpass-api.de/api/xapi?*[bbox=${BBOX}][@meta]" BBOX="13.15,47.24,13.52,47.45" wget --progress=dot:mega -O "restrictions.osm" "http://www.overpass-api.de/api/xapi?*[bbox=${BBOX}][@meta]" #query to get germany restrictions #(53.5,11.0,54.5,11.5) wget --progress=dot:mega -O "germany_restrictions.osm" "http://overpass-api.de/api/interpreter?data=(relation(53.5,11.0,54.5,11.5)[\"type\"~\"^restriction\"];);(._;>;);out;" #query to get world restrictions #times out does not work well wget --progress=dot:mega -O "world_restrictions.osm" "http://overpass-api.de/api/interpreter?data=(relation[\"type\"~\"^restriction\"];);(._;>;);out;" BBOX="-122.8,45.4,-122.5,45.6" wget --progress=dot:mega -O "sampledata.osm" "http://www.overpass-api.de/api/xapi?*[bbox=${BBOX}][@meta]" BBOX="-122.65,45.5, -122.5,45.6" wget --progress=dot:mega -O "sampledataNE.osm" "http://www.overpass-api.de/api/xapi?*[bbox=${BBOX}][@meta]" BBOX="-122.65,45.4, -122.5,45.5" wget --progress=dot:mega -O "sampledataSE.osm" "http://www.overpass-api.de/api/xapi?*[bbox=${BBOX}][@meta]" BBOX="-122.8,45.5, -122.65,45.6" wget --progress=dot:mega -O "sampledataNW.osm" "http://www.overpass-api.de/api/xapi?*[bbox=${BBOX}][@meta]" BBOX="-122.8,45.4, -122.65,45.5" wget --progress=dot:mega -O "sampledataSW.osm" "http://www.overpass-api.de/api/xapi?*[bbox=${BBOX}][@meta]" BBOX="-122.65,45.4, -122.6,45.45" wget --progress=dot:mega -O "small.osm" "http://www.overpass-api.de/api/xapi?*[bbox=${BBOX}][@meta]" BBOX="4.2878,50.8139,4.5023,50.8926" wget --progress=dot:mega -O "brussels.osm" "http://www.overpass-api.de/api/xapi?*[bbox=${BBOX}][@meta]" osm2pgrouting-2.3.8/tools/doxygen/000077500000000000000000000000001405641420700171635ustar00rootroot00000000000000osm2pgrouting-2.3.8/tools/doxygen/Doxyfile000066400000000000000000003054341405641420700207020ustar00rootroot00000000000000# Doxyfile 1.8.7 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all text # before the first occurrence of this tag. Doxygen uses libiconv (or the iconv # built into libc) for the transcoding. See http://www.gnu.org/software/libiconv # for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = "osm2pgrouting" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = 2.1 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = "osm2pgrouting provides the tools to import to a SQL geospatial database the routable data from osm." # With the PROJECT_LOGO tag one can specify an logo or icon that is included in # the documentation. The maximum height of the logo should not exceed 55 pixels # and the maximum width should not exceed 200 pixels. Doxygen will copy the logo # to the output directory. PROJECT_LOGO = "./images/pgrouting-logo.png" # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = ../../build/doxy/ # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. # The default value is: NO. CREATE_SUBDIRS = NO # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, # Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), # Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, # Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, # Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, # Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, # Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = YES # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a # new page for each member. If set to NO, the documentation of a member will be # part of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:\n" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" # will allow you to use the command class in the itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, Javascript, # C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: # FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: # Fortran. In the later case the parser tries to guess whether the code is fixed # or free formatted code, this is the default for Fortran type files), VHDL. For # instance to make doxygen treat .inc files as Fortran files (default is PHP), # and .f files as C (default is Fortran), use: inc=Fortran f=C. # # Note For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by by putting a % sign in front of the word # or globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = YES # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined # locally in source files will be included in the documentation. If set to NO # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO these classes will be included in the various overviews. This option has # no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # (class|struct|union) declarations. If set to NO these declarations will be # included in the documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file # names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = YES # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = YES # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = YES # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the # todo list. This list is created by putting \todo commands in the # documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the # test list. This list is created by putting \test commands in the # documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES the list # will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. Do not use file names with spaces, bibtex cannot handle them. See # also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters # in a documented function, or documenting parameters that don't exist or using # markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO doxygen will only warn about wrong or incomplete parameter # documentation, but not about the absence of documentation. # The default value is: NO. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). WARN_LOGFILE = "LOG.txt" #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. # Note: If this tag is empty the current directory is searched. INPUT = ../.. # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: http://www.gnu.org/software/libiconv) for the list of # possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank the # following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, # *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, # *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, # *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, # *.qsf, *.as and *.js. FILE_PATTERNS = # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = ../../tools ../../doc ../../build ../../cmake ../../.tx # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = */notUsed/* */tools/* */build/* */fix_typos/* # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = ./images # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER ) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = ../README.md #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # function all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES, then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see http://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the config file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = YES # The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in # which the alphabetical index list will be split. # Minimum value: 1, maximum value: 20, default value: 5. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- # defined cascading style sheet that is included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefor more robust against future updates. # Doxygen will copy the style sheet file to the output directory. For an example # see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the stylesheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see # http://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to NO can help when comparing the output of multiple runs. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: http://developer.apple.com/tools/xcode/), introduced with # OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler ( hhc.exe). If non-empty # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated ( # YES) or that it should be included in the master .chm file ( NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated ( # YES) or a normal table of contents ( NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location of Qt's # qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the # generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has # the same information as the tab index, you could consider setting # DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # # Note that when changing this option you need to delete any form_*.png files in # the HTML output directory before the changes have effect. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # http://www.mathjax.org) which uses client side Javascript for the rendering # instead of using prerendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: # http://docs.mathjax.org/en/latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from http://www.mathjax.org before deployment. # The default value is: http://cdn.mathjax.org/mathjax/latest. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /