├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake ├── FindHIDAPI.cmake ├── FindMonero.cmake └── MyUtils.cmake ├── ext ├── CMakeLists.txt ├── dateparser.cpp ├── dateparser.h ├── fmt │ ├── .gitignore │ ├── CMakeLists.txt │ ├── CONTRIBUTING.md │ ├── ChangeLog.rst │ ├── LICENSE.rst │ ├── README.rst │ ├── include │ │ └── fmt │ │ │ ├── chrono.h │ │ │ ├── color.h │ │ │ ├── compile.h │ │ │ ├── core.h │ │ │ ├── format-inl.h │ │ │ ├── format.h │ │ │ ├── locale.h │ │ │ ├── ostream.h │ │ │ ├── posix.h │ │ │ ├── printf.h │ │ │ └── ranges.h │ ├── src │ │ ├── format.cc │ │ └── posix.cc │ └── support │ │ ├── Android.mk │ │ ├── AndroidManifest.xml │ │ ├── C++.sublime-syntax │ │ ├── README │ │ ├── Vagrantfile │ │ ├── appveyor-build.py │ │ ├── appveyor.yml │ │ ├── build.gradle │ │ ├── cmake │ │ ├── FindSetEnv.cmake │ │ ├── cxx14.cmake │ │ ├── fmt-config.cmake.in │ │ └── fmt.pc.in │ │ ├── compute-powers.py │ │ ├── docopt.py │ │ ├── fmt.pro │ │ ├── manage.py │ │ ├── rst2md.py │ │ ├── rtd │ │ ├── conf.py │ │ ├── index.rst │ │ └── theme │ │ │ ├── layout.html │ │ │ └── theme.conf │ │ ├── travis-build.py │ │ └── update-coverity-branch.py ├── infix_iterator.h └── minicsv.h ├── main.cpp └── src ├── CMakeLists.txt ├── CmdLineOptions.cpp ├── CmdLineOptions.h ├── MicroCore.cpp ├── MicroCore.h ├── monero_headers.h ├── tools.cpp ├── tools.h ├── tx_details.cpp └── tx_details.h /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .sass-cache 3 | *.*~ 4 | *.user 5 | .idea/ 6 | build/ 7 | cmake-build-debug/ 8 | 9 | .kdev4/ 10 | *.kdev4 11 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | set(PROJECT_NAME 4 | xmr2csv) 5 | 6 | project(${PROJECT_NAME}) 7 | 8 | set(CMAKE_CXX_FLAGS 9 | "${CMAKE_CXX_FLAGS} -std=c++14") 10 | 11 | if (NOT MONERO_DIR) 12 | set(MONERO_DIR ~/monero) 13 | endif() 14 | 15 | message(STATUS MONERO_DIR ": ${MONERO_DIR}") 16 | 17 | if (NOT MONERO_SOURCE_DIR) 18 | set(MONERO_SOURCE_DIR ${MONERO_DIR} 19 | CACHE PATH "Path to the root directory for Monero") 20 | endif() 21 | 22 | if (NOT MONERO_BUILD_DIR) 23 | # set location of monero build tree 24 | set(MONERO_BUILD_DIR ${MONERO_SOURCE_DIR}/build/release/ 25 | CACHE PATH "Path to the build directory for Monero") 26 | 27 | if (NOT EXISTS ${MONERO_BUILD_DIR}) 28 | # try different location 29 | message(STATUS "Trying different folder for monero libraries") 30 | set(MONERO_BUILD_DIR ${MONERO_SOURCE_DIR}/build/Linux/master/release/ 31 | CACHE PATH "Path to the build directory for Monero" FORCE) 32 | endif() 33 | 34 | endif() 35 | 36 | if (NOT EXISTS ${MONERO_BUILD_DIR}) 37 | message(FATAL_ERROR "Monero libraries not found in: ${MONERO_BUILD_DIR}") 38 | endif() 39 | 40 | message(STATUS MONERO_DIR ": ${MONERO_DIR}") 41 | 42 | set(MONERO_SOURCE_DIR ${MONERO_DIR} 43 | CACHE PATH "Path to the root directory for Monero") 44 | 45 | # set location of monero build tree 46 | set(MONERO_BUILD_DIR ${MONERO_SOURCE_DIR}/build/release/ 47 | CACHE PATH "Path to the build directory for Monero") 48 | 49 | set(MY_CMAKE_DIR "${CMAKE_CURRENT_LIST_DIR}/cmake" 50 | CACHE PATH "The path to the cmake directory of the current project") 51 | 52 | list(APPEND CMAKE_MODULE_PATH "${MY_CMAKE_DIR}") 53 | 54 | set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} "${MONERO_BUILD_DIR}" 55 | CACHE PATH "Add Monero directory for library searching") 56 | 57 | include(MyUtils) 58 | 59 | find_package(Monero) 60 | 61 | 62 | # find boost 63 | find_package(Boost COMPONENTS 64 | system 65 | filesystem 66 | thread 67 | date_time 68 | chrono 69 | regex 70 | serialization 71 | program_options 72 | date_time 73 | REQUIRED) 74 | 75 | # include boost headers 76 | include_directories( 77 | ${Boost_INCLUDE_DIRS} 78 | ${CMAKE_SOURCE_DIR}/ext/fmt) 79 | 80 | message("Xmr_WALLET-CRYPTO_LIBRARIES ${Xmr_WALLET-CRYPTO_LIBRARIES}") 81 | 82 | if("${Xmr_WALLET-CRYPTO_LIBRARIES}" STREQUAL "Xmr_WALLET-CRYPTO_LIBRARY-NOTFOUND") 83 | set(WALLET_CRYPTO "") 84 | else() 85 | set(WALLET_CRYPTO ${Xmr_WALLET-CRYPTO_LIBRARIES}) 86 | endif() 87 | 88 | add_subdirectory(ext/fmt) 89 | 90 | # add src/ subfolder 91 | add_subdirectory(ext/) 92 | 93 | # add src/ subfolder 94 | add_subdirectory(src/) 95 | 96 | # speficie source files 97 | set(SOURCE_FILES 98 | main.cpp) 99 | 100 | # make executable called xmreg01 101 | add_executable(${PROJECT_NAME} 102 | ${SOURCE_FILES}) 103 | 104 | set_target_properties(${PROJECT_NAME} 105 | PROPERTIES LINKER_LANGUAGE CXX) 106 | 107 | set(LIBRARIES 108 | myxrm 109 | myext 110 | wallet 111 | device 112 | ${WALLET_CRYPTO} 113 | blockchain_db 114 | cryptonote_core 115 | cryptonote_format_utils_basic 116 | cryptonote_protocol 117 | cryptonote_basic 118 | daemonizer 119 | cncrypto 120 | blocks 121 | lmdb 122 | ringct 123 | ringct_basic 124 | common 125 | mnemonics 126 | epee 127 | easylogging 128 | randomx 129 | hardforks 130 | device 131 | version 132 | sodium 133 | fmt::fmt-header-only 134 | ${Boost_LIBRARIES} 135 | pthread 136 | unbound 137 | curl 138 | crypto 139 | ssl) 140 | 141 | if (Xmr_CHECKPOINTS_LIBRARIES) 142 | set(LIBRARIES ${LIBRARIES} checkpoints) 143 | endif() 144 | 145 | 146 | if(APPLE) 147 | set(LIBRARIES ${LIBRARIES} "-framework IOKit") 148 | else() 149 | set(LIBRARIES ${LIBRARIES} atomic) 150 | endif() 151 | 152 | 153 | if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT WIN32) 154 | set(LIBRARIES ${LIBRARIES} unwind) 155 | endif() 156 | 157 | if (WIN32) 158 | set(LIBRARIES ${LIBRARIES} 159 | wsock32 160 | ntdll 161 | ws2_32 162 | Iphlpapi 163 | ) 164 | else() 165 | set(LIBRARIES ${LIBRARIES} dl) 166 | endif() 167 | 168 | find_package(HIDAPI) 169 | set(LIBRARIES ${LIBRARIES} ${HIDAPI_LIBRARIES}) 170 | 171 | target_link_libraries(${PROJECT_NAME} ${LIBRARIES}) 172 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017, moneroexamples 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Exporting our transactions from blockchain and searching for ring members into csv files 2 | 3 | In this example, it is shown how to export our transactions from the blockchain 4 | into a csv file. This can be very useful 5 | for making summaries or reports of our incoming and outgoing (when spend key is given) 6 | transactions. 7 | 8 | Also it shows how we can search in which transactions used our outputs as 9 | its ring members. 10 | 11 | 12 | 13 | ## Compilation on Ubuntu 22.04 14 | 15 | ##### Compile latest Monero 16 | 17 | Download and compile recent Monero into your home folder as shown in the following link: 18 | 19 | https://github.com/moneroexamples/monero-compilation#example-compilation-of-master-branch-ie-development-version-of-monero-on-ubuntu-2004 20 | 21 | ##### Compile the transactions-export 22 | 23 | Once the Monero is compiles, the transactions-export can be downloaded and compiled 24 | as follows: 25 | 26 | ```bash 27 | # go to home folder if still in ~/monero 28 | cd ~ 29 | 30 | # download the source code 31 | git clone https://github.com/moneroexamples/transactions-export.git 32 | 33 | # enter the downloaded sourced code folder 34 | cd transactions-export 35 | 36 | # make a build folder and enter it 37 | mkdir build && cd build 38 | 39 | # create the makefile 40 | cmake .. 41 | 42 | # altearnatively can use: cmake -DMONERO_DIR=/path/to/monero_folder .. 43 | # if monero is not in ~/monero 44 | 45 | # compile 46 | make 47 | ``` 48 | 49 | 50 | ## Program options 51 | ```bash 52 | [mwo@arch cmake-build-debug]$ ./xmr2csv -h 53 | xmr2csv, export all your transactions into csv file: 54 | -h [ --help ] [=arg(=1)] (=0) produce help message 55 | -a [ --address ] arg monero address string 56 | -v [ --viewkey ] arg private view key string 57 | -s [ --spendkey ] arg private spend key string 58 | -t [ --start-height ] arg start from given height 59 | -n [ --no-of-blocks ] arg (=0) number of blocks to search starting 60 | from start-height 61 | -d [ --start-date ] arg start roughly from given date: 62 | yyyy-mm-dd 63 | -c [ --out-csv-file ] arg (=xmr_report.csv) 64 | name of outputs csv file 65 | -r [ --out-csv-file2 ] arg (=xmr_report_ring_members.csv) 66 | name of outputs csv file for file 67 | containing out outputs as ring members 68 | -r [ --out-csv-file3 ] arg (=xmr_report_ring_members_freq.csv) 69 | name of outputs csv file for file 70 | containing frequencies of outputs as 71 | ring members 72 | -r [ --out-csv-file4 ] arg (=xmr_report_key_images_outputs.csv) 73 | name of outputs csv file for file 74 | containing all key images scanned with 75 | the referenced output public keys 76 | -b [ --bc-path ] arg path to lmdb blockchain 77 | --testnet [=arg(=1)] (=0) is the address from testnet network 78 | -m [ --ring-members ] [=arg(=1)] (=0) search where our outputs are as ring 79 | members 80 | --all-outputs [=arg(=1)] (=0) save all outputs, whether they are ours 81 | or not 82 | --all-key-images [=arg(=1)] (=0) save all key_images, whether they are 83 | ours or not, with referenced output 84 | public keys 85 | ``` 86 | 87 | ## Getting our outputs using address and viewkey 88 | 89 | For testing of the viewer, one can use [official address and viewkey](https://github.com/monero-project/bitmonero#supporting-the-project) 90 | of the Monero, i.e., 91 | 92 | - Address: 44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A 93 | - Viewkey: f359631075708155cc3d92a32b75a7d02a5dcf27756707b47a2b31b21c389501 94 | 95 | ```bash 96 | ./xmr2csv -a 44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A -v f359631075708155cc3d92a32b75a7d02a5dcf27756707b47a2b31b21c389501 -c ./current.csv 97 | ``` 98 | 99 | Monero [forum donation](https://www.reddit.com/r/Monero/comments/5j2rm7/in_last_four_weekes_there_were_about_850_xmr/dbdmzt7/?context=3), 100 | 101 | - Address: 45ttEikQEZWN1m7VxaVN9rjQkpSdmpGZ82GwUps66neQ1PqbQMno4wMY8F5jiDt2GoHzCtMwa7PDPJUJYb1GYrMP4CwAwNp 102 | - Viewkey: c9347bc1e101eab46d3a6532c5b6066e925f499b47d285d5720e6a6f4cc4350c 103 | 104 | ```bash 105 | ./xmr2csv -a 45ttEikQEZWN1m7VxaVN9rjQkpSdmpGZ82GwUps66neQ1PqbQMno4wMY8F5jiDt2GoHzCtMwa7PDPJUJYb1GYrMP4CwAwNp -v c9347bc1e101eab46d3a6532c5b6066e925f499b47d285d5720e6a6f4cc4350c -c ./forum.csv 106 | ``` 107 | 108 | [Old Monero donation](https://github.com/monero-project/monero/pull/714/files) address: 109 | 110 | - Address: 46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em 111 | - Viewkey: e422831985c9205238ef84daf6805526c14d96fd7b059fe68c7ab98e495e5703 112 | 113 | ```bash 114 | ./xmr2csv -a 46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em -v e422831985c9205238ef84daf6805526c14d96fd7b059fe68c7ab98e495e5703 -c ./old.csv 115 | ``` 116 | 117 | 118 | 119 | ## Searching txs which use our outputs as ring members and their frequency 120 | 121 | Just add `-m` flag. This will produce `xmr_report_ring_members.csv` and `xmr_report_ring_members_frew.csv` files 122 | (default names) which 123 | contain the list of txs which use our outputs, and frequency of outputs use, respectively. To speed up the search 124 | we can use `-t` flag to specify starting block. 125 | 126 | #### Example 1 (Monero project donation address) 127 | 128 | ```bash 129 | ./xmr2csv -t 00580000 -m -a 45ttEikQEZWN1m7VxaVN9rjQkpSdmpGZ82GwUps66neQ1PqbQMno4wMY8F5jiDt2GoHzCtMwa7PDPJUJYb1GYrMP4CwAwNp -v c9347bc1e101eab46d3a6532c5b6066e925f499b47d285d5720e6a6f4cc4350c 130 | ``` 131 | 132 | Example frequency list output: 133 | 134 | ``` 135 | Most frequent outputs used as ring members are: 136 | - <6c04b7393d223b4c3b35493219c6a4d9bb37b6739c9a22193394d5e81964b5a3>: 12 times with ring sizes of 3_3_4_23_3_3_3_3_5_11_5_21 137 | - : 11 times with ring sizes of 3_3_3_5_4_3_4_3_5_5_5 138 | - : 11 times with ring sizes of 3_3_3_5_3_3_5_3_41_5_5 139 | - <51897c55c3b25daae30bf04cc8cebacddf559fb0cb6f4194f1e292931b57c29a>: 10 times with ring sizes of 4_3_3_3_3_5_10_3_3_5 140 | - <3ae472a405ffb000fa14cb380a5408cdebe25610ab16ced17cc9cf601dcf3860>: 10 times with ring sizes of 26_11_4_9_13_11_4_3_5_11 141 | - <04782e24ca5c944f0d4bb764fd3b95c6b6f59144ee2a3c7530a2269e4a19a650>: 10 times with ring sizes of 1_27_21_26_11_3_5_3_3_3 142 | - <435d2667c93dd7f5960d02cb920ee8db8fda693dd699e1c659703bf1fc359a86>: 10 times with ring sizes of 3_3_5_5_3_5_5_21_5_5 143 | - <0c6eec50b66c9a453ba6ad7b5228d3d224d187cd53ef215076d55d2347018969>: 10 times with ring sizes of 3_5_5_3_5_3_3_5_5_5 144 | - <253641711e995282ecd9182a862d474a782b2d642369dd4759e4c81dd8c117e7>: 10 times with ring sizes of 26_5_5_5_3_3_3_3_3_5 145 | ``` 146 | 147 | #### Example 2 (Monerujo project donation address) 148 | 149 | 150 | ```bash 151 | ./xmr2csv -t 01381000 -m -a 4AdkPJoxn7JCvAby9szgnt93MSEwdnxdhaASxbTBm6x5dCwmsDep2UYN4FhStDn5i11nsJbpU7oj59ahg8gXb1Mg3viqCuk -v b1aff2a12191723da0afbe75516f94dd8b068215f6e847d8da57aca5f1f98e0c 152 | ``` 153 | 154 | Example frequency list output: 155 | 156 | ``` 157 | Most frequent outputs used as ring members are: 158 | - <024a777f20a02a3c145c27e8dd53d05c708b78e1c9832f6b843acfd29c6ac469>: 10 times with ring sizes of 5_5_6_5_5_5_5_5_5_41 159 | - <64d6394f2f11142ef04dc67bb7bd2ae5e0ff0f11d0a211e4fe28d58dfdd6d6e3>: 7 times with ring sizes of 5_5_6_5_5_5_5 160 | - : 7 times with ring sizes of 5_5_26_5_5_5_5 161 | - : 7 times with ring sizes of 5_3_3_5_5_5_21 162 | - : 7 times with ring sizes of 6_5_5_5_5_5_5 163 | - <77328c5ea44e92c487205b24e10860bf73167907924b178923261dcb2d4b41d3>: 7 times with ring sizes of 5_5_5_5_5_5_5 164 | - : 6 times with ring sizes of 5_5_6_3_5_5 165 | - : 6 times with ring sizes of 5_5_5_9_5_5 166 | - <1b92b3e6c650c248a4865a95a15e1d264037b4068c7053119d5288aa70e60c8f>: 6 times with ring sizes of 5_5_5_5_5_21 167 | ``` 168 | 169 | ## How can you help? 170 | 171 | Constructive criticism, code and website edits are always good. They can be made through github. 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | -------------------------------------------------------------------------------- /cmake/FindHIDAPI.cmake: -------------------------------------------------------------------------------- 1 | # - try to find HIDAPI library 2 | # from http://www.signal11.us/oss/hidapi/ 3 | # 4 | # Cache Variables: (probably not for direct use in your scripts) 5 | # HIDAPI_INCLUDE_DIR 6 | # HIDAPI_LIBRARY 7 | # 8 | # Non-cache variables you might use in your CMakeLists.txt: 9 | # HIDAPI_FOUND 10 | # HIDAPI_INCLUDE_DIRS 11 | # HIDAPI_LIBRARIES 12 | # 13 | # Requires these CMake modules: 14 | # FindPackageHandleStandardArgs (known included with CMake >=2.6.2) 15 | # 16 | # Original Author: 17 | # 2009-2010 Ryan Pavlik 18 | # http://academic.cleardefinition.com 19 | # Iowa State University HCI Graduate Program/VRAC 20 | # 21 | # Copyright Iowa State University 2009-2010. 22 | # Distributed under the Boost Software License, Version 1.0. 23 | # (See accompanying file LICENSE_1_0.txt or copy at 24 | # http://www.boost.org/LICENSE_1_0.txt) 25 | 26 | find_library(HIDAPI_LIBRARY 27 | NAMES hidapi hidapi-libusb) 28 | 29 | find_path(HIDAPI_INCLUDE_DIR 30 | NAMES hidapi.h 31 | PATH_SUFFIXES 32 | hidapi) 33 | 34 | include(FindPackageHandleStandardArgs) 35 | find_package_handle_standard_args(HIDAPI 36 | DEFAULT_MSG 37 | HIDAPI_LIBRARY 38 | HIDAPI_INCLUDE_DIR) 39 | 40 | if(HIDAPI_FOUND) 41 | set(HIDAPI_LIBRARIES "${HIDAPI_LIBRARY}") 42 | if((STATIC AND UNIX AND NOT APPLE) OR (DEPENDS AND CMAKE_SYSTEM_NAME STREQUAL "Linux")) 43 | find_library(LIBUSB-1.0_LIBRARY usb-1.0) 44 | find_library(LIBUDEV_LIBRARY udev) 45 | if(LIBUSB-1.0_LIBRARY) 46 | set(HIDAPI_LIBRARIES "${HIDAPI_LIBRARIES};${LIBUSB-1.0_LIBRARY}") 47 | if(LIBUDEV_LIBRARY) 48 | set(HIDAPI_LIBRARIES "${HIDAPI_LIBRARIES};${LIBUDEV_LIBRARY}") 49 | else() 50 | message(WARNING "libudev library not found, binaries may fail to link.") 51 | endif() 52 | else() 53 | message(WARNING "libusb-1.0 library not found, binaries may fail to link.") 54 | endif() 55 | endif() 56 | 57 | set(HIDAPI_INCLUDE_DIRS "${HIDAPI_INCLUDE_DIR}") 58 | endif() 59 | 60 | mark_as_advanced(HIDAPI_INCLUDE_DIR HIDAPI_LIBRARY) 61 | -------------------------------------------------------------------------------- /cmake/FindMonero.cmake: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------ 2 | # CMake helper for the majority of the cpp-ethereum modules. 3 | # 4 | # This module defines 5 | # Monero_XXX_LIBRARIES, the libraries needed to use ethereum. 6 | # Monero_FOUND, If false, do not try to use ethereum. 7 | # 8 | # File addetped from cpp-ethereum 9 | # 10 | # The documentation for cpp-ethereum is hosted at http://cpp-ethereum.org 11 | # 12 | # ------------------------------------------------------------------------------ 13 | # This file is part of cpp-ethereum. 14 | # 15 | # cpp-ethereum is free software: you can redistribute it and/or modify 16 | # it under the terms of the GNU General Public License as published by 17 | # the Free Software Foundation, either version 3 of the License, or 18 | # (at your option) any later version. 19 | # 20 | # cpp-ethereum is distributed in the hope that it will be useful, 21 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | # GNU General Public License for more details. 24 | # 25 | # You should have received a copy of the GNU General Public License 26 | # along with cpp-ethereum. If not, see 27 | # 28 | # (c) 2014-2016 cpp-ethereum contributors. 29 | #------------------------------------------------------------------------------ 30 | 31 | set(LIBS common;blocks;cryptonote_basic;cryptonote_core;multisig; 32 | cryptonote_protocol;daemonizer;mnemonics;epee;lmdb;device;wallet-crypto; 33 | blockchain_db;ringct;wallet;cncrypto;easylogging;version; 34 | checkpoints;randomx;hardforks;miniupnpc) 35 | 36 | set(Xmr_INCLUDE_DIRS "${CPP_MONERO_DIR}") 37 | 38 | # if the project is a subset of main cpp-ethereum project 39 | # use same pattern for variables as Boost uses 40 | 41 | foreach (l ${LIBS}) 42 | 43 | string(TOUPPER ${l} L) 44 | 45 | find_library(Xmr_${L}_LIBRARY 46 | NAMES ${l} 47 | PATHS ${CMAKE_LIBRARY_PATH} 48 | PATH_SUFFIXES "/src/${l}" "/src/" "/external/db_drivers/lib${l}" "/lib" "/src/crypto" "/src/crypto/wallet" "/contrib/epee/src" "/external/easylogging++/" "/external/${l}" "external/miniupnp/miniupnpc" 49 | NO_DEFAULT_PATH 50 | ) 51 | 52 | set(Xmr_${L}_LIBRARIES ${Xmr_${L}_LIBRARY}) 53 | 54 | message(STATUS FindMonero " Xmr_${L}_LIBRARIES ${Xmr_${L}_LIBRARY}") 55 | 56 | if(NOT "${Xmr_${L}_LIBRARIES}" STREQUAL "${Xmr_${L}_LIBRARY-NOTFOUND}") 57 | add_library(${l} STATIC IMPORTED) 58 | set_property(TARGET ${l} PROPERTY IMPORTED_LOCATION ${Xmr_${L}_LIBRARIES}) 59 | endif() 60 | 61 | endforeach() 62 | 63 | if (EXISTS ${MONERO_BUILD_DIR}/src/ringct/libringct_basic.a) 64 | message(STATUS FindMonero " found libringct_basic.a") 65 | add_library(ringct_basic STATIC IMPORTED) 66 | set_property(TARGET ringct_basic 67 | PROPERTY IMPORTED_LOCATION ${MONERO_BUILD_DIR}/src/ringct/libringct_basic.a) 68 | endif() 69 | 70 | if (EXISTS ${MONERO_BUILD_DIR}/src/cryptonote_basic/libcryptonote_format_utils_basic.a) 71 | message(STATUS FindMonero " found libcryptonote_format_utils_basic.a") 72 | add_library(cryptonote_format_utils_basic STATIC IMPORTED) 73 | set_property(TARGET cryptonote_format_utils_basic 74 | PROPERTY IMPORTED_LOCATION ${MONERO_BUILD_DIR}/src/cryptonote_basic/libcryptonote_format_utils_basic.a) 75 | endif() 76 | 77 | 78 | message(STATUS ${MONERO_SOURCE_DIR}/build) 79 | 80 | # include monero headers 81 | include_directories( 82 | ${MONERO_SOURCE_DIR}/src 83 | ${MONERO_SOURCE_DIR}/src/crypto 84 | ${MONERO_SOURCE_DIR}/src/crypto/wallet 85 | ${MONERO_SOURCE_DIR}/external 86 | ${MONERO_SOURCE_DIR}/external/randomx/src 87 | ${MONERO_SOURCE_DIR}/build 88 | ${MONERO_SOURCE_DIR}/external/easylogging++ 89 | ${MONERO_SOURCE_DIR}/contrib/epee/include 90 | ${MONERO_SOURCE_DIR}/external/db_drivers/liblmdb 91 | ${MONERO_SOURCE_DIR}/generated_include/crypto/wallet) 92 | -------------------------------------------------------------------------------- /cmake/MyUtils.cmake: -------------------------------------------------------------------------------- 1 | 2 | macro(configure_files srcDir destDir) 3 | message(STATUS "Configuring directory ${destDir}") 4 | make_directory(${destDir}) 5 | 6 | file(GLOB templateFiles RELATIVE ${srcDir} ${srcDir}/*) 7 | foreach(templateFile ${templateFiles}) 8 | set(srcTemplatePath ${srcDir}/${templateFile}) 9 | if(NOT IS_DIRECTORY ${srcTemplatePath}) 10 | message(STATUS "Configuring file ${templateFile}") 11 | configure_file( 12 | ${srcTemplatePath} 13 | ${destDir}/${templateFile} 14 | @ONLY) 15 | endif(NOT IS_DIRECTORY ${srcTemplatePath}) 16 | endforeach(templateFile) 17 | endmacro(configure_files) 18 | 19 | macro(create_git_version) 20 | # Get the current working branch 21 | execute_process( 22 | COMMAND git rev-parse --abbrev-ref HEAD 23 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 24 | OUTPUT_VARIABLE GIT_BRANCH 25 | OUTPUT_STRIP_TRAILING_WHITESPACE 26 | ) 27 | 28 | # http://xit0.org/2013/04/cmake-use-git-branch-and-commit-details-in-project/ 29 | # Get the latest abbreviated commit hash of the working branch 30 | execute_process( 31 | COMMAND git log -1 --format=%h 32 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 33 | OUTPUT_VARIABLE GIT_COMMIT_HASH 34 | OUTPUT_STRIP_TRAILING_WHITESPACE 35 | ) 36 | 37 | # Get the date and time of last commit 38 | execute_process( 39 | COMMAND git log -1 --format=%cd --date=short 40 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 41 | OUTPUT_VARIABLE GIT_COMMIT_DATETIME 42 | OUTPUT_STRIP_TRAILING_WHITESPACE 43 | ) 44 | 45 | # Get current branch name 46 | execute_process( 47 | COMMAND git rev-parse --abbrev-ref HEAD 48 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 49 | OUTPUT_VARIABLE GIT_BRANCH_NAME 50 | OUTPUT_STRIP_TRAILING_WHITESPACE 51 | ) 52 | 53 | 54 | 55 | configure_file( 56 | ${CMAKE_SOURCE_DIR}/src/version.h.in 57 | ${CMAKE_BINARY_DIR}/gen/version.h 58 | ) 59 | 60 | include_directories(${CMAKE_BINARY_DIR}/gen) 61 | 62 | endmacro(create_git_version) -------------------------------------------------------------------------------- /ext/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | project(myext) 4 | 5 | set(SOURCE_HEADERS 6 | minicsv.h 7 | dateparser.h) 8 | 9 | set(SOURCE_FILES 10 | dateparser.cpp) 11 | 12 | 13 | # make static library called libmyxrm 14 | # that we are going to link to 15 | # in the root CMakeLists.txt file 16 | add_library(myext 17 | STATIC 18 | ${SOURCE_FILES}) 19 | -------------------------------------------------------------------------------- /ext/dateparser.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by marcin on 22/11/15. 3 | // 4 | 5 | #include "dateparser.h" 6 | 7 | 8 | dateparser::dateparser(std::string fmt) 9 | { 10 | // set format 11 | using namespace boost::local_time; 12 | local_time_input_facet* input_facet = new local_time_input_facet(); 13 | input_facet->format(fmt.c_str()); 14 | ss.imbue(std::locale(ss.getloc(), input_facet)); 15 | } 16 | 17 | 18 | bool 19 | dateparser::operator()(std::string const& text) 20 | { 21 | ss.clear(); 22 | ss.str(text); 23 | 24 | bool ok = bool(ss >> pt); 25 | 26 | if (ok) 27 | { 28 | auto tm = to_tm(pt); 29 | year = tm.tm_year; 30 | month = tm.tm_mon + 1; // for 1-based (1:jan, .. 12:dec) 31 | day = tm.tm_mday; 32 | } 33 | 34 | return ok; 35 | } -------------------------------------------------------------------------------- /ext/dateparser.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by mwo on 22/11/15. 3 | // 4 | 5 | #ifndef XMR2CSV_DATEPARSER_H 6 | #define XMR2CSV_DATEPARSER_H 7 | 8 | #include 9 | #include 10 | 11 | 12 | // taken from: http://stackoverflow.com/a/19482908/248823 13 | struct dateparser 14 | { 15 | boost::posix_time::ptime pt; 16 | unsigned year, month, day; 17 | 18 | dateparser(std::string fmt); 19 | 20 | bool 21 | operator()(std::string const& text); 22 | 23 | private: 24 | std::stringstream ss; 25 | }; 26 | 27 | #endif //XMR2CSV_DATEPARSER_H 28 | -------------------------------------------------------------------------------- /ext/fmt/.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | /_CPack_Packages 3 | /doc/doxyxml 4 | /doc/html 5 | virtualenv 6 | /Testing 7 | /install_manifest.txt 8 | *~ 9 | *.a 10 | *.so* 11 | *.zip 12 | cmake_install.cmake 13 | CPack*.cmake 14 | fmt-*.cmake 15 | CTestTestfile.cmake 16 | CMakeCache.txt 17 | CMakeFiles 18 | Makefile 19 | run-msbuild.bat 20 | -------------------------------------------------------------------------------- /ext/fmt/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1.0) 2 | 3 | # Use newer policies if available, up to most recent tested version of CMake. 4 | if(${CMAKE_VERSION} VERSION_LESS 3.11) 5 | cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) 6 | else() 7 | cmake_policy(VERSION 3.11) 8 | endif() 9 | 10 | # Determine if fmt is built as a subproject (using add_subdirectory) 11 | # or if it is the master project. 12 | set(MASTER_PROJECT OFF) 13 | if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) 14 | set(MASTER_PROJECT ON) 15 | message(STATUS "CMake version: ${CMAKE_VERSION}") 16 | endif () 17 | 18 | # Joins arguments and places the results in ${result_var}. 19 | function(join result_var) 20 | set(result ) 21 | foreach (arg ${ARGN}) 22 | set(result "${result}${arg}") 23 | endforeach () 24 | set(${result_var} "${result}" PARENT_SCOPE) 25 | endfunction() 26 | 27 | # Set the default CMAKE_BUILD_TYPE to Release. 28 | # This should be done before the project command since the latter can set 29 | # CMAKE_BUILD_TYPE itself (it does so for nmake). 30 | if (MASTER_PROJECT AND NOT CMAKE_BUILD_TYPE) 31 | join(doc "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or " 32 | "CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.") 33 | set(CMAKE_BUILD_TYPE Release CACHE STRING ${doc}) 34 | endif () 35 | 36 | option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF) 37 | option(FMT_WERROR "Halt the compilation with an error on compiler warnings." 38 | OFF) 39 | 40 | # Options that control generation of various targets. 41 | option(FMT_DOC "Generate the doc target." ${MASTER_PROJECT}) 42 | option(FMT_INSTALL "Generate the install target." ${MASTER_PROJECT}) 43 | option(FMT_TEST "Generate the test target." ${MASTER_PROJECT}) 44 | option(FMT_FUZZ "Generate the fuzz target." OFF) 45 | 46 | project(FMT CXX) 47 | 48 | # Get version from core.h 49 | file(READ include/fmt/core.h core_h) 50 | if (NOT core_h MATCHES "FMT_VERSION ([0-9]+)([0-9][0-9])([0-9][0-9])") 51 | message(FATAL_ERROR "Cannot get FMT_VERSION from core.h.") 52 | endif () 53 | # Use math to skip leading zeros if any. 54 | math(EXPR CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1}) 55 | math(EXPR CPACK_PACKAGE_VERSION_MINOR ${CMAKE_MATCH_2}) 56 | math(EXPR CPACK_PACKAGE_VERSION_PATCH ${CMAKE_MATCH_3}) 57 | join(FMT_VERSION ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}. 58 | ${CPACK_PACKAGE_VERSION_PATCH}) 59 | message(STATUS "Version: ${FMT_VERSION}") 60 | 61 | message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") 62 | 63 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 64 | 65 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} 66 | "${CMAKE_CURRENT_SOURCE_DIR}/support/cmake") 67 | 68 | include(cxx14) 69 | include(CheckCXXCompilerFlag) 70 | 71 | set(FMT_REQUIRED_FEATURES cxx_auto_type cxx_variadic_templates) 72 | 73 | if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") 74 | set(PEDANTIC_COMPILE_FLAGS -pedantic-errors -Wall -Wextra -pedantic 75 | -Wold-style-cast -Wundef 76 | -Wredundant-decls -Wwrite-strings -Wpointer-arith 77 | -Wcast-qual -Wformat=2 -Wmissing-include-dirs 78 | -Wcast-align -Wnon-virtual-dtor 79 | -Wctor-dtor-privacy -Wdisabled-optimization 80 | -Winvalid-pch -Woverloaded-virtual 81 | -Wconversion -Wswitch-enum 82 | -Wno-ctor-dtor-privacy -Wno-format-nonliteral -Wno-shadow) 83 | if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6) 84 | set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wnoexcept 85 | -Wno-dangling-else -Wno-unused-local-typedefs) 86 | endif () 87 | if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) 88 | set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wdouble-promotion 89 | -Wtrampolines -Wzero-as-null-pointer-constant -Wuseless-cast 90 | -Wvector-operation-performance -Wsized-deallocation) 91 | endif () 92 | if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0) 93 | set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wshift-overflow=2 94 | -Wnull-dereference -Wduplicated-cond) 95 | endif () 96 | set(WERROR_FLAG -Werror) 97 | endif () 98 | 99 | if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") 100 | set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -pedantic -Wconversion 101 | -Wno-sign-conversion -Wdeprecated -Wweak-vtables) 102 | check_cxx_compiler_flag(-Wzero-as-null-pointer-constant HAS_NULLPTR_WARNING) 103 | if (HAS_NULLPTR_WARNING) 104 | set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} 105 | -Wzero-as-null-pointer-constant) 106 | endif () 107 | set(WERROR_FLAG -Werror) 108 | endif () 109 | 110 | if (MSVC) 111 | set(PEDANTIC_COMPILE_FLAGS /W3) 112 | set(WERROR_FLAG /WX) 113 | endif () 114 | 115 | if (MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio") 116 | # If Microsoft SDK is installed create script run-msbuild.bat that 117 | # calls SetEnv.cmd to set up build environment and runs msbuild. 118 | # It is useful when building Visual Studio projects with the SDK 119 | # toolchain rather than Visual Studio. 120 | include(FindSetEnv) 121 | if (WINSDK_SETENV) 122 | set(MSBUILD_SETUP "call \"${WINSDK_SETENV}\"") 123 | endif () 124 | # Set FrameworkPathOverride to get rid of MSB3644 warnings. 125 | set(netfxpath "C:\\Program Files\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.0") 126 | file(WRITE run-msbuild.bat " 127 | ${MSBUILD_SETUP} 128 | ${CMAKE_MAKE_PROGRAM} -p:FrameworkPathOverride=\"${netfxpath}\" %*") 129 | endif () 130 | 131 | set(strtod_l_headers stdlib.h) 132 | if (APPLE) 133 | set(strtod_l_headers ${strtod_l_headers} xlocale.h) 134 | endif () 135 | 136 | include(CheckSymbolExists) 137 | if (WIN32) 138 | check_symbol_exists(_strtod_l "${strtod_l_headers}" HAVE_STRTOD_L) 139 | else () 140 | check_symbol_exists(strtod_l "${strtod_l_headers}" HAVE_STRTOD_L) 141 | endif () 142 | 143 | function(add_headers VAR) 144 | set(headers ${${VAR}}) 145 | foreach (header ${ARGN}) 146 | set(headers ${headers} include/fmt/${header}) 147 | endforeach() 148 | set(${VAR} ${headers} PARENT_SCOPE) 149 | endfunction() 150 | 151 | # Define the fmt library, its includes and the needed defines. 152 | add_headers(FMT_HEADERS chrono.h color.h compile.h core.h format.h format-inl.h 153 | locale.h ostream.h posix.h printf.h ranges.h) 154 | set(FMT_SOURCES src/format.cc src/posix.cc) 155 | 156 | add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst) 157 | add_library(fmt::fmt ALIAS fmt) 158 | 159 | if (HAVE_STRTOD_L) 160 | target_compile_definitions(fmt PUBLIC FMT_LOCALE) 161 | endif () 162 | 163 | if (FMT_WERROR) 164 | target_compile_options(fmt PRIVATE ${WERROR_FLAG}) 165 | endif () 166 | if (FMT_PEDANTIC) 167 | target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS}) 168 | endif () 169 | 170 | target_compile_features(fmt INTERFACE ${FMT_REQUIRED_FEATURES}) 171 | 172 | target_include_directories(fmt PUBLIC 173 | $ 174 | $) 175 | 176 | set_target_properties(fmt PROPERTIES 177 | VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR} 178 | DEBUG_POSTFIX d) 179 | 180 | if (BUILD_SHARED_LIBS) 181 | if (UNIX AND NOT APPLE AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "SunOS") 182 | # Fix rpmlint warning: 183 | # unused-direct-shlib-dependency /usr/lib/libformat.so.1.1.0 /lib/libm.so.6. 184 | target_link_libraries(fmt -Wl,--as-needed) 185 | endif () 186 | target_compile_definitions(fmt PRIVATE FMT_EXPORT INTERFACE FMT_SHARED) 187 | endif () 188 | if (FMT_SAFE_DURATION_CAST) 189 | target_compile_definitions(fmt PUBLIC FMT_SAFE_DURATION_CAST) 190 | endif() 191 | 192 | add_library(fmt-header-only INTERFACE) 193 | add_library(fmt::fmt-header-only ALIAS fmt-header-only) 194 | 195 | target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1) 196 | 197 | target_compile_features(fmt-header-only INTERFACE ${FMT_REQUIRED_FEATURES}) 198 | 199 | target_include_directories(fmt-header-only INTERFACE 200 | $ 201 | $) 202 | 203 | # Install targets. 204 | if (FMT_INSTALL) 205 | include(GNUInstallDirs) 206 | include(CMakePackageConfigHelpers) 207 | set(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING 208 | "Installation directory for cmake files, relative to ${CMAKE_INSTALL_PREFIX}.") 209 | set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake) 210 | set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake) 211 | set(pkgconfig ${PROJECT_BINARY_DIR}/fmt.pc) 212 | set(targets_export_name fmt-targets) 213 | 214 | set (INSTALL_TARGETS fmt) 215 | if (TARGET fmt-header-only) 216 | set(INSTALL_TARGETS ${INSTALL_TARGETS} fmt-header-only) 217 | endif () 218 | 219 | set(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING 220 | "Installation directory for libraries, relative to ${CMAKE_INSTALL_PREFIX}.") 221 | 222 | set(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR}/fmt CACHE STRING 223 | "Installation directory for include files, relative to ${CMAKE_INSTALL_PREFIX}.") 224 | 225 | set(FMT_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig CACHE PATH 226 | "Installation directory for pkgconfig (.pc) files, relative to ${CMAKE_INSTALL_PREFIX}.") 227 | 228 | # Generate the version, config and target files into the build directory. 229 | write_basic_package_version_file( 230 | ${version_config} 231 | VERSION ${FMT_VERSION} 232 | COMPATIBILITY AnyNewerVersion) 233 | configure_file( 234 | "${PROJECT_SOURCE_DIR}/support/cmake/fmt.pc.in" 235 | "${pkgconfig}" 236 | @ONLY) 237 | configure_package_config_file( 238 | ${PROJECT_SOURCE_DIR}/support/cmake/fmt-config.cmake.in 239 | ${project_config} 240 | INSTALL_DESTINATION ${FMT_CMAKE_DIR}) 241 | # Use a namespace because CMake provides better diagnostics for namespaced 242 | # imported targets. 243 | export(TARGETS ${INSTALL_TARGETS} NAMESPACE fmt:: 244 | FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake) 245 | 246 | # Install version, config and target files. 247 | install( 248 | FILES ${project_config} ${version_config} 249 | DESTINATION ${FMT_CMAKE_DIR}) 250 | install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR} 251 | NAMESPACE fmt::) 252 | 253 | # Install the library and headers. 254 | install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name} 255 | LIBRARY DESTINATION ${FMT_LIB_DIR} 256 | ARCHIVE DESTINATION ${FMT_LIB_DIR} 257 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) 258 | 259 | install(FILES $ 260 | DESTINATION ${FMT_LIB_DIR} OPTIONAL) 261 | install(FILES ${FMT_HEADERS} DESTINATION ${FMT_INC_DIR}) 262 | install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}") 263 | endif () 264 | 265 | if (FMT_DOC) 266 | add_subdirectory(doc) 267 | endif () 268 | 269 | if (FMT_TEST) 270 | enable_testing() 271 | add_subdirectory(test) 272 | endif () 273 | 274 | # Control fuzzing independent of the unit tests. 275 | if (FMT_FUZZ) 276 | add_subdirectory(test/fuzzing) 277 | endif () 278 | 279 | set(gitignore ${PROJECT_SOURCE_DIR}/.gitignore) 280 | if (MASTER_PROJECT AND EXISTS ${gitignore}) 281 | # Get the list of ignored files from .gitignore. 282 | file (STRINGS ${gitignore} lines) 283 | LIST(REMOVE_ITEM lines /doc/html) 284 | foreach (line ${lines}) 285 | string(REPLACE "." "[.]" line "${line}") 286 | string(REPLACE "*" ".*" line "${line}") 287 | set(ignored_files ${ignored_files} "${line}$" "${line}/") 288 | endforeach () 289 | set(ignored_files ${ignored_files} 290 | /.git /breathe /format-benchmark sphinx/ .buildinfo .doctrees) 291 | 292 | set(CPACK_SOURCE_GENERATOR ZIP) 293 | set(CPACK_SOURCE_IGNORE_FILES ${ignored_files}) 294 | set(CPACK_SOURCE_PACKAGE_FILE_NAME fmt-${FMT_VERSION}) 295 | set(CPACK_PACKAGE_NAME fmt) 296 | set(CPACK_RESOURCE_FILE_README ${PROJECT_SOURCE_DIR}/README.rst) 297 | include(CPack) 298 | endif () 299 | -------------------------------------------------------------------------------- /ext/fmt/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing to {fmt} 2 | ===================== 3 | 4 | By submitting a pull request or a patch, you represent that you have the right 5 | to license your contribution to the {fmt} project owners and the community, 6 | agree that your contributions are licensed under the {fmt} license, and agree 7 | to future changes to the licensing. 8 | 9 | All C++ code must adhere to [Google C++ Style Guide]( 10 | https://google.github.io/styleguide/cppguide.html) with the following 11 | exceptions: 12 | 13 | * Exceptions are permitted 14 | * snake_case should be used instead of UpperCamelCase for function and type 15 | names 16 | 17 | Thanks for contributing! 18 | -------------------------------------------------------------------------------- /ext/fmt/LICENSE.rst: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 - present, Victor Zverovich 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | --- Optional exception to the license --- 23 | 24 | As an exception, if, as a result of your compiling your source code, portions 25 | of this Software are embedded into a machine-executable object form of such 26 | source code, you may redistribute such embedded portions in such object form 27 | without including the above copyright and permission notices. 28 | -------------------------------------------------------------------------------- /ext/fmt/README.rst: -------------------------------------------------------------------------------- 1 | {fmt} 2 | ===== 3 | 4 | .. image:: https://travis-ci.org/fmtlib/fmt.png?branch=master 5 | :target: https://travis-ci.org/fmtlib/fmt 6 | 7 | .. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v 8 | :target: https://ci.appveyor.com/project/vitaut/fmt 9 | 10 | .. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/libfmt.svg 11 | :alt: fmt is continuously fuzzed att oss-fuzz 12 | :target: https://bugs.chromium.org/p/oss-fuzz/issues/list?colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20Summary&q=proj%3Dlibfmt&can=1 13 | 14 | .. image:: https://img.shields.io/badge/stackoverflow-fmt-blue.svg 15 | :alt: Ask questions at StackOverflow with the tag fmt 16 | :target: http://stackoverflow.com/questions/tagged/fmt 17 | 18 | **{fmt}** is an open-source formatting library for C++. 19 | It can be used as a safe and fast alternative to (s)printf and iostreams. 20 | 21 | `Documentation `__ 22 | 23 | Q&A: ask questions on `StackOverflow with the tag fmt `_. 24 | 25 | Features 26 | -------- 27 | 28 | * Replacement-based `format API `_ with 29 | positional arguments for localization. 30 | * `Format string syntax `_ similar to the one 31 | of `str.format `_ 32 | in Python. 33 | * Safe `printf implementation 34 | `_ including 35 | the POSIX extension for positional arguments. 36 | * Implementation of `C++20 std::format `__. 37 | * Support for user-defined types. 38 | * High performance: faster than common standard library implementations of 39 | `printf `_ and 40 | iostreams. See `Speed tests`_ and `Fast integer to string conversion in C++ 41 | `_. 42 | * Small code size both in terms of source code (the minimum configuration 43 | consists of just three header files, ``core.h``, ``format.h`` and 44 | ``format-inl.h``) and compiled code. See `Compile time and code bloat`_. 45 | * Reliability: the library has an extensive set of `unit tests 46 | `_ and is continuously fuzzed. 47 | * Safety: the library is fully type safe, errors in format strings can be 48 | reported at compile time, automatic memory management prevents buffer overflow 49 | errors. 50 | * Ease of use: small self-contained code base, no external dependencies, 51 | permissive MIT `license 52 | `_ 53 | * `Portability `_ with 54 | consistent output across platforms and support for older compilers. 55 | * Clean warning-free codebase even on high warning levels 56 | (``-Wall -Wextra -pedantic``). 57 | * Support for wide strings. 58 | * Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro. 59 | 60 | See the `documentation `_ for more details. 61 | 62 | Examples 63 | -------- 64 | 65 | Print ``Hello, world!`` to ``stdout``: 66 | 67 | .. code:: c++ 68 | 69 | fmt::print("Hello, {}!", "world"); // Python-like format string syntax 70 | fmt::printf("Hello, %s!", "world"); // printf format string syntax 71 | 72 | Format a string and use positional arguments: 73 | 74 | .. code:: c++ 75 | 76 | std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy"); 77 | // s == "I'd rather be happy than right." 78 | 79 | Check a format string at compile time: 80 | 81 | .. code:: c++ 82 | 83 | // test.cc 84 | #include 85 | std::string s = format(FMT_STRING("{2}"), 42); 86 | 87 | .. code:: 88 | 89 | $ c++ -Iinclude -std=c++14 test.cc 90 | ... 91 | test.cc:4:17: note: in instantiation of function template specialization 'fmt::v5::format' requested here 92 | std::string s = format(FMT_STRING("{2}"), 42); 93 | ^ 94 | include/fmt/core.h:778:19: note: non-constexpr function 'on_error' cannot be used in a constant expression 95 | ErrorHandler::on_error(message); 96 | ^ 97 | include/fmt/format.h:2226:16: note: in call to '&checker.context_->on_error(&"argument index out of range"[0])' 98 | context_.on_error("argument index out of range"); 99 | ^ 100 | 101 | Use {fmt} as a safe portable replacement for ``itoa`` 102 | (`godbolt `_): 103 | 104 | .. code:: c++ 105 | 106 | fmt::memory_buffer buf; 107 | format_to(buf, "{}", 42); // replaces itoa(42, buffer, 10) 108 | format_to(buf, "{:x}", 42); // replaces itoa(42, buffer, 16) 109 | // access the string with to_string(buf) or buf.data() 110 | 111 | Format objects of user-defined types via a simple `extension API 112 | `_: 113 | 114 | .. code:: c++ 115 | 116 | #include "fmt/format.h" 117 | 118 | struct date { 119 | int year, month, day; 120 | }; 121 | 122 | template <> 123 | struct fmt::formatter { 124 | constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } 125 | 126 | template 127 | auto format(const date& d, FormatContext& ctx) { 128 | return format_to(ctx.out(), "{}-{}-{}", d.year, d.month, d.day); 129 | } 130 | }; 131 | 132 | std::string s = fmt::format("The date is {}", date{2012, 12, 9}); 133 | // s == "The date is 2012-12-9" 134 | 135 | Create your own functions similar to `format 136 | `_ and 137 | `print `_ 138 | which take arbitrary arguments (`godbolt `_): 139 | 140 | .. code:: c++ 141 | 142 | // Prints formatted error message. 143 | void vreport_error(const char* format, fmt::format_args args) { 144 | fmt::print("Error: "); 145 | fmt::vprint(format, args); 146 | } 147 | template 148 | void report_error(const char* format, const Args & ... args) { 149 | vreport_error(format, fmt::make_format_args(args...)); 150 | } 151 | 152 | report_error("file not found: {}", path); 153 | 154 | Note that ``vreport_error`` is not parameterized on argument types which can 155 | improve compile times and reduce code size compared to a fully parameterized 156 | version. 157 | 158 | Benchmarks 159 | ---------- 160 | 161 | Speed tests 162 | ~~~~~~~~~~~ 163 | 164 | ================= ============= =========== 165 | Library Method Run Time, s 166 | ================= ============= =========== 167 | libc printf 1.03 168 | libc++ std::ostream 2.98 169 | {fmt} 4de41a fmt::print 0.76 170 | Boost Format 1.67 boost::format 7.24 171 | Folly Format folly::format 2.23 172 | ================= ============= =========== 173 | 174 | {fmt} is the fastest of the benchmarked methods, ~35% faster than ``printf``. 175 | 176 | The above results were generated by building ``tinyformat_test.cpp`` on macOS 177 | 10.14.3 with ``clang++ -O3 -DSPEED_TEST -DHAVE_FORMAT``, and taking the best of 178 | three runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"`` 179 | or equivalent is filled 2,000,000 times with output sent to ``/dev/null``; for 180 | further details refer to the `source 181 | `_. 182 | 183 | {fmt} is 10x faster than ``std::ostringstream`` and ``sprintf`` on floating-point 184 | formatting (`dtoa-benchmark `_) 185 | and as fast as `double-conversion `_: 186 | 187 | .. image:: https://user-images.githubusercontent.com/576385/69767160-cdaca400-112f-11ea-9fc5-347c9f83caad.png 188 | :target: https://fmt.dev/unknown_mac64_clang10.0.html 189 | 190 | Compile time and code bloat 191 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 192 | 193 | The script `bloat-test.py 194 | `_ 195 | from `format-benchmark `_ 196 | tests compile time and code bloat for nontrivial projects. 197 | It generates 100 translation units and uses ``printf()`` or its alternative 198 | five times in each to simulate a medium sized project. The resulting 199 | executable size and compile time (Apple LLVM version 8.1.0 (clang-802.0.42), 200 | macOS Sierra, best of three) is shown in the following tables. 201 | 202 | **Optimized build (-O3)** 203 | 204 | ============= =============== ==================== ================== 205 | Method Compile Time, s Executable size, KiB Stripped size, KiB 206 | ============= =============== ==================== ================== 207 | printf 2.6 29 26 208 | printf+string 16.4 29 26 209 | iostreams 31.1 59 55 210 | {fmt} 19.0 37 34 211 | Boost Format 91.9 226 203 212 | Folly Format 115.7 101 88 213 | ============= =============== ==================== ================== 214 | 215 | As you can see, {fmt} has 60% less overhead in terms of resulting binary code 216 | size compared to iostreams and comes pretty close to ``printf``. Boost Format 217 | and Folly Format have the largest overheads. 218 | 219 | ``printf+string`` is the same as ``printf`` but with extra ```` 220 | include to measure the overhead of the latter. 221 | 222 | **Non-optimized build** 223 | 224 | ============= =============== ==================== ================== 225 | Method Compile Time, s Executable size, KiB Stripped size, KiB 226 | ============= =============== ==================== ================== 227 | printf 2.2 33 30 228 | printf+string 16.0 33 30 229 | iostreams 28.3 56 52 230 | {fmt} 18.2 59 50 231 | Boost Format 54.1 365 303 232 | Folly Format 79.9 445 430 233 | ============= =============== ==================== ================== 234 | 235 | ``libc``, ``lib(std)c++`` and ``libfmt`` are all linked as shared libraries to 236 | compare formatting function overhead only. Boost Format is a 237 | header-only library so it doesn't provide any linkage options. 238 | 239 | Running the tests 240 | ~~~~~~~~~~~~~~~~~ 241 | 242 | Please refer to `Building the library`__ for the instructions on how to build 243 | the library and run the unit tests. 244 | 245 | __ https://fmt.dev/latest/usage.html#building-the-library 246 | 247 | Benchmarks reside in a separate repository, 248 | `format-benchmarks `_, 249 | so to run the benchmarks you first need to clone this repository and 250 | generate Makefiles with CMake:: 251 | 252 | $ git clone --recursive https://github.com/fmtlib/format-benchmark.git 253 | $ cd format-benchmark 254 | $ cmake . 255 | 256 | Then you can run the speed test:: 257 | 258 | $ make speed-test 259 | 260 | or the bloat test:: 261 | 262 | $ make bloat-test 263 | 264 | Projects using this library 265 | --------------------------- 266 | 267 | * `0 A.D. `_: A free, open-source, cross-platform real-time 268 | strategy game 269 | 270 | * `AMPL/MP `_: 271 | An open-source library for mathematical programming 272 | 273 | * `AvioBook `_: A comprehensive aircraft 274 | operations suite 275 | 276 | * `Celestia `_: Real-time 3D visualization of space 277 | 278 | * `Ceph `_: A scalable distributed storage system 279 | 280 | * `ccache `_: A compiler cache 281 | 282 | * `CUAUV `_: Cornell University's autonomous underwater 283 | vehicle 284 | 285 | * `HarpyWar/pvpgn `_: 286 | Player vs Player Gaming Network with tweaks 287 | 288 | * `KBEngine `_: An open-source MMOG server engine 289 | 290 | * `Keypirinha `_: A semantic launcher for Windows 291 | 292 | * `Kodi `_ (formerly xbmc): Home theater software 293 | 294 | * `Lifeline `_: A 2D game 295 | 296 | * `Drake `_: A planning, control, and analysis toolbox 297 | for nonlinear dynamical systems (MIT) 298 | 299 | * `Envoy `_: C++ L7 proxy and communication bus 300 | (Lyft) 301 | 302 | * `FiveM `_: a modification framework for GTA V 303 | 304 | * `MongoDB `_: Distributed document database 305 | 306 | * `MongoDB Smasher `_: A small tool to 307 | generate randomized datasets 308 | 309 | * `OpenSpace `_: An open-source astrovisualization 310 | framework 311 | 312 | * `PenUltima Online (POL) `_: 313 | An MMO server, compatible with most Ultima Online clients 314 | 315 | * `quasardb `_: A distributed, high-performance, 316 | associative database 317 | 318 | * `readpe `_: Read Portable Executable 319 | 320 | * `redis-cerberus `_: A Redis cluster 321 | proxy 322 | 323 | * `rpclib `_: A modern C++ msgpack-RPC server and client 324 | library 325 | 326 | * `Saddy `_: 327 | Small crossplatform 2D graphic engine 328 | 329 | * `Salesforce Analytics Cloud `_: 330 | Business intelligence software 331 | 332 | * `Scylla `_: A Cassandra-compatible NoSQL data store 333 | that can handle 1 million transactions per second on a single server 334 | 335 | * `Seastar `_: An advanced, open-source C++ 336 | framework for high-performance server applications on modern hardware 337 | 338 | * `spdlog `_: Super fast C++ logging library 339 | 340 | * `Stellar `_: Financial platform 341 | 342 | * `Touch Surgery `_: Surgery simulator 343 | 344 | * `TrinityCore `_: Open-source 345 | MMORPG framework 346 | 347 | `More... `_ 348 | 349 | If you are aware of other projects using this library, please let me know 350 | by `email `_ or by submitting an 351 | `issue `_. 352 | 353 | Motivation 354 | ---------- 355 | 356 | So why yet another formatting library? 357 | 358 | There are plenty of methods for doing this task, from standard ones like 359 | the printf family of function and iostreams to Boost Format and FastFormat 360 | libraries. The reason for creating a new library is that every existing 361 | solution that I found either had serious issues or didn't provide 362 | all the features I needed. 363 | 364 | printf 365 | ~~~~~~ 366 | 367 | The good thing about ``printf`` is that it is pretty fast and readily available 368 | being a part of the C standard library. The main drawback is that it 369 | doesn't support user-defined types. ``printf`` also has safety issues although 370 | they are somewhat mitigated with `__attribute__ ((format (printf, ...)) 371 | `_ in GCC. 372 | There is a POSIX extension that adds positional arguments required for 373 | `i18n `_ 374 | to ``printf`` but it is not a part of C99 and may not be available on some 375 | platforms. 376 | 377 | iostreams 378 | ~~~~~~~~~ 379 | 380 | The main issue with iostreams is best illustrated with an example: 381 | 382 | .. code:: c++ 383 | 384 | std::cout << std::setprecision(2) << std::fixed << 1.23456 << "\n"; 385 | 386 | which is a lot of typing compared to printf: 387 | 388 | .. code:: c++ 389 | 390 | printf("%.2f\n", 1.23456); 391 | 392 | Matthew Wilson, the author of FastFormat, called this "chevron hell". iostreams 393 | don't support positional arguments by design. 394 | 395 | The good part is that iostreams support user-defined types and are safe although 396 | error handling is awkward. 397 | 398 | Boost Format 399 | ~~~~~~~~~~~~ 400 | 401 | This is a very powerful library which supports both ``printf``-like format 402 | strings and positional arguments. Its main drawback is performance. According to 403 | various benchmarks it is much slower than other methods considered here. Boost 404 | Format also has excessive build times and severe code bloat issues (see 405 | `Benchmarks`_). 406 | 407 | FastFormat 408 | ~~~~~~~~~~ 409 | 410 | This is an interesting library which is fast, safe and has positional 411 | arguments. However it has significant limitations, citing its author: 412 | 413 | Three features that have no hope of being accommodated within the 414 | current design are: 415 | 416 | * Leading zeros (or any other non-space padding) 417 | * Octal/hexadecimal encoding 418 | * Runtime width/alignment specification 419 | 420 | It is also quite big and has a heavy dependency, STLSoft, which might be 421 | too restrictive for using it in some projects. 422 | 423 | Boost Spirit.Karma 424 | ~~~~~~~~~~~~~~~~~~ 425 | 426 | This is not really a formatting library but I decided to include it here for 427 | completeness. As iostreams, it suffers from the problem of mixing verbatim text 428 | with arguments. The library is pretty fast, but slower on integer formatting 429 | than ``fmt::format_int`` on Karma's own benchmark, 430 | see `Fast integer to string conversion in C++ 431 | `_. 432 | 433 | FAQ 434 | --- 435 | 436 | Q: how can I capture formatting arguments and format them later? 437 | 438 | A: use ``std::tuple``: 439 | 440 | .. code:: c++ 441 | 442 | template 443 | auto capture(const Args&... args) { 444 | return std::make_tuple(args...); 445 | } 446 | 447 | auto print_message = [](const auto&... args) { 448 | fmt::print(args...); 449 | }; 450 | 451 | // Capture and store arguments: 452 | auto args = capture("{} {}", 42, "foo"); 453 | // Do formatting: 454 | std::apply(print_message, args); 455 | 456 | License 457 | ------- 458 | 459 | {fmt} is distributed under the MIT `license 460 | `_. 461 | 462 | The `Format String Syntax 463 | `_ 464 | section in the documentation is based on the one from Python `string module 465 | documentation `_ 466 | adapted for the current library. For this reason the documentation is 467 | distributed under the Python Software Foundation license available in 468 | `doc/python-license.txt 469 | `_. 470 | It only applies if you distribute the documentation of fmt. 471 | 472 | Acknowledgments 473 | --------------- 474 | 475 | The {fmt} library is maintained by Victor Zverovich (`vitaut 476 | `_) and Jonathan Müller (`foonathan 477 | `_) with contributions from many other people. 478 | See `Contributors `_ and 479 | `Releases `_ for some of the names. 480 | Let us know if your contribution is not listed or mentioned incorrectly and 481 | we'll make it right. 482 | 483 | The benchmark section of this readme file and the performance tests are taken 484 | from the excellent `tinyformat `_ library 485 | written by Chris Foster. Boost Format library is acknowledged transitively 486 | since it had some influence on tinyformat. 487 | Some ideas used in the implementation are borrowed from `Loki 488 | `_ SafeFormat and `Diagnostic API 489 | `_ in 490 | `Clang `_. 491 | Format string syntax and the documentation are based on Python's `str.format 492 | `_. 493 | Thanks `Doug Turnbull `_ for his valuable 494 | comments and contribution to the design of the type-safe API and 495 | `Gregory Czajkowski `_ for implementing binary 496 | formatting. Thanks `Ruslan Baratov `_ for comprehensive 497 | `comparison of integer formatting algorithms `_ 498 | and useful comments regarding performance, `Boris Kaul `_ for 499 | `C++ counting digits benchmark `_. 500 | Thanks to `CarterLi `_ for contributing various 501 | improvements to the code. 502 | -------------------------------------------------------------------------------- /ext/fmt/include/fmt/locale.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - std::locale support 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_LOCALE_H_ 9 | #define FMT_LOCALE_H_ 10 | 11 | #include 12 | #include "format.h" 13 | 14 | FMT_BEGIN_NAMESPACE 15 | 16 | namespace internal { 17 | template 18 | typename buffer_context::iterator vformat_to( 19 | const std::locale& loc, buffer& buf, 20 | basic_string_view format_str, 21 | basic_format_args> args) { 22 | using range = buffer_range; 23 | return vformat_to>(buf, to_string_view(format_str), args, 24 | internal::locale_ref(loc)); 25 | } 26 | 27 | template 28 | std::basic_string vformat(const std::locale& loc, 29 | basic_string_view format_str, 30 | basic_format_args> args) { 31 | basic_memory_buffer buffer; 32 | internal::vformat_to(loc, buffer, format_str, args); 33 | return fmt::to_string(buffer); 34 | } 35 | } // namespace internal 36 | 37 | template > 38 | inline std::basic_string vformat( 39 | const std::locale& loc, const S& format_str, 40 | basic_format_args> args) { 41 | return internal::vformat(loc, to_string_view(format_str), args); 42 | } 43 | 44 | template > 45 | inline std::basic_string format(const std::locale& loc, 46 | const S& format_str, Args&&... args) { 47 | return internal::vformat( 48 | loc, to_string_view(format_str), 49 | {internal::make_args_checked(format_str, args...)}); 50 | } 51 | 52 | template ::value, char_t>> 55 | inline OutputIt vformat_to(OutputIt out, const std::locale& loc, 56 | const S& format_str, 57 | format_args_t args) { 58 | using range = internal::output_range; 59 | return vformat_to>( 60 | range(out), to_string_view(format_str), args, internal::locale_ref(loc)); 61 | } 62 | 63 | template ::value&& 65 | internal::is_string::value)> 66 | inline OutputIt format_to(OutputIt out, const std::locale& loc, 67 | const S& format_str, Args&&... args) { 68 | internal::check_format_string(format_str); 69 | using context = format_context_t>; 70 | format_arg_store as{args...}; 71 | return vformat_to(out, loc, to_string_view(format_str), 72 | basic_format_args(as)); 73 | } 74 | 75 | FMT_END_NAMESPACE 76 | 77 | #endif // FMT_LOCALE_H_ 78 | -------------------------------------------------------------------------------- /ext/fmt/include/fmt/ostream.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - std::ostream support 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_OSTREAM_H_ 9 | #define FMT_OSTREAM_H_ 10 | 11 | #include 12 | #include "format.h" 13 | 14 | FMT_BEGIN_NAMESPACE 15 | namespace internal { 16 | 17 | template class formatbuf : public std::basic_streambuf { 18 | private: 19 | using int_type = typename std::basic_streambuf::int_type; 20 | using traits_type = typename std::basic_streambuf::traits_type; 21 | 22 | buffer& buffer_; 23 | 24 | public: 25 | formatbuf(buffer& buf) : buffer_(buf) {} 26 | 27 | protected: 28 | // The put-area is actually always empty. This makes the implementation 29 | // simpler and has the advantage that the streambuf and the buffer are always 30 | // in sync and sputc never writes into uninitialized memory. The obvious 31 | // disadvantage is that each call to sputc always results in a (virtual) call 32 | // to overflow. There is no disadvantage here for sputn since this always 33 | // results in a call to xsputn. 34 | 35 | int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE { 36 | if (!traits_type::eq_int_type(ch, traits_type::eof())) 37 | buffer_.push_back(static_cast(ch)); 38 | return ch; 39 | } 40 | 41 | std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE { 42 | buffer_.append(s, s + count); 43 | return count; 44 | } 45 | }; 46 | 47 | template struct test_stream : std::basic_ostream { 48 | private: 49 | // Hide all operator<< from std::basic_ostream. 50 | void_t<> operator<<(null<>); 51 | void_t<> operator<<(const Char*); 52 | 53 | template ::value && 54 | !std::is_enum::value)> 55 | void_t<> operator<<(T); 56 | }; 57 | 58 | // Checks if T has a user-defined operator<< (e.g. not a member of 59 | // std::ostream). 60 | template class is_streamable { 61 | private: 62 | template 63 | static bool_constant&>() 64 | << std::declval()), 65 | void_t<>>::value> 66 | test(int); 67 | 68 | template static std::false_type test(...); 69 | 70 | using result = decltype(test(0)); 71 | 72 | public: 73 | static const bool value = result::value; 74 | }; 75 | 76 | // Write the content of buf to os. 77 | template 78 | void write(std::basic_ostream& os, buffer& buf) { 79 | const Char* buf_data = buf.data(); 80 | using unsigned_streamsize = std::make_unsigned::type; 81 | unsigned_streamsize size = buf.size(); 82 | unsigned_streamsize max_size = to_unsigned(max_value()); 83 | do { 84 | unsigned_streamsize n = size <= max_size ? size : max_size; 85 | os.write(buf_data, static_cast(n)); 86 | buf_data += n; 87 | size -= n; 88 | } while (size != 0); 89 | } 90 | 91 | template 92 | void format_value(buffer& buf, const T& value, 93 | locale_ref loc = locale_ref()) { 94 | formatbuf format_buf(buf); 95 | std::basic_ostream output(&format_buf); 96 | if (loc) output.imbue(loc.get()); 97 | output.exceptions(std::ios_base::failbit | std::ios_base::badbit); 98 | output << value; 99 | buf.resize(buf.size()); 100 | } 101 | 102 | // Formats an object of type T that has an overloaded ostream operator<<. 103 | template 104 | struct fallback_formatter::value>> 105 | : formatter, Char> { 106 | template 107 | auto format(const T& value, Context& ctx) -> decltype(ctx.out()) { 108 | basic_memory_buffer buffer; 109 | format_value(buffer, value, ctx.locale()); 110 | basic_string_view str(buffer.data(), buffer.size()); 111 | return formatter, Char>::format(str, ctx); 112 | } 113 | }; 114 | } // namespace internal 115 | 116 | template 117 | void vprint(std::basic_ostream& os, basic_string_view format_str, 118 | basic_format_args> args) { 119 | basic_memory_buffer buffer; 120 | internal::vformat_to(buffer, format_str, args); 121 | internal::write(os, buffer); 122 | } 123 | 124 | /** 125 | \rst 126 | Prints formatted data to the stream *os*. 127 | 128 | **Example**:: 129 | 130 | fmt::print(cerr, "Don't {}!", "panic"); 131 | \endrst 132 | */ 133 | template ::value, char_t>> 135 | void print(std::basic_ostream& os, const S& format_str, Args&&... args) { 136 | vprint(os, to_string_view(format_str), 137 | {internal::make_args_checked(format_str, args...)}); 138 | } 139 | FMT_END_NAMESPACE 140 | 141 | #endif // FMT_OSTREAM_H_ 142 | -------------------------------------------------------------------------------- /ext/fmt/include/fmt/posix.h: -------------------------------------------------------------------------------- 1 | // A C++ interface to POSIX functions. 2 | // 3 | // Copyright (c) 2012 - 2016, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_POSIX_H_ 9 | #define FMT_POSIX_H_ 10 | 11 | #if defined(__MINGW32__) || defined(__CYGWIN__) 12 | // Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/. 13 | # undef __STRICT_ANSI__ 14 | #endif 15 | 16 | #include 17 | #include // for locale_t 18 | #include 19 | #include // for strtod_l 20 | 21 | #include 22 | 23 | #if defined __APPLE__ || defined(__FreeBSD__) 24 | # include // for LC_NUMERIC_MASK on OS X 25 | #endif 26 | 27 | #include "format.h" 28 | 29 | // UWP doesn't provide _pipe. 30 | #if FMT_HAS_INCLUDE("winapifamily.h") 31 | # include 32 | #endif 33 | #if FMT_HAS_INCLUDE("fcntl.h") && \ 34 | (!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) 35 | # include // for O_RDONLY 36 | # define FMT_USE_FCNTL 1 37 | #else 38 | # define FMT_USE_FCNTL 0 39 | #endif 40 | 41 | #ifndef FMT_POSIX 42 | # if defined(_WIN32) && !defined(__MINGW32__) 43 | // Fix warnings about deprecated symbols. 44 | # define FMT_POSIX(call) _##call 45 | # else 46 | # define FMT_POSIX(call) call 47 | # endif 48 | #endif 49 | 50 | // Calls to system functions are wrapped in FMT_SYSTEM for testability. 51 | #ifdef FMT_SYSTEM 52 | # define FMT_POSIX_CALL(call) FMT_SYSTEM(call) 53 | #else 54 | # define FMT_SYSTEM(call) call 55 | # ifdef _WIN32 56 | // Fix warnings about deprecated symbols. 57 | # define FMT_POSIX_CALL(call) ::_##call 58 | # else 59 | # define FMT_POSIX_CALL(call) ::call 60 | # endif 61 | #endif 62 | 63 | // Retries the expression while it evaluates to error_result and errno 64 | // equals to EINTR. 65 | #ifndef _WIN32 66 | # define FMT_RETRY_VAL(result, expression, error_result) \ 67 | do { \ 68 | (result) = (expression); \ 69 | } while ((result) == (error_result) && errno == EINTR) 70 | #else 71 | # define FMT_RETRY_VAL(result, expression, error_result) result = (expression) 72 | #endif 73 | 74 | #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) 75 | 76 | FMT_BEGIN_NAMESPACE 77 | 78 | /** 79 | \rst 80 | A reference to a null-terminated string. It can be constructed from a C 81 | string or ``std::string``. 82 | 83 | You can use one of the following type aliases for common character types: 84 | 85 | +---------------+-----------------------------+ 86 | | Type | Definition | 87 | +===============+=============================+ 88 | | cstring_view | basic_cstring_view | 89 | +---------------+-----------------------------+ 90 | | wcstring_view | basic_cstring_view | 91 | +---------------+-----------------------------+ 92 | 93 | This class is most useful as a parameter type to allow passing 94 | different types of strings to a function, for example:: 95 | 96 | template 97 | std::string format(cstring_view format_str, const Args & ... args); 98 | 99 | format("{}", 42); 100 | format(std::string("{}"), 42); 101 | \endrst 102 | */ 103 | template class basic_cstring_view { 104 | private: 105 | const Char* data_; 106 | 107 | public: 108 | /** Constructs a string reference object from a C string. */ 109 | basic_cstring_view(const Char* s) : data_(s) {} 110 | 111 | /** 112 | \rst 113 | Constructs a string reference from an ``std::string`` object. 114 | \endrst 115 | */ 116 | basic_cstring_view(const std::basic_string& s) : data_(s.c_str()) {} 117 | 118 | /** Returns the pointer to a C string. */ 119 | const Char* c_str() const { return data_; } 120 | }; 121 | 122 | using cstring_view = basic_cstring_view; 123 | using wcstring_view = basic_cstring_view; 124 | 125 | // An error code. 126 | class error_code { 127 | private: 128 | int value_; 129 | 130 | public: 131 | explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {} 132 | 133 | int get() const FMT_NOEXCEPT { return value_; } 134 | }; 135 | 136 | // A buffered file. 137 | class buffered_file { 138 | private: 139 | FILE* file_; 140 | 141 | friend class file; 142 | 143 | explicit buffered_file(FILE* f) : file_(f) {} 144 | 145 | public: 146 | buffered_file(const buffered_file&) = delete; 147 | void operator=(const buffered_file&) = delete; 148 | 149 | // Constructs a buffered_file object which doesn't represent any file. 150 | buffered_file() FMT_NOEXCEPT : file_(nullptr) {} 151 | 152 | // Destroys the object closing the file it represents if any. 153 | FMT_API ~buffered_file() FMT_NOEXCEPT; 154 | 155 | public: 156 | buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) { 157 | other.file_ = nullptr; 158 | } 159 | 160 | buffered_file& operator=(buffered_file&& other) { 161 | close(); 162 | file_ = other.file_; 163 | other.file_ = nullptr; 164 | return *this; 165 | } 166 | 167 | // Opens a file. 168 | FMT_API buffered_file(cstring_view filename, cstring_view mode); 169 | 170 | // Closes the file. 171 | FMT_API void close(); 172 | 173 | // Returns the pointer to a FILE object representing this file. 174 | FILE* get() const FMT_NOEXCEPT { return file_; } 175 | 176 | // We place parentheses around fileno to workaround a bug in some versions 177 | // of MinGW that define fileno as a macro. 178 | FMT_API int(fileno)() const; 179 | 180 | void vprint(string_view format_str, format_args args) { 181 | fmt::vprint(file_, format_str, args); 182 | } 183 | 184 | template 185 | inline void print(string_view format_str, const Args&... args) { 186 | vprint(format_str, make_format_args(args...)); 187 | } 188 | }; 189 | 190 | #if FMT_USE_FCNTL 191 | // A file. Closed file is represented by a file object with descriptor -1. 192 | // Methods that are not declared with FMT_NOEXCEPT may throw 193 | // fmt::system_error in case of failure. Note that some errors such as 194 | // closing the file multiple times will cause a crash on Windows rather 195 | // than an exception. You can get standard behavior by overriding the 196 | // invalid parameter handler with _set_invalid_parameter_handler. 197 | class file { 198 | private: 199 | int fd_; // File descriptor. 200 | 201 | // Constructs a file object with a given descriptor. 202 | explicit file(int fd) : fd_(fd) {} 203 | 204 | public: 205 | // Possible values for the oflag argument to the constructor. 206 | enum { 207 | RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only. 208 | WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only. 209 | RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing. 210 | }; 211 | 212 | // Constructs a file object which doesn't represent any file. 213 | file() FMT_NOEXCEPT : fd_(-1) {} 214 | 215 | // Opens a file and constructs a file object representing this file. 216 | FMT_API file(cstring_view path, int oflag); 217 | 218 | public: 219 | file(const file&) = delete; 220 | void operator=(const file&) = delete; 221 | 222 | file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; } 223 | 224 | file& operator=(file&& other) FMT_NOEXCEPT { 225 | close(); 226 | fd_ = other.fd_; 227 | other.fd_ = -1; 228 | return *this; 229 | } 230 | 231 | // Destroys the object closing the file it represents if any. 232 | FMT_API ~file() FMT_NOEXCEPT; 233 | 234 | // Returns the file descriptor. 235 | int descriptor() const FMT_NOEXCEPT { return fd_; } 236 | 237 | // Closes the file. 238 | FMT_API void close(); 239 | 240 | // Returns the file size. The size has signed type for consistency with 241 | // stat::st_size. 242 | FMT_API long long size() const; 243 | 244 | // Attempts to read count bytes from the file into the specified buffer. 245 | FMT_API std::size_t read(void* buffer, std::size_t count); 246 | 247 | // Attempts to write count bytes from the specified buffer to the file. 248 | FMT_API std::size_t write(const void* buffer, std::size_t count); 249 | 250 | // Duplicates a file descriptor with the dup function and returns 251 | // the duplicate as a file object. 252 | FMT_API static file dup(int fd); 253 | 254 | // Makes fd be the copy of this file descriptor, closing fd first if 255 | // necessary. 256 | FMT_API void dup2(int fd); 257 | 258 | // Makes fd be the copy of this file descriptor, closing fd first if 259 | // necessary. 260 | FMT_API void dup2(int fd, error_code& ec) FMT_NOEXCEPT; 261 | 262 | // Creates a pipe setting up read_end and write_end file objects for reading 263 | // and writing respectively. 264 | FMT_API static void pipe(file& read_end, file& write_end); 265 | 266 | // Creates a buffered_file object associated with this file and detaches 267 | // this file object from the file. 268 | FMT_API buffered_file fdopen(const char* mode); 269 | }; 270 | 271 | // Returns the memory page size. 272 | long getpagesize(); 273 | #endif // FMT_USE_FCNTL 274 | 275 | #ifdef FMT_LOCALE 276 | // A "C" numeric locale. 277 | class Locale { 278 | private: 279 | # ifdef _WIN32 280 | using locale_t = _locale_t; 281 | 282 | enum { LC_NUMERIC_MASK = LC_NUMERIC }; 283 | 284 | static locale_t newlocale(int category_mask, const char* locale, locale_t) { 285 | return _create_locale(category_mask, locale); 286 | } 287 | 288 | static void freelocale(locale_t locale) { _free_locale(locale); } 289 | 290 | static double strtod_l(const char* nptr, char** endptr, _locale_t locale) { 291 | return _strtod_l(nptr, endptr, locale); 292 | } 293 | # endif 294 | 295 | locale_t locale_; 296 | 297 | public: 298 | using type = locale_t; 299 | Locale(const Locale&) = delete; 300 | void operator=(const Locale&) = delete; 301 | 302 | Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", nullptr)) { 303 | if (!locale_) FMT_THROW(system_error(errno, "cannot create locale")); 304 | } 305 | ~Locale() { freelocale(locale_); } 306 | 307 | type get() const { return locale_; } 308 | 309 | // Converts string to floating-point number and advances str past the end 310 | // of the parsed input. 311 | double strtod(const char*& str) const { 312 | char* end = nullptr; 313 | double result = strtod_l(str, &end, locale_); 314 | str = end; 315 | return result; 316 | } 317 | }; 318 | #endif // FMT_LOCALE 319 | FMT_END_NAMESPACE 320 | 321 | #endif // FMT_POSIX_H_ 322 | -------------------------------------------------------------------------------- /ext/fmt/include/fmt/ranges.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - experimental range support 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | // 8 | // Copyright (c) 2018 - present, Remotion (Igor Schulz) 9 | // All Rights Reserved 10 | // {fmt} support for ranges, containers and types tuple interface. 11 | 12 | #ifndef FMT_RANGES_H_ 13 | #define FMT_RANGES_H_ 14 | 15 | #include 16 | #include "format.h" 17 | 18 | // output only up to N items from the range. 19 | #ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT 20 | # define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256 21 | #endif 22 | 23 | FMT_BEGIN_NAMESPACE 24 | 25 | template struct formatting_base { 26 | template 27 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 28 | return ctx.begin(); 29 | } 30 | }; 31 | 32 | template 33 | struct formatting_range : formatting_base { 34 | static FMT_CONSTEXPR_DECL const std::size_t range_length_limit = 35 | FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the 36 | // range. 37 | Char prefix; 38 | Char delimiter; 39 | Char postfix; 40 | formatting_range() : prefix('{'), delimiter(','), postfix('}') {} 41 | static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true; 42 | static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false; 43 | }; 44 | 45 | template 46 | struct formatting_tuple : formatting_base { 47 | Char prefix; 48 | Char delimiter; 49 | Char postfix; 50 | formatting_tuple() : prefix('('), delimiter(','), postfix(')') {} 51 | static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true; 52 | static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false; 53 | }; 54 | 55 | namespace internal { 56 | 57 | template 58 | OutputIterator copy(const RangeT& range, OutputIterator out) { 59 | for (auto it = range.begin(), end = range.end(); it != end; ++it) 60 | *out++ = *it; 61 | return out; 62 | } 63 | 64 | template 65 | OutputIterator copy(const char* str, OutputIterator out) { 66 | while (*str) *out++ = *str++; 67 | return out; 68 | } 69 | 70 | template 71 | OutputIterator copy(char ch, OutputIterator out) { 72 | *out++ = ch; 73 | return out; 74 | } 75 | 76 | /// Return true value if T has std::string interface, like std::string_view. 77 | template class is_like_std_string { 78 | template 79 | static auto check(U* p) 80 | -> decltype((void)p->find('a'), p->length(), (void)p->data(), int()); 81 | template static void check(...); 82 | 83 | public: 84 | static FMT_CONSTEXPR_DECL const bool value = 85 | is_string::value || !std::is_void(nullptr))>::value; 86 | }; 87 | 88 | template 89 | struct is_like_std_string> : std::true_type {}; 90 | 91 | template struct conditional_helper {}; 92 | 93 | template struct is_range_ : std::false_type {}; 94 | 95 | #if !FMT_MSC_VER || FMT_MSC_VER > 1800 96 | template 97 | struct is_range_< 98 | T, conditional_t().begin()), 100 | decltype(std::declval().end())>, 101 | void>> : std::true_type {}; 102 | #endif 103 | 104 | /// tuple_size and tuple_element check. 105 | template class is_tuple_like_ { 106 | template 107 | static auto check(U* p) 108 | -> decltype(std::tuple_size::value, 109 | (void)std::declval::type>(), 110 | int()); 111 | template static void check(...); 112 | 113 | public: 114 | static FMT_CONSTEXPR_DECL const bool value = 115 | !std::is_void(nullptr))>::value; 116 | }; 117 | 118 | // Check for integer_sequence 119 | #if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900 120 | template 121 | using integer_sequence = std::integer_sequence; 122 | template using index_sequence = std::index_sequence; 123 | template 124 | using make_index_sequence = std::make_index_sequence; 125 | #else 126 | template struct integer_sequence { 127 | using value_type = T; 128 | 129 | static FMT_CONSTEXPR std::size_t size() { return sizeof...(N); } 130 | }; 131 | 132 | template 133 | using index_sequence = integer_sequence; 134 | 135 | template 136 | struct make_integer_sequence : make_integer_sequence {}; 137 | template 138 | struct make_integer_sequence : integer_sequence {}; 139 | 140 | template 141 | using make_index_sequence = make_integer_sequence; 142 | #endif 143 | 144 | template 145 | void for_each(index_sequence, Tuple&& tup, F&& f) FMT_NOEXCEPT { 146 | using std::get; 147 | // using free function get(T) now. 148 | const int _[] = {0, ((void)f(get(tup)), 0)...}; 149 | (void)_; // blocks warnings 150 | } 151 | 152 | template 153 | FMT_CONSTEXPR make_index_sequence::value> get_indexes( 154 | T const&) { 155 | return {}; 156 | } 157 | 158 | template void for_each(Tuple&& tup, F&& f) { 159 | const auto indexes = get_indexes(tup); 160 | for_each(indexes, std::forward(tup), std::forward(f)); 161 | } 162 | 163 | template ::type>::value)> 165 | FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) { 166 | return add_space ? " {}" : "{}"; 167 | } 168 | 169 | template ::type>::value)> 171 | FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) { 172 | return add_space ? " \"{}\"" : "\"{}\""; 173 | } 174 | 175 | FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) { 176 | return add_space ? " \"{}\"" : "\"{}\""; 177 | } 178 | FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) { 179 | return add_space ? L" \"{}\"" : L"\"{}\""; 180 | } 181 | 182 | FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) { 183 | return add_space ? " '{}'" : "'{}'"; 184 | } 185 | FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) { 186 | return add_space ? L" '{}'" : L"'{}'"; 187 | } 188 | 189 | } // namespace internal 190 | 191 | template struct is_tuple_like { 192 | static FMT_CONSTEXPR_DECL const bool value = 193 | internal::is_tuple_like_::value && !internal::is_range_::value; 194 | }; 195 | 196 | template 197 | struct formatter::value>> { 198 | private: 199 | // C++11 generic lambda for format() 200 | template struct format_each { 201 | template void operator()(const T& v) { 202 | if (i > 0) { 203 | if (formatting.add_prepostfix_space) { 204 | *out++ = ' '; 205 | } 206 | out = internal::copy(formatting.delimiter, out); 207 | } 208 | out = format_to(out, 209 | internal::format_str_quoted( 210 | (formatting.add_delimiter_spaces && i > 0), v), 211 | v); 212 | ++i; 213 | } 214 | 215 | formatting_tuple& formatting; 216 | std::size_t& i; 217 | typename std::add_lvalue_reference().out())>::type out; 219 | }; 220 | 221 | public: 222 | formatting_tuple formatting; 223 | 224 | template 225 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 226 | return formatting.parse(ctx); 227 | } 228 | 229 | template 230 | auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) { 231 | auto out = ctx.out(); 232 | std::size_t i = 0; 233 | internal::copy(formatting.prefix, out); 234 | 235 | internal::for_each(values, format_each{formatting, i, out}); 236 | if (formatting.add_prepostfix_space) { 237 | *out++ = ' '; 238 | } 239 | internal::copy(formatting.postfix, out); 240 | 241 | return ctx.out(); 242 | } 243 | }; 244 | 245 | template struct is_range { 246 | static FMT_CONSTEXPR_DECL const bool value = 247 | internal::is_range_::value && 248 | !internal::is_like_std_string::value && 249 | !std::is_convertible>::value && 250 | !std::is_constructible, T>::value; 251 | }; 252 | 253 | template 254 | struct formatter::value>> { 256 | formatting_range formatting; 257 | 258 | template 259 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 260 | return formatting.parse(ctx); 261 | } 262 | 263 | template 264 | typename FormatContext::iterator format(const RangeT& values, 265 | FormatContext& ctx) { 266 | auto out = internal::copy(formatting.prefix, ctx.out()); 267 | std::size_t i = 0; 268 | for (auto it = values.begin(), end = values.end(); it != end; ++it) { 269 | if (i > 0) { 270 | if (formatting.add_prepostfix_space) *out++ = ' '; 271 | out = internal::copy(formatting.delimiter, out); 272 | } 273 | out = format_to(out, 274 | internal::format_str_quoted( 275 | (formatting.add_delimiter_spaces && i > 0), *it), 276 | *it); 277 | if (++i > formatting.range_length_limit) { 278 | out = format_to(out, " ... "); 279 | break; 280 | } 281 | } 282 | if (formatting.add_prepostfix_space) *out++ = ' '; 283 | return internal::copy(formatting.postfix, out); 284 | } 285 | }; 286 | 287 | template struct tuple_arg_join : internal::view { 288 | const std::tuple& tuple; 289 | basic_string_view sep; 290 | 291 | tuple_arg_join(const std::tuple& t, basic_string_view s) 292 | : tuple{t}, sep{s} {} 293 | }; 294 | 295 | template 296 | struct formatter, Char> { 297 | template 298 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 299 | return ctx.begin(); 300 | } 301 | 302 | template 303 | typename FormatContext::iterator format( 304 | const tuple_arg_join& value, FormatContext& ctx) { 305 | return format(value, ctx, internal::make_index_sequence{}); 306 | } 307 | 308 | private: 309 | template 310 | typename FormatContext::iterator format( 311 | const tuple_arg_join& value, FormatContext& ctx, 312 | internal::index_sequence) { 313 | return format_args(value, ctx, std::get(value.tuple)...); 314 | } 315 | 316 | template 317 | typename FormatContext::iterator format_args( 318 | const tuple_arg_join&, FormatContext& ctx) { 319 | // NOTE: for compilers that support C++17, this empty function instantiation 320 | // can be replaced with a constexpr branch in the variadic overload. 321 | return ctx.out(); 322 | } 323 | 324 | template 325 | typename FormatContext::iterator format_args( 326 | const tuple_arg_join& value, FormatContext& ctx, 327 | const Arg& arg, const Args&... args) { 328 | using base = formatter::type, Char>; 329 | auto out = ctx.out(); 330 | out = base{}.format(arg, ctx); 331 | if (sizeof...(Args) > 0) { 332 | out = std::copy(value.sep.begin(), value.sep.end(), out); 333 | ctx.advance_to(out); 334 | return format_args(value, ctx, args...); 335 | } 336 | return out; 337 | } 338 | }; 339 | 340 | /** 341 | \rst 342 | Returns an object that formats `tuple` with elements separated by `sep`. 343 | 344 | **Example**:: 345 | 346 | std::tuple t = {1, 'a'}; 347 | fmt::print("{}", fmt::join(t, ", ")); 348 | // Output: "1, a" 349 | \endrst 350 | */ 351 | template 352 | FMT_CONSTEXPR tuple_arg_join join(const std::tuple& tuple, 353 | string_view sep) { 354 | return {tuple, sep}; 355 | } 356 | 357 | template 358 | FMT_CONSTEXPR tuple_arg_join join(const std::tuple& tuple, 359 | wstring_view sep) { 360 | return {tuple, sep}; 361 | } 362 | 363 | FMT_END_NAMESPACE 364 | 365 | #endif // FMT_RANGES_H_ 366 | -------------------------------------------------------------------------------- /ext/fmt/src/format.cc: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ 2 | // 3 | // Copyright (c) 2012 - 2016, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #include "fmt/format-inl.h" 9 | 10 | FMT_BEGIN_NAMESPACE 11 | template struct FMT_API internal::basic_data; 12 | 13 | // Workaround a bug in MSVC2013 that prevents instantiation of format_float. 14 | int (*instantiate_format_float)(double, int, internal::float_specs, 15 | internal::buffer&) = 16 | internal::format_float; 17 | 18 | #ifndef FMT_STATIC_THOUSANDS_SEPARATOR 19 | template FMT_API internal::locale_ref::locale_ref(const std::locale& loc); 20 | template FMT_API std::locale internal::locale_ref::get() const; 21 | #endif 22 | 23 | // Explicit instantiations for char. 24 | 25 | template FMT_API std::string internal::grouping_impl(locale_ref); 26 | template FMT_API char internal::thousands_sep_impl(locale_ref); 27 | template FMT_API char internal::decimal_point_impl(locale_ref); 28 | 29 | template FMT_API void internal::buffer::append(const char*, const char*); 30 | 31 | template FMT_API void internal::arg_map::init( 32 | const basic_format_args& args); 33 | 34 | template FMT_API std::string internal::vformat( 35 | string_view, basic_format_args); 36 | 37 | template FMT_API format_context::iterator internal::vformat_to( 38 | internal::buffer&, string_view, basic_format_args); 39 | 40 | template FMT_API int internal::snprintf_float(double, int, 41 | internal::float_specs, 42 | internal::buffer&); 43 | template FMT_API int internal::snprintf_float(long double, int, 44 | internal::float_specs, 45 | internal::buffer&); 46 | template FMT_API int internal::format_float(double, int, internal::float_specs, 47 | internal::buffer&); 48 | template FMT_API int internal::format_float(long double, int, 49 | internal::float_specs, 50 | internal::buffer&); 51 | 52 | // Explicit instantiations for wchar_t. 53 | 54 | template FMT_API std::string internal::grouping_impl(locale_ref); 55 | template FMT_API wchar_t internal::thousands_sep_impl(locale_ref); 56 | template FMT_API wchar_t internal::decimal_point_impl(locale_ref); 57 | 58 | template FMT_API void internal::buffer::append(const wchar_t*, 59 | const wchar_t*); 60 | 61 | template FMT_API std::wstring internal::vformat( 62 | wstring_view, basic_format_args); 63 | FMT_END_NAMESPACE 64 | -------------------------------------------------------------------------------- /ext/fmt/src/posix.cc: -------------------------------------------------------------------------------- 1 | // A C++ interface to POSIX functions. 2 | // 3 | // Copyright (c) 2012 - 2016, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | // Disable bogus MSVC warnings. 9 | #if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER) 10 | # define _CRT_SECURE_NO_WARNINGS 11 | #endif 12 | 13 | #include "fmt/posix.h" 14 | 15 | #include 16 | 17 | #if FMT_USE_FCNTL 18 | #include 19 | #include 20 | 21 | #ifndef _WIN32 22 | # include 23 | #else 24 | # ifndef WIN32_LEAN_AND_MEAN 25 | # define WIN32_LEAN_AND_MEAN 26 | # endif 27 | # include 28 | # include 29 | 30 | # define O_CREAT _O_CREAT 31 | # define O_TRUNC _O_TRUNC 32 | 33 | # ifndef S_IRUSR 34 | # define S_IRUSR _S_IREAD 35 | # endif 36 | 37 | # ifndef S_IWUSR 38 | # define S_IWUSR _S_IWRITE 39 | # endif 40 | 41 | # ifdef __MINGW32__ 42 | # define _SH_DENYNO 0x40 43 | # endif 44 | #endif // _WIN32 45 | #endif // FMT_USE_FCNTL 46 | 47 | #ifdef fileno 48 | # undef fileno 49 | #endif 50 | 51 | namespace { 52 | #ifdef _WIN32 53 | // Return type of read and write functions. 54 | using RWResult = int; 55 | 56 | // On Windows the count argument to read and write is unsigned, so convert 57 | // it from size_t preventing integer overflow. 58 | inline unsigned convert_rwcount(std::size_t count) { 59 | return count <= UINT_MAX ? static_cast(count) : UINT_MAX; 60 | } 61 | #else 62 | // Return type of read and write functions. 63 | using RWResult = ssize_t; 64 | 65 | inline std::size_t convert_rwcount(std::size_t count) { return count; } 66 | #endif 67 | } // namespace 68 | 69 | FMT_BEGIN_NAMESPACE 70 | 71 | buffered_file::~buffered_file() FMT_NOEXCEPT { 72 | if (file_ && FMT_SYSTEM(fclose(file_)) != 0) 73 | report_system_error(errno, "cannot close file"); 74 | } 75 | 76 | buffered_file::buffered_file(cstring_view filename, cstring_view mode) { 77 | FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), 78 | nullptr); 79 | if (!file_) 80 | FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str())); 81 | } 82 | 83 | void buffered_file::close() { 84 | if (!file_) return; 85 | int result = FMT_SYSTEM(fclose(file_)); 86 | file_ = nullptr; 87 | if (result != 0) FMT_THROW(system_error(errno, "cannot close file")); 88 | } 89 | 90 | // A macro used to prevent expansion of fileno on broken versions of MinGW. 91 | #define FMT_ARGS 92 | 93 | int buffered_file::fileno() const { 94 | int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_)); 95 | if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor")); 96 | return fd; 97 | } 98 | 99 | #if FMT_USE_FCNTL 100 | file::file(cstring_view path, int oflag) { 101 | int mode = S_IRUSR | S_IWUSR; 102 | #if defined(_WIN32) && !defined(__MINGW32__) 103 | fd_ = -1; 104 | FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode)); 105 | #else 106 | FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode))); 107 | #endif 108 | if (fd_ == -1) 109 | FMT_THROW(system_error(errno, "cannot open file {}", path.c_str())); 110 | } 111 | 112 | file::~file() FMT_NOEXCEPT { 113 | // Don't retry close in case of EINTR! 114 | // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html 115 | if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0) 116 | report_system_error(errno, "cannot close file"); 117 | } 118 | 119 | void file::close() { 120 | if (fd_ == -1) return; 121 | // Don't retry close in case of EINTR! 122 | // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html 123 | int result = FMT_POSIX_CALL(close(fd_)); 124 | fd_ = -1; 125 | if (result != 0) FMT_THROW(system_error(errno, "cannot close file")); 126 | } 127 | 128 | long long file::size() const { 129 | #ifdef _WIN32 130 | // Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT 131 | // is less than 0x0500 as is the case with some default MinGW builds. 132 | // Both functions support large file sizes. 133 | DWORD size_upper = 0; 134 | HANDLE handle = reinterpret_cast(_get_osfhandle(fd_)); 135 | DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper)); 136 | if (size_lower == INVALID_FILE_SIZE) { 137 | DWORD error = GetLastError(); 138 | if (error != NO_ERROR) 139 | FMT_THROW(windows_error(GetLastError(), "cannot get file size")); 140 | } 141 | unsigned long long long_size = size_upper; 142 | return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower; 143 | #else 144 | using Stat = struct stat; 145 | Stat file_stat = Stat(); 146 | if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1) 147 | FMT_THROW(system_error(errno, "cannot get file attributes")); 148 | static_assert(sizeof(long long) >= sizeof(file_stat.st_size), 149 | "return type of file::size is not large enough"); 150 | return file_stat.st_size; 151 | #endif 152 | } 153 | 154 | std::size_t file::read(void* buffer, std::size_t count) { 155 | RWResult result = 0; 156 | FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count)))); 157 | if (result < 0) FMT_THROW(system_error(errno, "cannot read from file")); 158 | return internal::to_unsigned(result); 159 | } 160 | 161 | std::size_t file::write(const void* buffer, std::size_t count) { 162 | RWResult result = 0; 163 | FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count)))); 164 | if (result < 0) FMT_THROW(system_error(errno, "cannot write to file")); 165 | return internal::to_unsigned(result); 166 | } 167 | 168 | file file::dup(int fd) { 169 | // Don't retry as dup doesn't return EINTR. 170 | // http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html 171 | int new_fd = FMT_POSIX_CALL(dup(fd)); 172 | if (new_fd == -1) 173 | FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd)); 174 | return file(new_fd); 175 | } 176 | 177 | void file::dup2(int fd) { 178 | int result = 0; 179 | FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); 180 | if (result == -1) { 181 | FMT_THROW(system_error(errno, "cannot duplicate file descriptor {} to {}", 182 | fd_, fd)); 183 | } 184 | } 185 | 186 | void file::dup2(int fd, error_code& ec) FMT_NOEXCEPT { 187 | int result = 0; 188 | FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); 189 | if (result == -1) ec = error_code(errno); 190 | } 191 | 192 | void file::pipe(file& read_end, file& write_end) { 193 | // Close the descriptors first to make sure that assignments don't throw 194 | // and there are no leaks. 195 | read_end.close(); 196 | write_end.close(); 197 | int fds[2] = {}; 198 | #ifdef _WIN32 199 | // Make the default pipe capacity same as on Linux 2.6.11+. 200 | enum { DEFAULT_CAPACITY = 65536 }; 201 | int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY)); 202 | #else 203 | // Don't retry as the pipe function doesn't return EINTR. 204 | // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html 205 | int result = FMT_POSIX_CALL(pipe(fds)); 206 | #endif 207 | if (result != 0) FMT_THROW(system_error(errno, "cannot create pipe")); 208 | // The following assignments don't throw because read_fd and write_fd 209 | // are closed. 210 | read_end = file(fds[0]); 211 | write_end = file(fds[1]); 212 | } 213 | 214 | buffered_file file::fdopen(const char* mode) { 215 | // Don't retry as fdopen doesn't return EINTR. 216 | FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode)); 217 | if (!f) 218 | FMT_THROW( 219 | system_error(errno, "cannot associate stream with file descriptor")); 220 | buffered_file bf(f); 221 | fd_ = -1; 222 | return bf; 223 | } 224 | 225 | long getpagesize() { 226 | #ifdef _WIN32 227 | SYSTEM_INFO si; 228 | GetSystemInfo(&si); 229 | return si.dwPageSize; 230 | #else 231 | long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE)); 232 | if (size < 0) FMT_THROW(system_error(errno, "cannot get memory page size")); 233 | return size; 234 | #endif 235 | } 236 | #endif // FMT_USE_FCNTL 237 | FMT_END_NAMESPACE 238 | -------------------------------------------------------------------------------- /ext/fmt/support/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | include $(CLEAR_VARS) 3 | 4 | LOCAL_MODULE := fmt_static 5 | LOCAL_MODULE_FILENAME := libfmt 6 | 7 | LOCAL_SRC_FILES := ../src/format.cc 8 | 9 | LOCAL_C_INCLUDES := $(LOCAL_PATH) 10 | LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) 11 | 12 | LOCAL_CFLAGS += -std=c++11 -fexceptions 13 | 14 | include $(BUILD_STATIC_LIBRARY) 15 | 16 | -------------------------------------------------------------------------------- /ext/fmt/support/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /ext/fmt/support/README: -------------------------------------------------------------------------------- 1 | This directory contains build support files such as 2 | 3 | * CMake modules 4 | * Build scripts 5 | * qmake (static build with dynamic libc only) 6 | 7 | -------------------------------------------------------------------------------- /ext/fmt/support/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # A vagrant config for testing against gcc-4.8. 5 | Vagrant.configure("2") do |config| 6 | config.vm.box = "ubuntu/xenial64" 7 | 8 | config.vm.provider "virtualbox" do |vb| 9 | vb.memory = "4096" 10 | end 11 | 12 | config.vm.provision "shell", inline: <<-SHELL 13 | apt-get update 14 | apt-get install -y g++ make wget git 15 | wget -q https://github.com/Kitware/CMake/releases/download/v3.14.4/cmake-3.14.4-Linux-x86_64.tar.gz 16 | tar xzf cmake-3.14.4-Linux-x86_64.tar.gz 17 | ln -s `pwd`/cmake-3.14.4-Linux-x86_64/bin/cmake /usr/local/bin 18 | SHELL 19 | end 20 | -------------------------------------------------------------------------------- /ext/fmt/support/appveyor-build.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Build the project on AppVeyor. 3 | 4 | import os 5 | from subprocess import check_call 6 | 7 | build = os.environ['BUILD'] 8 | config = os.environ['CONFIGURATION'] 9 | platform = os.environ['PLATFORM'] 10 | path = os.environ['PATH'] 11 | image = os.environ['APPVEYOR_BUILD_WORKER_IMAGE'] 12 | jobid = os.environ['APPVEYOR_JOB_ID'] 13 | cmake_command = ['cmake', '-DFMT_PEDANTIC=ON', '-DCMAKE_BUILD_TYPE=' + config, '..'] 14 | if build == 'mingw': 15 | cmake_command.append('-GMinGW Makefiles') 16 | build_command = ['mingw32-make', '-j4'] 17 | test_command = ['mingw32-make', 'test'] 18 | # Remove the path to Git bin directory from $PATH because it breaks 19 | # MinGW config. 20 | path = path.replace(r'C:\Program Files (x86)\Git\bin', '') 21 | os.environ['PATH'] = r'C:\MinGW\bin;' + path 22 | else: 23 | # Add MSBuild 14.0 to PATH as described in 24 | # http://help.appveyor.com/discussions/problems/2229-v140-not-found-on-vs2105rc. 25 | os.environ['PATH'] = r'C:\Program Files (x86)\MSBuild\15.0\Bin;' + path 26 | if image == 'Visual Studio 2019': 27 | generator = 'Visual Studio 16 2019' 28 | if platform == 'x64': 29 | cmake_command.extend(['-A', 'x64']) 30 | else: 31 | if image == 'Visual Studio 2015': 32 | generator = 'Visual Studio 14 2015' 33 | elif image == 'Visual Studio 2017': 34 | generator = 'Visual Studio 15 2017' 35 | if platform == 'x64': 36 | generator += ' Win64' 37 | cmake_command.append('-G' + generator) 38 | build_command = ['cmake', '--build', '.', '--config', config, '--', '/m:4'] 39 | test_command = ['ctest', '-C', config] 40 | 41 | check_call(cmake_command) 42 | check_call(build_command) 43 | check_call(test_command) 44 | -------------------------------------------------------------------------------- /ext/fmt/support/appveyor.yml: -------------------------------------------------------------------------------- 1 | configuration: 2 | - Debug 3 | - Release 4 | 5 | clone_depth: 1 6 | 7 | image: 8 | - Visual Studio 2015 9 | - Visual Studio 2019 10 | - Visual Studio 2017 11 | 12 | platform: 13 | - Win32 14 | - x64 15 | 16 | environment: 17 | CTEST_OUTPUT_ON_FAILURE: 1 18 | MSVC_DEFAULT_OPTIONS: ON 19 | BUILD: msvc 20 | 21 | matrix: 22 | exclude: 23 | - image: Visual Studio 2015 24 | platform: Win32 25 | - image: Visual Studio 2019 26 | platform: Win32 27 | 28 | before_build: 29 | - mkdir build 30 | - cd build 31 | 32 | build_script: 33 | - python ../support/appveyor-build.py 34 | 35 | on_failure: 36 | - appveyor PushArtifact Testing/Temporary/LastTest.log 37 | - appveyor AddTest test 38 | 39 | # Uncomment this to debug AppVeyor failures. 40 | #on_finish: 41 | # - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) 42 | -------------------------------------------------------------------------------- /ext/fmt/support/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | // General gradle arguments for root project 3 | buildscript { 4 | repositories { 5 | google() 6 | jcenter() 7 | } 8 | dependencies { 9 | // 10 | // https://developer.android.com/studio/releases/gradle-plugin 11 | // 12 | // Notice that 3.3.0 here is the version of [Android Gradle Plugin] 13 | // Accroding to URL above you will need Gradle 5.0 or higher 14 | // 15 | // If you are using Android Studio, and it is using Gradle's lower 16 | // version, Use the plugin version 3.1.3 ~ 3.2.0 for Gradle 4.4 ~ 4.10 17 | classpath 'com.android.tools.build:gradle:3.3.0' 18 | } 19 | } 20 | repositories { 21 | google() 22 | jcenter() 23 | } 24 | 25 | // Output: Shared library (.so) for Android 26 | apply plugin: 'com.android.library' 27 | 28 | android { 29 | compileSdkVersion 25 // Android 7.0 30 | 31 | // Target ABI 32 | // - This option controls target platform of module 33 | // - The platform might be limited by compiler's support 34 | // some can work with Clang(default), but some can work only with GCC... 35 | // if bad, both toolchains might not support it 36 | splits { 37 | abi { 38 | enable true 39 | // Specify platforms for Application 40 | reset() 41 | include "arm64-v8a", "armeabi-v7a", "x86_64" 42 | } 43 | } 44 | 45 | defaultConfig { 46 | minSdkVersion 21 // Android 5.0+ 47 | targetSdkVersion 25 // Follow Compile SDK 48 | versionCode 21 // Follow release count 49 | versionName "5.3.0" // Follow Official version 50 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 51 | 52 | externalNativeBuild { 53 | cmake { 54 | arguments "-DANDROID_STL=c++_shared" // Specify Android STL 55 | arguments "-DBUILD_SHARED_LIBS=true" // Build shared object 56 | arguments "-DFMT_TEST=false" // Skip test 57 | arguments "-DFMT_DOC=false" // Skip document 58 | cppFlags "-std=c++17" 59 | } 60 | } 61 | println("Gradle CMake Plugin: ") 62 | println(externalNativeBuild.cmake.cppFlags) 63 | println(externalNativeBuild.cmake.arguments) 64 | } 65 | 66 | // External Native build 67 | // - Use existing CMakeList.txt 68 | // - Give path to CMake. This gradle file should be 69 | // neighbor of the top level cmake 70 | externalNativeBuild { 71 | cmake { 72 | path "../CMakeLists.txt" 73 | // buildStagingDirectory "./build" // Custom path for cmake output 74 | } 75 | //println(cmake.path) 76 | } 77 | 78 | sourceSets{ 79 | // Android Manifest for Gradle 80 | main { 81 | manifest.srcFile 'AndroidManifest.xml' 82 | } 83 | } 84 | } 85 | 86 | assemble.doLast 87 | { 88 | // Instead of `ninja install`, Gradle will deploy the files. 89 | // We are doing this since FMT is dependent to the ANDROID_STL after build 90 | copy { 91 | from 'build/intermediates/cmake' 92 | into '../libs' 93 | } 94 | // Copy debug binaries 95 | copy { 96 | from '../libs/debug/obj' 97 | into '../libs/debug' 98 | } 99 | // Copy Release binaries 100 | copy { 101 | from '../libs/release/obj' 102 | into '../libs/release' 103 | } 104 | // Remove empty directory 105 | delete '../libs/debug/obj' 106 | delete '../libs/release/obj' 107 | } 108 | -------------------------------------------------------------------------------- /ext/fmt/support/cmake/FindSetEnv.cmake: -------------------------------------------------------------------------------- 1 | # A CMake script to find SetEnv.cmd. 2 | 3 | find_program(WINSDK_SETENV NAMES SetEnv.cmd 4 | PATHS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]/bin") 5 | if (WINSDK_SETENV AND PRINT_PATH) 6 | execute_process(COMMAND ${CMAKE_COMMAND} -E echo "${WINSDK_SETENV}") 7 | endif () 8 | -------------------------------------------------------------------------------- /ext/fmt/support/cmake/cxx14.cmake: -------------------------------------------------------------------------------- 1 | # C++14 feature support detection 2 | 3 | include(CheckCXXSourceCompiles) 4 | include(CheckCXXCompilerFlag) 5 | 6 | if (NOT CMAKE_CXX_STANDARD) 7 | set(CMAKE_CXX_STANDARD 11) 8 | endif() 9 | message(STATUS "CXX_STANDARD: ${CMAKE_CXX_STANDARD}") 10 | 11 | if (CMAKE_CXX_STANDARD EQUAL 20) 12 | check_cxx_compiler_flag(-std=c++20 has_std_20_flag) 13 | check_cxx_compiler_flag(-std=c++2a has_std_2a_flag) 14 | 15 | if (has_std_20_flag) 16 | set(CXX_STANDARD_FLAG -std=c++20) 17 | elseif (has_std_2a_flag) 18 | set(CXX_STANDARD_FLAG -std=c++2a) 19 | endif () 20 | elseif (CMAKE_CXX_STANDARD EQUAL 17) 21 | check_cxx_compiler_flag(-std=c++17 has_std_17_flag) 22 | check_cxx_compiler_flag(-std=c++1z has_std_1z_flag) 23 | 24 | if (has_std_17_flag) 25 | set(CXX_STANDARD_FLAG -std=c++17) 26 | elseif (has_std_1z_flag) 27 | set(CXX_STANDARD_FLAG -std=c++1z) 28 | endif () 29 | elseif (CMAKE_CXX_STANDARD EQUAL 14) 30 | check_cxx_compiler_flag(-std=c++14 has_std_14_flag) 31 | check_cxx_compiler_flag(-std=c++1y has_std_1y_flag) 32 | 33 | if (has_std_14_flag) 34 | set(CXX_STANDARD_FLAG -std=c++14) 35 | elseif (has_std_1y_flag) 36 | set(CXX_STANDARD_FLAG -std=c++1y) 37 | endif () 38 | elseif (CMAKE_CXX_STANDARD EQUAL 11) 39 | check_cxx_compiler_flag(-std=c++11 has_std_11_flag) 40 | check_cxx_compiler_flag(-std=c++0x has_std_0x_flag) 41 | 42 | if (has_std_11_flag) 43 | set(CXX_STANDARD_FLAG -std=c++11) 44 | elseif (has_std_0x_flag) 45 | set(CXX_STANDARD_FLAG -std=c++0x) 46 | endif () 47 | endif () 48 | 49 | set(CMAKE_REQUIRED_FLAGS ${CXX_STANDARD_FLAG}) 50 | 51 | # Check if variadic templates are working and not affected by GCC bug 39653: 52 | # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39653 53 | # Can be removed once gcc 4.4 support is dropped. 54 | check_cxx_source_compiles(" 55 | template 56 | struct S { typedef typename S::type type; }; 57 | int main() {}" SUPPORTS_VARIADIC_TEMPLATES) 58 | if (NOT SUPPORTS_VARIADIC_TEMPLATES) 59 | set (SUPPORTS_VARIADIC_TEMPLATES OFF) 60 | endif () 61 | 62 | # Check if user-defined literals are available 63 | check_cxx_source_compiles(" 64 | void operator\"\" _udl(long double); 65 | int main() {}" 66 | SUPPORTS_USER_DEFINED_LITERALS) 67 | if (NOT SUPPORTS_USER_DEFINED_LITERALS) 68 | set (SUPPORTS_USER_DEFINED_LITERALS OFF) 69 | endif () 70 | 71 | # Check if is available 72 | set(CMAKE_REQUIRED_FLAGS -std=c++1z) 73 | check_cxx_source_compiles(" 74 | #include 75 | int main() {}" 76 | FMT_HAS_VARIANT) 77 | if (NOT FMT_HAS_VARIANT) 78 | set (FMT_HAS_VARIANT OFF) 79 | endif () 80 | 81 | set(CMAKE_REQUIRED_FLAGS ) 82 | -------------------------------------------------------------------------------- /ext/fmt/support/cmake/fmt-config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | include(${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake) 4 | check_required_components(fmt) 5 | -------------------------------------------------------------------------------- /ext/fmt/support/cmake/fmt.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@CMAKE_INSTALL_PREFIX@ 2 | exec_prefix=@CMAKE_INSTALL_PREFIX@ 3 | libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@ 4 | includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ 5 | 6 | Name: fmt 7 | Description: A modern formatting library 8 | Version: @FMT_VERSION@ 9 | Libs: -L${libdir} -lfmt 10 | Cflags: -I${includedir} 11 | 12 | -------------------------------------------------------------------------------- /ext/fmt/support/compute-powers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Compute 10 ** exp with exp in the range [min_exponent, max_exponent] and print 3 | # normalized (with most-significant bit equal to 1) significands in hexadecimal. 4 | 5 | from __future__ import print_function 6 | 7 | min_exponent = -348 8 | max_exponent = 340 9 | step = 8 10 | significand_size = 64 11 | exp_offset = 2000 12 | 13 | class fp: 14 | pass 15 | 16 | powers = [] 17 | for i, exp in enumerate(range(min_exponent, max_exponent + 1, step)): 18 | result = fp() 19 | n = 10 ** exp if exp >= 0 else 2 ** exp_offset / 10 ** -exp 20 | k = significand_size + 1 21 | # Convert to binary and round. 22 | binary = '{:b}'.format(n) 23 | result.f = (int('{:0<{}}'.format(binary[:k], k), 2) + 1) / 2 24 | result.e = len(binary) - (exp_offset if exp < 0 else 0) - significand_size 25 | powers.append(result) 26 | # Sanity check. 27 | exp_offset10 = 400 28 | actual = result.f * 10 ** exp_offset10 29 | if result.e > 0: 30 | actual *= 2 ** result.e 31 | else: 32 | for j in range(-result.e): 33 | actual /= 2 34 | expected = 10 ** (exp_offset10 + exp) 35 | precision = len('{}'.format(expected)) - len('{}'.format(actual - expected)) 36 | if precision < 19: 37 | print('low precision:', precision) 38 | exit(1) 39 | 40 | print('Significands:', end='') 41 | for i, fp in enumerate(powers): 42 | if i % 3 == 0: 43 | print(end='\n ') 44 | print(' {:0<#16x}'.format(fp.f, ), end=',') 45 | 46 | print('\n\nExponents:', end='') 47 | for i, fp in enumerate(powers): 48 | if i % 11 == 0: 49 | print(end='\n ') 50 | print(' {:5}'.format(fp.e), end=',') 51 | 52 | print('\n\nMax exponent difference:', 53 | max([x.e - powers[i - 1].e for i, x in enumerate(powers)][1:])) 54 | -------------------------------------------------------------------------------- /ext/fmt/support/docopt.py: -------------------------------------------------------------------------------- 1 | """Pythonic command-line interface parser that will make you smile. 2 | 3 | * http://docopt.org 4 | * Repository and issue-tracker: https://github.com/docopt/docopt 5 | * Licensed under terms of MIT license (see LICENSE-MIT) 6 | * Copyright (c) 2013 Vladimir Keleshev, vladimir@keleshev.com 7 | 8 | """ 9 | import sys 10 | import re 11 | 12 | 13 | __all__ = ['docopt'] 14 | __version__ = '0.6.1' 15 | 16 | 17 | class DocoptLanguageError(Exception): 18 | 19 | """Error in construction of usage-message by developer.""" 20 | 21 | 22 | class DocoptExit(SystemExit): 23 | 24 | """Exit in case user invoked program with incorrect arguments.""" 25 | 26 | usage = '' 27 | 28 | def __init__(self, message=''): 29 | SystemExit.__init__(self, (message + '\n' + self.usage).strip()) 30 | 31 | 32 | class Pattern(object): 33 | 34 | def __eq__(self, other): 35 | return repr(self) == repr(other) 36 | 37 | def __hash__(self): 38 | return hash(repr(self)) 39 | 40 | def fix(self): 41 | self.fix_identities() 42 | self.fix_repeating_arguments() 43 | return self 44 | 45 | def fix_identities(self, uniq=None): 46 | """Make pattern-tree tips point to same object if they are equal.""" 47 | if not hasattr(self, 'children'): 48 | return self 49 | uniq = list(set(self.flat())) if uniq is None else uniq 50 | for i, child in enumerate(self.children): 51 | if not hasattr(child, 'children'): 52 | assert child in uniq 53 | self.children[i] = uniq[uniq.index(child)] 54 | else: 55 | child.fix_identities(uniq) 56 | 57 | def fix_repeating_arguments(self): 58 | """Fix elements that should accumulate/increment values.""" 59 | either = [list(child.children) for child in transform(self).children] 60 | for case in either: 61 | for e in [child for child in case if case.count(child) > 1]: 62 | if type(e) is Argument or type(e) is Option and e.argcount: 63 | if e.value is None: 64 | e.value = [] 65 | elif type(e.value) is not list: 66 | e.value = e.value.split() 67 | if type(e) is Command or type(e) is Option and e.argcount == 0: 68 | e.value = 0 69 | return self 70 | 71 | 72 | def transform(pattern): 73 | """Expand pattern into an (almost) equivalent one, but with single Either. 74 | 75 | Example: ((-a | -b) (-c | -d)) => (-a -c | -a -d | -b -c | -b -d) 76 | Quirks: [-a] => (-a), (-a...) => (-a -a) 77 | 78 | """ 79 | result = [] 80 | groups = [[pattern]] 81 | while groups: 82 | children = groups.pop(0) 83 | parents = [Required, Optional, OptionsShortcut, Either, OneOrMore] 84 | if any(t in map(type, children) for t in parents): 85 | child = [c for c in children if type(c) in parents][0] 86 | children.remove(child) 87 | if type(child) is Either: 88 | for c in child.children: 89 | groups.append([c] + children) 90 | elif type(child) is OneOrMore: 91 | groups.append(child.children * 2 + children) 92 | else: 93 | groups.append(child.children + children) 94 | else: 95 | result.append(children) 96 | return Either(*[Required(*e) for e in result]) 97 | 98 | 99 | class LeafPattern(Pattern): 100 | 101 | """Leaf/terminal node of a pattern tree.""" 102 | 103 | def __init__(self, name, value=None): 104 | self.name, self.value = name, value 105 | 106 | def __repr__(self): 107 | return '%s(%r, %r)' % (self.__class__.__name__, self.name, self.value) 108 | 109 | def flat(self, *types): 110 | return [self] if not types or type(self) in types else [] 111 | 112 | def match(self, left, collected=None): 113 | collected = [] if collected is None else collected 114 | pos, match = self.single_match(left) 115 | if match is None: 116 | return False, left, collected 117 | left_ = left[:pos] + left[pos + 1:] 118 | same_name = [a for a in collected if a.name == self.name] 119 | if type(self.value) in (int, list): 120 | if type(self.value) is int: 121 | increment = 1 122 | else: 123 | increment = ([match.value] if type(match.value) is str 124 | else match.value) 125 | if not same_name: 126 | match.value = increment 127 | return True, left_, collected + [match] 128 | same_name[0].value += increment 129 | return True, left_, collected 130 | return True, left_, collected + [match] 131 | 132 | 133 | class BranchPattern(Pattern): 134 | 135 | """Branch/inner node of a pattern tree.""" 136 | 137 | def __init__(self, *children): 138 | self.children = list(children) 139 | 140 | def __repr__(self): 141 | return '%s(%s)' % (self.__class__.__name__, 142 | ', '.join(repr(a) for a in self.children)) 143 | 144 | def flat(self, *types): 145 | if type(self) in types: 146 | return [self] 147 | return sum([child.flat(*types) for child in self.children], []) 148 | 149 | 150 | class Argument(LeafPattern): 151 | 152 | def single_match(self, left): 153 | for n, pattern in enumerate(left): 154 | if type(pattern) is Argument: 155 | return n, Argument(self.name, pattern.value) 156 | return None, None 157 | 158 | @classmethod 159 | def parse(class_, source): 160 | name = re.findall('(<\S*?>)', source)[0] 161 | value = re.findall('\[default: (.*)\]', source, flags=re.I) 162 | return class_(name, value[0] if value else None) 163 | 164 | 165 | class Command(Argument): 166 | 167 | def __init__(self, name, value=False): 168 | self.name, self.value = name, value 169 | 170 | def single_match(self, left): 171 | for n, pattern in enumerate(left): 172 | if type(pattern) is Argument: 173 | if pattern.value == self.name: 174 | return n, Command(self.name, True) 175 | else: 176 | break 177 | return None, None 178 | 179 | 180 | class Option(LeafPattern): 181 | 182 | def __init__(self, short=None, long=None, argcount=0, value=False): 183 | assert argcount in (0, 1) 184 | self.short, self.long, self.argcount = short, long, argcount 185 | self.value = None if value is False and argcount else value 186 | 187 | @classmethod 188 | def parse(class_, option_description): 189 | short, long, argcount, value = None, None, 0, False 190 | options, _, description = option_description.strip().partition(' ') 191 | options = options.replace(',', ' ').replace('=', ' ') 192 | for s in options.split(): 193 | if s.startswith('--'): 194 | long = s 195 | elif s.startswith('-'): 196 | short = s 197 | else: 198 | argcount = 1 199 | if argcount: 200 | matched = re.findall('\[default: (.*)\]', description, flags=re.I) 201 | value = matched[0] if matched else None 202 | return class_(short, long, argcount, value) 203 | 204 | def single_match(self, left): 205 | for n, pattern in enumerate(left): 206 | if self.name == pattern.name: 207 | return n, pattern 208 | return None, None 209 | 210 | @property 211 | def name(self): 212 | return self.long or self.short 213 | 214 | def __repr__(self): 215 | return 'Option(%r, %r, %r, %r)' % (self.short, self.long, 216 | self.argcount, self.value) 217 | 218 | 219 | class Required(BranchPattern): 220 | 221 | def match(self, left, collected=None): 222 | collected = [] if collected is None else collected 223 | l = left 224 | c = collected 225 | for pattern in self.children: 226 | matched, l, c = pattern.match(l, c) 227 | if not matched: 228 | return False, left, collected 229 | return True, l, c 230 | 231 | 232 | class Optional(BranchPattern): 233 | 234 | def match(self, left, collected=None): 235 | collected = [] if collected is None else collected 236 | for pattern in self.children: 237 | m, left, collected = pattern.match(left, collected) 238 | return True, left, collected 239 | 240 | 241 | class OptionsShortcut(Optional): 242 | 243 | """Marker/placeholder for [options] shortcut.""" 244 | 245 | 246 | class OneOrMore(BranchPattern): 247 | 248 | def match(self, left, collected=None): 249 | assert len(self.children) == 1 250 | collected = [] if collected is None else collected 251 | l = left 252 | c = collected 253 | l_ = None 254 | matched = True 255 | times = 0 256 | while matched: 257 | # could it be that something didn't match but changed l or c? 258 | matched, l, c = self.children[0].match(l, c) 259 | times += 1 if matched else 0 260 | if l_ == l: 261 | break 262 | l_ = l 263 | if times >= 1: 264 | return True, l, c 265 | return False, left, collected 266 | 267 | 268 | class Either(BranchPattern): 269 | 270 | def match(self, left, collected=None): 271 | collected = [] if collected is None else collected 272 | outcomes = [] 273 | for pattern in self.children: 274 | matched, _, _ = outcome = pattern.match(left, collected) 275 | if matched: 276 | outcomes.append(outcome) 277 | if outcomes: 278 | return min(outcomes, key=lambda outcome: len(outcome[1])) 279 | return False, left, collected 280 | 281 | 282 | class Tokens(list): 283 | 284 | def __init__(self, source, error=DocoptExit): 285 | self += source.split() if hasattr(source, 'split') else source 286 | self.error = error 287 | 288 | @staticmethod 289 | def from_pattern(source): 290 | source = re.sub(r'([\[\]\(\)\|]|\.\.\.)', r' \1 ', source) 291 | source = [s for s in re.split('\s+|(\S*<.*?>)', source) if s] 292 | return Tokens(source, error=DocoptLanguageError) 293 | 294 | def move(self): 295 | return self.pop(0) if len(self) else None 296 | 297 | def current(self): 298 | return self[0] if len(self) else None 299 | 300 | 301 | def parse_long(tokens, options): 302 | """long ::= '--' chars [ ( ' ' | '=' ) chars ] ;""" 303 | long, eq, value = tokens.move().partition('=') 304 | assert long.startswith('--') 305 | value = None if eq == value == '' else value 306 | similar = [o for o in options if o.long == long] 307 | if tokens.error is DocoptExit and similar == []: # if no exact match 308 | similar = [o for o in options if o.long and o.long.startswith(long)] 309 | if len(similar) > 1: # might be simply specified ambiguously 2+ times? 310 | raise tokens.error('%s is not a unique prefix: %s?' % 311 | (long, ', '.join(o.long for o in similar))) 312 | elif len(similar) < 1: 313 | argcount = 1 if eq == '=' else 0 314 | o = Option(None, long, argcount) 315 | options.append(o) 316 | if tokens.error is DocoptExit: 317 | o = Option(None, long, argcount, value if argcount else True) 318 | else: 319 | o = Option(similar[0].short, similar[0].long, 320 | similar[0].argcount, similar[0].value) 321 | if o.argcount == 0: 322 | if value is not None: 323 | raise tokens.error('%s must not have an argument' % o.long) 324 | else: 325 | if value is None: 326 | if tokens.current() in [None, '--']: 327 | raise tokens.error('%s requires argument' % o.long) 328 | value = tokens.move() 329 | if tokens.error is DocoptExit: 330 | o.value = value if value is not None else True 331 | return [o] 332 | 333 | 334 | def parse_shorts(tokens, options): 335 | """shorts ::= '-' ( chars )* [ [ ' ' ] chars ] ;""" 336 | token = tokens.move() 337 | assert token.startswith('-') and not token.startswith('--') 338 | left = token.lstrip('-') 339 | parsed = [] 340 | while left != '': 341 | short, left = '-' + left[0], left[1:] 342 | similar = [o for o in options if o.short == short] 343 | if len(similar) > 1: 344 | raise tokens.error('%s is specified ambiguously %d times' % 345 | (short, len(similar))) 346 | elif len(similar) < 1: 347 | o = Option(short, None, 0) 348 | options.append(o) 349 | if tokens.error is DocoptExit: 350 | o = Option(short, None, 0, True) 351 | else: # why copying is necessary here? 352 | o = Option(short, similar[0].long, 353 | similar[0].argcount, similar[0].value) 354 | value = None 355 | if o.argcount != 0: 356 | if left == '': 357 | if tokens.current() in [None, '--']: 358 | raise tokens.error('%s requires argument' % short) 359 | value = tokens.move() 360 | else: 361 | value = left 362 | left = '' 363 | if tokens.error is DocoptExit: 364 | o.value = value if value is not None else True 365 | parsed.append(o) 366 | return parsed 367 | 368 | 369 | def parse_pattern(source, options): 370 | tokens = Tokens.from_pattern(source) 371 | result = parse_expr(tokens, options) 372 | if tokens.current() is not None: 373 | raise tokens.error('unexpected ending: %r' % ' '.join(tokens)) 374 | return Required(*result) 375 | 376 | 377 | def parse_expr(tokens, options): 378 | """expr ::= seq ( '|' seq )* ;""" 379 | seq = parse_seq(tokens, options) 380 | if tokens.current() != '|': 381 | return seq 382 | result = [Required(*seq)] if len(seq) > 1 else seq 383 | while tokens.current() == '|': 384 | tokens.move() 385 | seq = parse_seq(tokens, options) 386 | result += [Required(*seq)] if len(seq) > 1 else seq 387 | return [Either(*result)] if len(result) > 1 else result 388 | 389 | 390 | def parse_seq(tokens, options): 391 | """seq ::= ( atom [ '...' ] )* ;""" 392 | result = [] 393 | while tokens.current() not in [None, ']', ')', '|']: 394 | atom = parse_atom(tokens, options) 395 | if tokens.current() == '...': 396 | atom = [OneOrMore(*atom)] 397 | tokens.move() 398 | result += atom 399 | return result 400 | 401 | 402 | def parse_atom(tokens, options): 403 | """atom ::= '(' expr ')' | '[' expr ']' | 'options' 404 | | long | shorts | argument | command ; 405 | """ 406 | token = tokens.current() 407 | result = [] 408 | if token in '([': 409 | tokens.move() 410 | matching, pattern = {'(': [')', Required], '[': [']', Optional]}[token] 411 | result = pattern(*parse_expr(tokens, options)) 412 | if tokens.move() != matching: 413 | raise tokens.error("unmatched '%s'" % token) 414 | return [result] 415 | elif token == 'options': 416 | tokens.move() 417 | return [OptionsShortcut()] 418 | elif token.startswith('--') and token != '--': 419 | return parse_long(tokens, options) 420 | elif token.startswith('-') and token not in ('-', '--'): 421 | return parse_shorts(tokens, options) 422 | elif token.startswith('<') and token.endswith('>') or token.isupper(): 423 | return [Argument(tokens.move())] 424 | else: 425 | return [Command(tokens.move())] 426 | 427 | 428 | def parse_argv(tokens, options, options_first=False): 429 | """Parse command-line argument vector. 430 | 431 | If options_first: 432 | argv ::= [ long | shorts ]* [ argument ]* [ '--' [ argument ]* ] ; 433 | else: 434 | argv ::= [ long | shorts | argument ]* [ '--' [ argument ]* ] ; 435 | 436 | """ 437 | parsed = [] 438 | while tokens.current() is not None: 439 | if tokens.current() == '--': 440 | return parsed + [Argument(None, v) for v in tokens] 441 | elif tokens.current().startswith('--'): 442 | parsed += parse_long(tokens, options) 443 | elif tokens.current().startswith('-') and tokens.current() != '-': 444 | parsed += parse_shorts(tokens, options) 445 | elif options_first: 446 | return parsed + [Argument(None, v) for v in tokens] 447 | else: 448 | parsed.append(Argument(None, tokens.move())) 449 | return parsed 450 | 451 | 452 | def parse_defaults(doc): 453 | defaults = [] 454 | for s in parse_section('options:', doc): 455 | # FIXME corner case "bla: options: --foo" 456 | _, _, s = s.partition(':') # get rid of "options:" 457 | split = re.split('\n[ \t]*(-\S+?)', '\n' + s)[1:] 458 | split = [s1 + s2 for s1, s2 in zip(split[::2], split[1::2])] 459 | options = [Option.parse(s) for s in split if s.startswith('-')] 460 | defaults += options 461 | return defaults 462 | 463 | 464 | def parse_section(name, source): 465 | pattern = re.compile('^([^\n]*' + name + '[^\n]*\n?(?:[ \t].*?(?:\n|$))*)', 466 | re.IGNORECASE | re.MULTILINE) 467 | return [s.strip() for s in pattern.findall(source)] 468 | 469 | 470 | def formal_usage(section): 471 | _, _, section = section.partition(':') # drop "usage:" 472 | pu = section.split() 473 | return '( ' + ' '.join(') | (' if s == pu[0] else s for s in pu[1:]) + ' )' 474 | 475 | 476 | def extras(help, version, options, doc): 477 | if help and any((o.name in ('-h', '--help')) and o.value for o in options): 478 | print(doc.strip("\n")) 479 | sys.exit() 480 | if version and any(o.name == '--version' and o.value for o in options): 481 | print(version) 482 | sys.exit() 483 | 484 | 485 | class Dict(dict): 486 | def __repr__(self): 487 | return '{%s}' % ',\n '.join('%r: %r' % i for i in sorted(self.items())) 488 | 489 | 490 | def docopt(doc, argv=None, help=True, version=None, options_first=False): 491 | """Parse `argv` based on command-line interface described in `doc`. 492 | 493 | `docopt` creates your command-line interface based on its 494 | description that you pass as `doc`. Such description can contain 495 | --options, , commands, which could be 496 | [optional], (required), (mutually | exclusive) or repeated... 497 | 498 | Parameters 499 | ---------- 500 | doc : str 501 | Description of your command-line interface. 502 | argv : list of str, optional 503 | Argument vector to be parsed. sys.argv[1:] is used if not 504 | provided. 505 | help : bool (default: True) 506 | Set to False to disable automatic help on -h or --help 507 | options. 508 | version : any object 509 | If passed, the object will be printed if --version is in 510 | `argv`. 511 | options_first : bool (default: False) 512 | Set to True to require options precede positional arguments, 513 | i.e. to forbid options and positional arguments intermix. 514 | 515 | Returns 516 | ------- 517 | args : dict 518 | A dictionary, where keys are names of command-line elements 519 | such as e.g. "--verbose" and "", and values are the 520 | parsed values of those elements. 521 | 522 | Example 523 | ------- 524 | >>> from docopt import docopt 525 | >>> doc = ''' 526 | ... Usage: 527 | ... my_program tcp [--timeout=] 528 | ... my_program serial [--baud=] [--timeout=] 529 | ... my_program (-h | --help | --version) 530 | ... 531 | ... Options: 532 | ... -h, --help Show this screen and exit. 533 | ... --baud= Baudrate [default: 9600] 534 | ... ''' 535 | >>> argv = ['tcp', '127.0.0.1', '80', '--timeout', '30'] 536 | >>> docopt(doc, argv) 537 | {'--baud': '9600', 538 | '--help': False, 539 | '--timeout': '30', 540 | '--version': False, 541 | '': '127.0.0.1', 542 | '': '80', 543 | 'serial': False, 544 | 'tcp': True} 545 | 546 | See also 547 | -------- 548 | * For video introduction see http://docopt.org 549 | * Full documentation is available in README.rst as well as online 550 | at https://github.com/docopt/docopt#readme 551 | 552 | """ 553 | argv = sys.argv[1:] if argv is None else argv 554 | 555 | usage_sections = parse_section('usage:', doc) 556 | if len(usage_sections) == 0: 557 | raise DocoptLanguageError('"usage:" (case-insensitive) not found.') 558 | if len(usage_sections) > 1: 559 | raise DocoptLanguageError('More than one "usage:" (case-insensitive).') 560 | DocoptExit.usage = usage_sections[0] 561 | 562 | options = parse_defaults(doc) 563 | pattern = parse_pattern(formal_usage(DocoptExit.usage), options) 564 | # [default] syntax for argument is disabled 565 | #for a in pattern.flat(Argument): 566 | # same_name = [d for d in arguments if d.name == a.name] 567 | # if same_name: 568 | # a.value = same_name[0].value 569 | argv = parse_argv(Tokens(argv), list(options), options_first) 570 | pattern_options = set(pattern.flat(Option)) 571 | for options_shortcut in pattern.flat(OptionsShortcut): 572 | doc_options = parse_defaults(doc) 573 | options_shortcut.children = list(set(doc_options) - pattern_options) 574 | #if any_options: 575 | # options_shortcut.children += [Option(o.short, o.long, o.argcount) 576 | # for o in argv if type(o) is Option] 577 | extras(help, version, argv, doc) 578 | matched, left, collected = pattern.fix().match(argv) 579 | if matched and left == []: # better error message if left? 580 | return Dict((a.name, a.value) for a in (pattern.flat() + collected)) 581 | raise DocoptExit() 582 | -------------------------------------------------------------------------------- /ext/fmt/support/fmt.pro: -------------------------------------------------------------------------------- 1 | # Staticlib configuration for qmake builds 2 | # For some reason qmake 3.1 fails to identify source dependencies and excludes format.cc and printf.cc 3 | # from compilation so it _MUST_ be called as qmake -nodepend 4 | # A workaround is implemented below: a custom compiler is defined which does not track dependencies 5 | 6 | TEMPLATE = lib 7 | 8 | TARGET = fmt 9 | 10 | QMAKE_EXT_CPP = .cc 11 | 12 | CONFIG = staticlib warn_on c++11 13 | 14 | FMT_SOURCES = \ 15 | ../src/format.cc \ 16 | ../src/posix.cc 17 | 18 | fmt.name = libfmt 19 | fmt.input = FMT_SOURCES 20 | fmt.output = ${QMAKE_FILE_BASE}$$QMAKE_EXT_OBJ 21 | fmt.clean = ${QMAKE_FILE_BASE}$$QMAKE_EXT_OBJ 22 | fmt.depends = ${QMAKE_FILE_IN} 23 | # QMAKE_RUN_CXX will not be expanded 24 | fmt.commands = $$QMAKE_CXX -c $$QMAKE_CXXFLAGS $$QMAKE_CXXFLAGS_WARN_ON $$QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO $$QMAKE_CXXFLAGS_CXX11 ${QMAKE_FILE_IN} 25 | fmt.variable_out = OBJECTS 26 | fmt.CONFIG = no_dependencies no_link 27 | QMAKE_EXTRA_COMPILERS += fmt 28 | -------------------------------------------------------------------------------- /ext/fmt/support/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """Manage site and releases. 4 | 5 | Usage: 6 | manage.py release [] 7 | manage.py site 8 | 9 | For the release command $FMT_TOKEN should contain a GitHub personal access token 10 | obtained from https://github.com/settings/tokens. 11 | """ 12 | 13 | from __future__ import print_function 14 | import datetime, docopt, errno, fileinput, json, os 15 | import re, requests, shutil, sys, tempfile 16 | from contextlib import contextmanager 17 | from distutils.version import LooseVersion 18 | from subprocess import check_call 19 | 20 | 21 | class Git: 22 | def __init__(self, dir): 23 | self.dir = dir 24 | 25 | def call(self, method, args, **kwargs): 26 | return check_call(['git', method] + list(args), **kwargs) 27 | 28 | def add(self, *args): 29 | return self.call('add', args, cwd=self.dir) 30 | 31 | def checkout(self, *args): 32 | return self.call('checkout', args, cwd=self.dir) 33 | 34 | def clean(self, *args): 35 | return self.call('clean', args, cwd=self.dir) 36 | 37 | def clone(self, *args): 38 | return self.call('clone', list(args) + [self.dir]) 39 | 40 | def commit(self, *args): 41 | return self.call('commit', args, cwd=self.dir) 42 | 43 | def pull(self, *args): 44 | return self.call('pull', args, cwd=self.dir) 45 | 46 | def push(self, *args): 47 | return self.call('push', args, cwd=self.dir) 48 | 49 | def reset(self, *args): 50 | return self.call('reset', args, cwd=self.dir) 51 | 52 | def update(self, *args): 53 | clone = not os.path.exists(self.dir) 54 | if clone: 55 | self.clone(*args) 56 | return clone 57 | 58 | 59 | def clean_checkout(repo, branch): 60 | repo.clean('-f', '-d') 61 | repo.reset('--hard') 62 | repo.checkout(branch) 63 | 64 | 65 | class Runner: 66 | def __init__(self, cwd): 67 | self.cwd = cwd 68 | 69 | def __call__(self, *args, **kwargs): 70 | kwargs['cwd'] = kwargs.get('cwd', self.cwd) 71 | check_call(args, **kwargs) 72 | 73 | 74 | def create_build_env(): 75 | """Create a build environment.""" 76 | class Env: 77 | pass 78 | env = Env() 79 | 80 | # Import the documentation build module. 81 | env.fmt_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 82 | sys.path.insert(0, os.path.join(env.fmt_dir, 'doc')) 83 | import build 84 | 85 | env.build_dir = 'build' 86 | env.versions = build.versions 87 | 88 | # Virtualenv and repos are cached to speed up builds. 89 | build.create_build_env(os.path.join(env.build_dir, 'virtualenv')) 90 | 91 | env.fmt_repo = Git(os.path.join(env.build_dir, 'fmt')) 92 | return env 93 | 94 | 95 | @contextmanager 96 | def rewrite(filename): 97 | class Buffer: 98 | pass 99 | buffer = Buffer() 100 | if not os.path.exists(filename): 101 | buffer.data = '' 102 | yield buffer 103 | return 104 | with open(filename) as f: 105 | buffer.data = f.read() 106 | yield buffer 107 | with open(filename, 'w') as f: 108 | f.write(buffer.data) 109 | 110 | 111 | fmt_repo_url = 'git@github.com:fmtlib/fmt' 112 | 113 | 114 | def update_site(env): 115 | env.fmt_repo.update(fmt_repo_url) 116 | 117 | doc_repo = Git(os.path.join(env.build_dir, 'fmtlib.github.io')) 118 | doc_repo.update('git@github.com:fmtlib/fmtlib.github.io') 119 | 120 | for version in env.versions: 121 | clean_checkout(env.fmt_repo, version) 122 | target_doc_dir = os.path.join(env.fmt_repo.dir, 'doc') 123 | # Remove the old theme. 124 | for entry in os.listdir(target_doc_dir): 125 | path = os.path.join(target_doc_dir, entry) 126 | if os.path.isdir(path): 127 | shutil.rmtree(path) 128 | # Copy the new theme. 129 | for entry in ['_static', '_templates', 'basic-bootstrap', 'bootstrap', 130 | 'conf.py', 'fmt.less']: 131 | src = os.path.join(env.fmt_dir, 'doc', entry) 132 | dst = os.path.join(target_doc_dir, entry) 133 | copy = shutil.copytree if os.path.isdir(src) else shutil.copyfile 134 | copy(src, dst) 135 | # Rename index to contents. 136 | contents = os.path.join(target_doc_dir, 'contents.rst') 137 | if not os.path.exists(contents): 138 | os.rename(os.path.join(target_doc_dir, 'index.rst'), contents) 139 | # Fix issues in reference.rst/api.rst. 140 | for filename in ['reference.rst', 'api.rst']: 141 | pattern = re.compile('doxygenfunction.. (bin|oct|hexu|hex)$', re.M) 142 | with rewrite(os.path.join(target_doc_dir, filename)) as b: 143 | b.data = b.data.replace('std::ostream &', 'std::ostream&') 144 | b.data = re.sub(pattern, r'doxygenfunction:: \1(int)', b.data) 145 | b.data = b.data.replace('std::FILE*', 'std::FILE *') 146 | b.data = b.data.replace('unsigned int', 'unsigned') 147 | b.data = b.data.replace('operator""_', 'operator"" _') 148 | b.data = b.data.replace(', size_t', ', std::size_t') 149 | # Fix a broken link in index.rst. 150 | index = os.path.join(target_doc_dir, 'index.rst') 151 | with rewrite(index) as b: 152 | b.data = b.data.replace( 153 | 'doc/latest/index.html#format-string-syntax', 'syntax.html') 154 | # Build the docs. 155 | html_dir = os.path.join(env.build_dir, 'html') 156 | if os.path.exists(html_dir): 157 | shutil.rmtree(html_dir) 158 | include_dir = env.fmt_repo.dir 159 | if LooseVersion(version) >= LooseVersion('5.0.0'): 160 | include_dir = os.path.join(include_dir, 'include', 'fmt') 161 | elif LooseVersion(version) >= LooseVersion('3.0.0'): 162 | include_dir = os.path.join(include_dir, 'fmt') 163 | import build 164 | build.build_docs(version, doc_dir=target_doc_dir, 165 | include_dir=include_dir, work_dir=env.build_dir) 166 | shutil.rmtree(os.path.join(html_dir, '.doctrees')) 167 | # Create symlinks for older versions. 168 | for link, target in {'index': 'contents', 'api': 'reference'}.items(): 169 | link = os.path.join(html_dir, link) + '.html' 170 | target += '.html' 171 | if os.path.exists(os.path.join(html_dir, target)) and \ 172 | not os.path.exists(link): 173 | os.symlink(target, link) 174 | # Copy docs to the website. 175 | version_doc_dir = os.path.join(doc_repo.dir, version) 176 | try: 177 | shutil.rmtree(version_doc_dir) 178 | except OSError as e: 179 | if e.errno != errno.ENOENT: 180 | raise 181 | shutil.move(html_dir, version_doc_dir) 182 | 183 | 184 | def release(args): 185 | env = create_build_env() 186 | fmt_repo = env.fmt_repo 187 | 188 | branch = args.get('') 189 | if branch is None: 190 | branch = 'master' 191 | if not fmt_repo.update('-b', branch, fmt_repo_url): 192 | clean_checkout(fmt_repo, branch) 193 | 194 | # Convert changelog from RST to GitHub-flavored Markdown and get the 195 | # version. 196 | changelog = 'ChangeLog.rst' 197 | changelog_path = os.path.join(fmt_repo.dir, changelog) 198 | import rst2md 199 | changes, version = rst2md.convert(changelog_path) 200 | cmakelists = 'CMakeLists.txt' 201 | for line in fileinput.input(os.path.join(fmt_repo.dir, cmakelists), 202 | inplace=True): 203 | prefix = 'set(FMT_VERSION ' 204 | if line.startswith(prefix): 205 | line = prefix + version + ')\n' 206 | sys.stdout.write(line) 207 | 208 | # Update the version in the changelog. 209 | title_len = 0 210 | for line in fileinput.input(changelog_path, inplace=True): 211 | if line.decode('utf-8').startswith(version + ' - TBD'): 212 | line = version + ' - ' + datetime.date.today().isoformat() 213 | title_len = len(line) 214 | line += '\n' 215 | elif title_len: 216 | line = '-' * title_len + '\n' 217 | title_len = 0 218 | sys.stdout.write(line) 219 | 220 | # Add the version to the build script. 221 | script = os.path.join('doc', 'build.py') 222 | script_path = os.path.join(fmt_repo.dir, script) 223 | for line in fileinput.input(script_path, inplace=True): 224 | m = re.match(r'( *versions = )\[(.+)\]', line) 225 | if m: 226 | line = '{}[{}, \'{}\']\n'.format(m.group(1), m.group(2), version) 227 | sys.stdout.write(line) 228 | 229 | fmt_repo.checkout('-B', 'release') 230 | fmt_repo.add(changelog, cmakelists, script) 231 | fmt_repo.commit('-m', 'Update version') 232 | 233 | # Build the docs and package. 234 | run = Runner(fmt_repo.dir) 235 | run('cmake', '.') 236 | run('make', 'doc', 'package_source') 237 | update_site(env) 238 | 239 | # Create a release on GitHub. 240 | fmt_repo.push('origin', 'release') 241 | params = {'access_token': os.getenv('FMT_TOKEN')} 242 | r = requests.post('https://api.github.com/repos/fmtlib/fmt/releases', 243 | params=params, 244 | data=json.dumps({'tag_name': version, 245 | 'target_commitish': 'release', 246 | 'body': changes, 'draft': True})) 247 | if r.status_code != 201: 248 | raise Exception('Failed to create a release ' + str(r)) 249 | id = r.json()['id'] 250 | uploads_url = 'https://uploads.github.com/repos/fmtlib/fmt/releases' 251 | package = 'fmt-{}.zip'.format(version) 252 | r = requests.post( 253 | '{}/{}/assets?name={}'.format(uploads_url, id, package), 254 | headers={'Content-Type': 'application/zip'}, 255 | params=params, data=open('build/fmt/' + package, 'rb')) 256 | if r.status_code != 201: 257 | raise Exception('Failed to upload an asset ' + str(r)) 258 | 259 | 260 | if __name__ == '__main__': 261 | args = docopt.docopt(__doc__) 262 | if args.get('release'): 263 | release(args) 264 | elif args.get('site'): 265 | update_site(create_build_env()) 266 | -------------------------------------------------------------------------------- /ext/fmt/support/rst2md.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # reStructuredText (RST) to GitHub-flavored Markdown converter 3 | 4 | import re, sys 5 | from docutils import core, nodes, writers 6 | 7 | 8 | def is_github_ref(node): 9 | return re.match('https://github.com/.*/(issues|pull)/.*', node['refuri']) 10 | 11 | 12 | class Translator(nodes.NodeVisitor): 13 | def __init__(self, document): 14 | nodes.NodeVisitor.__init__(self, document) 15 | self.output = '' 16 | self.indent = 0 17 | self.preserve_newlines = False 18 | 19 | def write(self, text): 20 | self.output += text.replace('\n', '\n' + ' ' * self.indent) 21 | 22 | def visit_document(self, node): 23 | pass 24 | 25 | def depart_document(self, node): 26 | pass 27 | 28 | def visit_section(self, node): 29 | pass 30 | 31 | def depart_section(self, node): 32 | # Skip all sections except the first one. 33 | raise nodes.StopTraversal 34 | 35 | def visit_title(self, node): 36 | self.version = re.match(r'(\d+\.\d+\.\d+).*', node.children[0]).group(1) 37 | raise nodes.SkipChildren 38 | 39 | def visit_title_reference(self, node): 40 | raise Exception(node) 41 | 42 | def depart_title(self, node): 43 | pass 44 | 45 | def visit_Text(self, node): 46 | if not self.preserve_newlines: 47 | node = node.replace('\n', ' ') 48 | self.write(node) 49 | 50 | def depart_Text(self, node): 51 | pass 52 | 53 | def visit_bullet_list(self, node): 54 | pass 55 | 56 | def depart_bullet_list(self, node): 57 | pass 58 | 59 | def visit_list_item(self, node): 60 | self.write('* ') 61 | self.indent += 2 62 | 63 | def depart_list_item(self, node): 64 | self.indent -= 2 65 | self.write('\n\n') 66 | 67 | def visit_paragraph(self, node): 68 | pass 69 | 70 | def depart_paragraph(self, node): 71 | pass 72 | 73 | def visit_reference(self, node): 74 | if not is_github_ref(node): 75 | self.write('[') 76 | 77 | def depart_reference(self, node): 78 | if not is_github_ref(node): 79 | self.write('](' + node['refuri'] + ')') 80 | 81 | def visit_target(self, node): 82 | pass 83 | 84 | def depart_target(self, node): 85 | pass 86 | 87 | def visit_literal(self, node): 88 | self.write('`') 89 | 90 | def depart_literal(self, node): 91 | self.write('`') 92 | 93 | def visit_literal_block(self, node): 94 | self.write('\n\n```') 95 | if 'c++' in node['classes']: 96 | self.write('c++') 97 | self.write('\n') 98 | self.preserve_newlines = True 99 | 100 | def depart_literal_block(self, node): 101 | self.write('\n```\n') 102 | self.preserve_newlines = False 103 | 104 | def visit_inline(self, node): 105 | pass 106 | 107 | def depart_inline(self, node): 108 | pass 109 | 110 | def visit_image(self, node): 111 | self.write('![](' + node['uri'] + ')') 112 | 113 | def depart_image(self, node): 114 | pass 115 | 116 | def write_row(self, row, widths): 117 | for i, entry in enumerate(row): 118 | text = entry[0][0] if len(entry) > 0 else '' 119 | if i != 0: 120 | self.write('|') 121 | self.write('{:{}}'.format(text, widths[i])) 122 | self.write('\n') 123 | 124 | def visit_table(self, node): 125 | table = node.children[0] 126 | colspecs = table[:-2] 127 | thead = table[-2] 128 | tbody = table[-1] 129 | widths = [int(cs['colwidth']) for cs in colspecs] 130 | sep = '|'.join(['-' * w for w in widths]) + '\n' 131 | self.write('\n\n') 132 | self.write_row(thead[0], widths) 133 | self.write(sep) 134 | for row in tbody: 135 | self.write_row(row, widths) 136 | raise nodes.SkipChildren 137 | 138 | def depart_table(self, node): 139 | pass 140 | 141 | class MDWriter(writers.Writer): 142 | """GitHub-flavored markdown writer""" 143 | 144 | supported = ('md',) 145 | """Formats this writer supports.""" 146 | 147 | def translate(self): 148 | translator = Translator(self.document) 149 | self.document.walkabout(translator) 150 | self.output = (translator.output, translator.version) 151 | 152 | 153 | def convert(rst_path): 154 | """Converts RST file to Markdown.""" 155 | return core.publish_file(source_path=rst_path, writer=MDWriter()) 156 | 157 | 158 | if __name__ == '__main__': 159 | convert(sys.argv[1]) 160 | -------------------------------------------------------------------------------- /ext/fmt/support/rtd/conf.py: -------------------------------------------------------------------------------- 1 | # Sphinx configuration for readthedocs. 2 | 3 | import os, sys 4 | 5 | master_doc = 'index' 6 | html_theme = 'theme' 7 | html_theme_path = ["."] 8 | -------------------------------------------------------------------------------- /ext/fmt/support/rtd/index.rst: -------------------------------------------------------------------------------- 1 | If you are not redirected automatically, follow the 2 | `link to the fmt documentation `_. 3 | -------------------------------------------------------------------------------- /ext/fmt/support/rtd/theme/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "basic/layout.html" %} 2 | 3 | {% block extrahead %} 4 | 5 | 6 | 9 | Page Redirection 10 | {% endblock %} 11 | 12 | {% block document %} 13 | If you are not redirected automatically, follow the link to the fmt documentation. 14 | {% endblock %} 15 | 16 | {% block footer %} 17 | {% endblock %} 18 | -------------------------------------------------------------------------------- /ext/fmt/support/rtd/theme/theme.conf: -------------------------------------------------------------------------------- 1 | [theme] 2 | inherit = basic 3 | -------------------------------------------------------------------------------- /ext/fmt/support/travis-build.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Build the project on Travis CI. 3 | 4 | from __future__ import print_function 5 | import errno, os, shutil, subprocess, sys, urllib 6 | from subprocess import call, check_call, Popen, PIPE, STDOUT 7 | 8 | def rmtree_if_exists(dir): 9 | try: 10 | shutil.rmtree(dir) 11 | except OSError as e: 12 | if e.errno == errno.ENOENT: 13 | pass 14 | 15 | def makedirs_if_not_exist(dir): 16 | try: 17 | os.makedirs(dir) 18 | except OSError as e: 19 | if e.errno != errno.EEXIST: 20 | raise 21 | 22 | def install_dependencies(): 23 | branch = os.environ['TRAVIS_BRANCH'] 24 | if branch != 'master': 25 | print('Branch: ' + branch) 26 | exit(0) # Ignore non-master branches 27 | check_call('curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key ' + 28 | '| sudo apt-key add -', shell=True) 29 | check_call('echo "deb https://deb.nodesource.com/node_0.10 precise main" ' + 30 | '| sudo tee /etc/apt/sources.list.d/nodesource.list', shell=True) 31 | check_call(['sudo', 'apt-get', 'update']) 32 | check_call(['sudo', 'apt-get', 'install', 'python-virtualenv', 'nodejs']) 33 | check_call(['sudo', 'npm', 'install', '-g', 'less@2.6.1', 'less-plugin-clean-css']) 34 | deb_file = 'doxygen_1.8.6-2_amd64.deb' 35 | urllib.urlretrieve('http://mirrors.kernel.org/ubuntu/pool/main/d/doxygen/' + 36 | deb_file, deb_file) 37 | check_call(['sudo', 'dpkg', '-i', deb_file]) 38 | 39 | fmt_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) 40 | 41 | build = os.environ['BUILD'] 42 | if build == 'Doc': 43 | travis = 'TRAVIS' in os.environ 44 | if travis: 45 | install_dependencies() 46 | sys.path.insert(0, os.path.join(fmt_dir, 'doc')) 47 | import build 48 | build.create_build_env() 49 | html_dir = build.build_docs() 50 | repo = 'fmtlib.github.io' 51 | if travis and 'KEY' not in os.environ: 52 | # Don't update the repo if building on Travis from an account that 53 | # doesn't have push access. 54 | print('Skipping update of ' + repo) 55 | exit(0) 56 | # Clone the fmtlib.github.io repo. 57 | rmtree_if_exists(repo) 58 | git_url = 'https://github.com/' if travis else 'git@github.com:' 59 | check_call(['git', 'clone', git_url + 'fmtlib/{}.git'.format(repo)]) 60 | # Copy docs to the repo. 61 | target_dir = os.path.join(repo, 'dev') 62 | rmtree_if_exists(target_dir) 63 | shutil.copytree(html_dir, target_dir, ignore=shutil.ignore_patterns('.*')) 64 | if travis: 65 | check_call(['git', 'config', '--global', 'user.name', 'amplbot']) 66 | check_call(['git', 'config', '--global', 'user.email', 'viz@ampl.com']) 67 | # Push docs to GitHub pages. 68 | check_call(['git', 'add', '--all'], cwd=repo) 69 | if call(['git', 'diff-index', '--quiet', 'HEAD'], cwd=repo): 70 | check_call(['git', 'commit', '-m', 'Update documentation'], cwd=repo) 71 | cmd = 'git push' 72 | if travis: 73 | cmd += ' https://$KEY@github.com/fmtlib/fmtlib.github.io.git master' 74 | p = Popen(cmd, shell=True, stdout=PIPE, stderr=STDOUT, cwd=repo) 75 | # Print the output without the key. 76 | print(p.communicate()[0].replace(os.environ['KEY'], '$KEY')) 77 | if p.returncode != 0: 78 | raise subprocess.CalledProcessError(p.returncode, cmd) 79 | exit(0) 80 | 81 | standard = os.environ['STANDARD'] 82 | install_dir = os.path.join(fmt_dir, "_install") 83 | build_dir = os.path.join(fmt_dir, "_build") 84 | test_build_dir = os.path.join(fmt_dir, "_build_test") 85 | 86 | # Configure the library. 87 | makedirs_if_not_exist(build_dir) 88 | cmake_flags = [ 89 | '-DCMAKE_INSTALL_PREFIX=' + install_dir, '-DCMAKE_BUILD_TYPE=' + build, 90 | '-DCMAKE_CXX_STANDARD=' + standard 91 | ] 92 | 93 | # Make sure the fuzzers still compile. 94 | main_cmake_flags = list(cmake_flags) 95 | if 'ENABLE_FUZZING' in os.environ: 96 | main_cmake_flags += ['-DFMT_FUZZ=ON', '-DFMT_FUZZ_LINKMAIN=On'] 97 | 98 | check_call(['cmake', '-DFMT_DOC=OFF', '-DFMT_PEDANTIC=ON', '-DFMT_WERROR=ON', fmt_dir] + 99 | main_cmake_flags, cwd=build_dir) 100 | 101 | # Build the library. 102 | check_call(['cmake', '--build','.'], cwd=build_dir) 103 | 104 | # Test the library. 105 | env = os.environ.copy() 106 | env['CTEST_OUTPUT_ON_FAILURE'] = '1' 107 | if call(['make', 'test'], env=env, cwd=build_dir): 108 | with open(os.path.join(build_dir, 'Testing', 'Temporary', 'LastTest.log'), 'r') as f: 109 | print(f.read()) 110 | sys.exit(-1) 111 | 112 | # Install the library. 113 | check_call(['make', 'install'], cwd=build_dir) 114 | 115 | # Test installation. 116 | makedirs_if_not_exist(test_build_dir) 117 | check_call(['cmake', os.path.join(fmt_dir, "test", "find-package-test")] + 118 | cmake_flags, cwd=test_build_dir) 119 | check_call(['make', '-j4'], cwd=test_build_dir) 120 | -------------------------------------------------------------------------------- /ext/fmt/support/update-coverity-branch.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Update the coverity branch from the master branch. 3 | # It is not done automatically because Coverity Scan limits 4 | # the number of submissions per day. 5 | 6 | from __future__ import print_function 7 | import shutil, tempfile 8 | from subprocess import check_output, STDOUT 9 | 10 | class Git: 11 | def __init__(self, dir): 12 | self.dir = dir 13 | 14 | def __call__(self, *args): 15 | output = check_output(['git'] + list(args), cwd=self.dir, stderr=STDOUT) 16 | print(output) 17 | return output 18 | 19 | dir = tempfile.mkdtemp() 20 | try: 21 | git = Git(dir) 22 | git('clone', '-b', 'coverity', 'git@github.com:fmtlib/fmt.git', dir) 23 | output = git('merge', '-X', 'theirs', '--no-commit', 'origin/master') 24 | if 'Fast-forward' not in output: 25 | git('reset', 'HEAD', '.travis.yml') 26 | git('checkout', '--', '.travis.yml') 27 | git('commit', '-m', 'Update coverity branch') 28 | git('push') 29 | finally: 30 | shutil.rmtree(dir) 31 | -------------------------------------------------------------------------------- /ext/infix_iterator.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by mwo on 24/05/15. 3 | // 4 | // source: http://codereview.stackexchange.com/questions/13176/infix-iterator-code 5 | 6 | // infix_iterator.h 7 | #if !defined(INFIX_ITERATOR_H_) 8 | #define INFIX_ITERATOR_H_ 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | template > 16 | class infix_ostream_iterator : 17 | public std::iterator 18 | { 19 | std::basic_ostream *os; 20 | std::basic_string delimiter; 21 | std::basic_string real_delim; 22 | 23 | public: 24 | 25 | typedef charT char_type; 26 | typedef traits traits_type; 27 | typedef std::basic_ostream ostream_type; 28 | 29 | infix_ostream_iterator(ostream_type &s) 30 | : os(&s) 31 | {} 32 | 33 | infix_ostream_iterator(ostream_type &s, charT const *d) 34 | : os(&s), 35 | real_delim(d) 36 | {} 37 | 38 | infix_ostream_iterator &operator=(T const &item) 39 | { 40 | *os << delimiter << item; 41 | delimiter = real_delim; 42 | return *this; 43 | } 44 | 45 | infix_ostream_iterator &operator*() { 46 | return *this; 47 | } 48 | 49 | infix_ostream_iterator &operator++() { 50 | return *this; 51 | } 52 | 53 | infix_ostream_iterator &operator++(int) { 54 | return *this; 55 | } 56 | }; 57 | 58 | 59 | template 60 | std::string 61 | vec2str(std::vector const& vec, const char* sep = ":") 62 | { 63 | std::stringstream ss; 64 | 65 | std::copy(std::begin(vec), std::end(vec), 66 | infix_ostream_iterator(ss, sep)); 67 | 68 | return ss.str(); 69 | } 70 | 71 | #endif 72 | 73 | -------------------------------------------------------------------------------- /ext/minicsv.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Minimalistic CSV Streams 1.6 3 | // Copyright (C) 2014, by Wong Shao Voon (shaovoon@yahoo.com) 4 | // 5 | // http://opensource.org/licenses/MIT 6 | // 7 | // version 1.2 : make use of make_shared 8 | // version 1.3 : fixed: to when reading the last line and it does not have linefeed 9 | // added: skip_1st_line and skip_line functions to ifstream class 10 | // version 1.4 : Removed the use of smart ptr. 11 | // version 1.5 : Performance increase on writing without flushing every line. 12 | // version 1.6 : Add string streams 13 | // version 1.7 : You MUST specify the escape/unescape string when calling set_delimiter. Option to surround/trim string with quotes 14 | // version 1.7.1 : Add stream operator overload usage in example.cpp 15 | // Disable the surround/trim quote on text by default 16 | // version 1.7.2 : Stream operator overload for const char* 17 | // version 1.7.3 : Add num_of_delimiter method to ifstream and istringstream 18 | // Fix g++ compilation errors 19 | // version 1.7.4 : Add get_rest_of_line 20 | // version 1.7.5 : Add terminate_on_blank_line variable. Set to false if your file format has blank lines in between. 21 | // version 1.7.6 : Ignore delimiters within quotes during reading when enable_trim_quote_on_str is true; 22 | // version 1.7.7 : Fixed multiple symbol linkage errors 23 | 24 | //#define USE_BOOST_LEXICAL_CAST 25 | 26 | #ifndef MiniCSV_H 27 | #define MiniCSV_H 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | #ifdef USE_BOOST_LEXICAL_CAST 34 | # include 35 | #endif 36 | 37 | #define NEWLINE '\n' 38 | 39 | namespace csv 40 | { 41 | inline std::string const & replace(std::string & src, std::string const & to_find, std::string const & to_replace) 42 | { 43 | size_t pos = 0; 44 | while (std::string::npos != pos) 45 | { 46 | pos = src.find(to_find, pos); 47 | 48 | if (std::string::npos != pos) 49 | { 50 | src.erase(pos, to_find.size()); 51 | src.insert(pos, to_replace); 52 | pos += to_replace.size(); 53 | } 54 | } 55 | 56 | return src; 57 | } 58 | 59 | inline std::string trim_right(const std::string& str, const std::string& trimChars) 60 | { 61 | std::string result = ""; 62 | size_t endpos = str.find_last_not_of(trimChars); 63 | if (std::string::npos != endpos) 64 | { 65 | result = str.substr(0, endpos + 1); 66 | } 67 | else 68 | result = str; 69 | 70 | return result; 71 | } 72 | 73 | inline std::string trim_left(const std::string& str, const std::string& trimChars) 74 | { 75 | std::string result = ""; 76 | 77 | size_t startpos = str.find_first_not_of(trimChars); 78 | if (std::string::npos != startpos) 79 | { 80 | result = str.substr(startpos); 81 | } 82 | else 83 | result = str; 84 | 85 | return result; 86 | } 87 | 88 | inline std::string trim(const std::string& str, const std::string& trimChars) 89 | { 90 | return trim_left(trim_right(str, trimChars), trimChars); 91 | } 92 | 93 | class ifstream 94 | { 95 | public: 96 | ifstream() : str(""), pos(0), delimiter(","), unescape_str("##"), trim_quote_on_str(false), trim_quote('\"'), terminate_on_blank_line(true) 97 | { 98 | } 99 | ifstream(const char * file) 100 | { 101 | open(file); 102 | } 103 | void open(const char * file) 104 | { 105 | init(); 106 | istm.open(file, std::ios_base::in); 107 | } 108 | void init() 109 | { 110 | str = ""; 111 | pos = 0; 112 | delimiter = ','; 113 | unescape_str = "##"; 114 | trim_quote_on_str = false; 115 | trim_quote = '\"'; 116 | terminate_on_blank_line = true; 117 | } 118 | void close() 119 | { 120 | istm.close(); 121 | } 122 | bool is_open() 123 | { 124 | return istm.is_open(); 125 | } 126 | void enable_trim_quote_on_str(bool enable, char quote) 127 | { 128 | trim_quote_on_str = enable; 129 | trim_quote = quote; 130 | } 131 | // eof is replaced by read_line 132 | //bool eof() const 133 | void set_delimiter(char delimiter_, std::string const & unescape_str_) 134 | { 135 | delimiter = delimiter_; 136 | unescape_str = unescape_str_; 137 | } 138 | std::string const & get_delimiter() const 139 | { 140 | return delimiter; 141 | } 142 | std::string const & get_unescape_str() const 143 | { 144 | return unescape_str; 145 | } 146 | void skip_line() 147 | { 148 | if(!istm.eof()) 149 | { 150 | std::getline(istm, str); 151 | pos = 0; 152 | } 153 | } 154 | bool read_line() 155 | { 156 | this->str = ""; 157 | while(!istm.eof()) 158 | { 159 | std::getline(istm, this->str); 160 | pos = 0; 161 | 162 | if (this->str.empty()) 163 | { 164 | if (terminate_on_blank_line) 165 | break; 166 | else 167 | continue; 168 | } 169 | 170 | return true; 171 | } 172 | return false; 173 | } 174 | std::string get_delimited_str() 175 | { 176 | std::string str = ""; 177 | char ch = '\0'; 178 | bool within_quote = false; 179 | do 180 | { 181 | if(pos>=this->str.size()) 182 | { 183 | this->str = ""; 184 | 185 | return unescape(str); 186 | } 187 | 188 | ch = this->str[pos]; 189 | if (trim_quote_on_str) 190 | { 191 | if (within_quote == false && ch == trim_quote && ((pos > 0 && this->str[pos - 1] == delimiter[0]) || pos == 0)) 192 | within_quote = true; 193 | else if (within_quote && ch == trim_quote) 194 | within_quote = false; 195 | } 196 | 197 | ++(pos); 198 | 199 | if (ch == delimiter[0] && within_quote == false) 200 | break; 201 | if (ch == '\r' || ch == '\n') 202 | break; 203 | 204 | str += ch; 205 | } 206 | while(true); 207 | 208 | return unescape(str); 209 | } 210 | std::string unescape(std::string & src) 211 | { 212 | src = unescape_str.empty() ? src : replace(src, unescape_str, delimiter); 213 | return trim_quote_on_str ? trim(src, std::string(1, trim_quote)) : src; 214 | } 215 | size_t num_of_delimiter() const 216 | { 217 | if (delimiter.size() == 0) 218 | return 0; 219 | 220 | size_t cnt = 0; 221 | for (size_t i = 0; i < str.size(); ++i) 222 | { 223 | if (str[i] == delimiter[0]) 224 | ++cnt; 225 | } 226 | return cnt; 227 | } 228 | std::string get_rest_of_line() const 229 | { 230 | return str.substr(pos); 231 | } 232 | const std::string& get_line() const 233 | { 234 | return str; 235 | } 236 | void enable_terminate_on_blank_line(bool enable) 237 | { 238 | terminate_on_blank_line = enable; 239 | } 240 | bool is_terminate_on_blank_line() const 241 | { 242 | return terminate_on_blank_line; 243 | } 244 | 245 | private: 246 | std::ifstream istm; 247 | std::string str; 248 | size_t pos; 249 | std::string delimiter; 250 | std::string unescape_str; 251 | bool trim_quote_on_str; 252 | char trim_quote; 253 | bool terminate_on_blank_line; 254 | }; 255 | 256 | class ofstream 257 | { 258 | public: 259 | 260 | ofstream() : after_newline(true), delimiter(","), escape_str("##"), surround_quote_on_str(false), surround_quote('\"') 261 | { 262 | } 263 | ofstream(const char * file) 264 | { 265 | open(file); 266 | } 267 | void open(const char * file) 268 | { 269 | init(); 270 | ostm.open(file, std::ios_base::out); 271 | } 272 | void init() 273 | { 274 | after_newline = true; 275 | delimiter = ','; 276 | escape_str = "##"; 277 | surround_quote_on_str = false; 278 | surround_quote = '\"'; 279 | } 280 | void flush() 281 | { 282 | ostm.flush(); 283 | } 284 | void close() 285 | { 286 | ostm.close(); 287 | } 288 | bool is_open() 289 | { 290 | return ostm.is_open(); 291 | } 292 | void enable_surround_quote_on_str(bool enable, char quote) 293 | { 294 | surround_quote_on_str = enable; 295 | surround_quote = quote; 296 | } 297 | void set_delimiter(char delimiter_, std::string const & escape_str_) 298 | { 299 | delimiter = delimiter_; 300 | escape_str = escape_str_; 301 | } 302 | std::string const & get_delimiter() const 303 | { 304 | return delimiter; 305 | } 306 | std::string const & get_escape_str() const 307 | { 308 | return escape_str; 309 | } 310 | void set_after_newline(bool after_newline_) 311 | { 312 | after_newline = after_newline_; 313 | } 314 | bool get_after_newline() const 315 | { 316 | return after_newline; 317 | } 318 | std::ofstream& get_ofstream() 319 | { 320 | return ostm; 321 | } 322 | void escape_and_output(std::string src) 323 | { 324 | ostm << ((escape_str.empty()) ? src : replace(src, delimiter, escape_str)); 325 | } 326 | void escape_str_and_output(std::string src) 327 | { 328 | src = ((escape_str.empty()) ? src : replace(src, delimiter, escape_str)); 329 | if (surround_quote_on_str) 330 | { 331 | ostm << surround_quote << src << surround_quote; 332 | } 333 | else 334 | { 335 | ostm << src; 336 | } 337 | } 338 | private: 339 | std::ofstream ostm; 340 | bool after_newline; 341 | std::string delimiter; 342 | std::string escape_str; 343 | bool surround_quote_on_str; 344 | char surround_quote; 345 | }; 346 | 347 | 348 | } // ns csv 349 | 350 | template 351 | csv::ifstream& operator >> (csv::ifstream& istm, T& val) 352 | { 353 | std::string str = istm.get_delimited_str(); 354 | 355 | #ifdef USE_BOOST_LEXICAL_CAST 356 | val = boost::lexical_cast(str); 357 | #else 358 | std::istringstream is(str); 359 | is >> val; 360 | #endif 361 | 362 | return istm; 363 | } 364 | template<> 365 | inline csv::ifstream& operator >> (csv::ifstream& istm, std::string& val) 366 | { 367 | val = istm.get_delimited_str(); 368 | 369 | return istm; 370 | } 371 | 372 | template 373 | csv::ofstream& operator << (csv::ofstream& ostm, const T& val) 374 | { 375 | if(!ostm.get_after_newline()) 376 | ostm.get_ofstream() << ostm.get_delimiter(); 377 | 378 | std::ostringstream os_temp; 379 | 380 | os_temp << val; 381 | 382 | ostm.escape_and_output(os_temp.str()); 383 | 384 | ostm.set_after_newline(false); 385 | 386 | return ostm; 387 | } 388 | 389 | template 390 | csv::ofstream& operator << (csv::ofstream& ostm, const T* val) 391 | { 392 | if (!ostm.get_after_newline()) 393 | ostm.get_ofstream() << ostm.get_delimiter(); 394 | 395 | std::ostringstream os_temp; 396 | 397 | os_temp << *val; 398 | 399 | ostm.escape_and_output(os_temp.str()); 400 | 401 | ostm.set_after_newline(false); 402 | 403 | return ostm; 404 | } 405 | 406 | template<> 407 | inline csv::ofstream& operator << (csv::ofstream& ostm, const std::string& val) 408 | { 409 | if (!ostm.get_after_newline()) 410 | ostm.get_ofstream() << ostm.get_delimiter(); 411 | 412 | std::string temp = val; 413 | ostm.escape_str_and_output(temp); 414 | 415 | ostm.set_after_newline(false); 416 | 417 | return ostm; 418 | } 419 | template<> 420 | inline csv::ofstream& operator << (csv::ofstream& ostm, const char& val) 421 | { 422 | if(val==NEWLINE) 423 | { 424 | ostm.get_ofstream() << NEWLINE; 425 | 426 | ostm.set_after_newline(true); 427 | } 428 | else 429 | { 430 | std::ostringstream os_temp; 431 | 432 | os_temp << val; 433 | 434 | ostm.escape_and_output(os_temp.str()); 435 | } 436 | 437 | return ostm; 438 | } 439 | template<> 440 | inline csv::ofstream& operator << (csv::ofstream& ostm, const char* val) 441 | { 442 | const std::string temp = val; 443 | 444 | ostm << temp; 445 | 446 | return ostm; 447 | } 448 | 449 | namespace csv 450 | { 451 | 452 | class istringstream 453 | { 454 | public: 455 | istringstream(const char * text) 456 | : str("") 457 | , pos(0) 458 | , delimiter(",") 459 | , unescape_str("##") 460 | , trim_quote_on_str(false) 461 | , trim_quote('\"') 462 | , terminate_on_blank_line(true) 463 | { 464 | istm.str(text); 465 | } 466 | void enable_trim_quote_on_str(bool enable, char quote) 467 | { 468 | trim_quote_on_str = enable; 469 | trim_quote = quote; 470 | } 471 | void set_delimiter(char delimiter_, std::string const & unescape_str_) 472 | { 473 | delimiter = delimiter_; 474 | unescape_str = unescape_str_; 475 | } 476 | std::string const & get_delimiter() const 477 | { 478 | return delimiter; 479 | } 480 | std::string const & get_unescape_str() const 481 | { 482 | return unescape_str; 483 | } 484 | void skip_line() 485 | { 486 | std::getline(istm, str); 487 | pos = 0; 488 | } 489 | bool read_line() 490 | { 491 | this->str = ""; 492 | while (!istm.eof()) 493 | { 494 | std::getline(istm, this->str); 495 | pos = 0; 496 | 497 | if (this->str.empty()) 498 | { 499 | if (terminate_on_blank_line) 500 | break; 501 | else 502 | continue; 503 | } 504 | 505 | return true; 506 | } 507 | return false; 508 | return false; 509 | } 510 | std::string get_delimited_str() 511 | { 512 | std::string str = ""; 513 | char ch = '\0'; 514 | bool within_quote = false; 515 | do 516 | { 517 | if (pos >= this->str.size()) 518 | { 519 | this->str = ""; 520 | 521 | return unescape(str); 522 | } 523 | 524 | ch = this->str[pos]; 525 | if (trim_quote_on_str) 526 | { 527 | if (within_quote == false && ch == trim_quote && ((pos > 0 && this->str[pos - 1] == delimiter[0]) || pos == 0)) 528 | within_quote = true; 529 | else if (within_quote && ch == trim_quote) 530 | within_quote = false; 531 | } 532 | 533 | ++(pos); 534 | 535 | if (ch == delimiter[0] && within_quote == false) 536 | break; 537 | if (ch == '\r' || ch == '\n') 538 | break; 539 | 540 | str += ch; 541 | } while (true); 542 | 543 | return unescape(str); 544 | } 545 | 546 | std::string unescape(std::string & src) 547 | { 548 | src = unescape_str.empty() ? src : replace(src, unescape_str, delimiter); 549 | return trim_quote_on_str ? trim(src, std::string(1, trim_quote)) : src; 550 | } 551 | 552 | size_t num_of_delimiter() const 553 | { 554 | if (delimiter.size() == 0) 555 | return 0; 556 | 557 | size_t cnt = 0; 558 | for (size_t i = 0; i < str.size(); ++i) 559 | { 560 | if (str[i] == delimiter[0]) 561 | ++cnt; 562 | } 563 | return cnt; 564 | } 565 | std::string get_rest_of_line() const 566 | { 567 | return str.substr(pos); 568 | } 569 | const std::string& get_line() const 570 | { 571 | return str; 572 | } 573 | void enable_terminate_on_blank_line(bool enable) 574 | { 575 | terminate_on_blank_line = enable; 576 | } 577 | bool is_terminate_on_blank_line() const 578 | { 579 | return terminate_on_blank_line; 580 | } 581 | 582 | private: 583 | std::istringstream istm; 584 | std::string str; 585 | size_t pos; 586 | std::string delimiter; 587 | std::string unescape_str; 588 | bool trim_quote_on_str; 589 | char trim_quote; 590 | bool terminate_on_blank_line; 591 | }; 592 | 593 | class ostringstream 594 | { 595 | public: 596 | 597 | ostringstream() : after_newline(true), delimiter(","), escape_str("##"), surround_quote_on_str(false), surround_quote('\"') 598 | { 599 | } 600 | void enable_surround_quote_on_str(bool enable, char quote) 601 | { 602 | surround_quote_on_str = enable; 603 | surround_quote = quote; 604 | } 605 | void set_delimiter(char delimiter_, std::string const & escape_str_) 606 | { 607 | delimiter = delimiter_; 608 | escape_str = escape_str_; 609 | } 610 | std::string const & get_delimiter() const 611 | { 612 | return delimiter; 613 | } 614 | std::string const & get_escape_str() const 615 | { 616 | return escape_str; 617 | } 618 | void set_after_newline(bool after_newline_) 619 | { 620 | after_newline = after_newline_; 621 | } 622 | bool get_after_newline() const 623 | { 624 | return after_newline; 625 | } 626 | std::ostringstream& get_ostringstream() 627 | { 628 | return ostm; 629 | } 630 | std::string get_text() 631 | { 632 | return ostm.str(); 633 | } 634 | void escape_and_output(std::string src) 635 | { 636 | ostm << ((escape_str.empty()) ? src : replace(src, delimiter, escape_str)); 637 | } 638 | void escape_str_and_output(std::string src) 639 | { 640 | src = ((escape_str.empty()) ? src : replace(src, delimiter, escape_str)); 641 | if (surround_quote_on_str) 642 | { 643 | ostm << surround_quote << src << surround_quote; 644 | } 645 | else 646 | { 647 | ostm << src; 648 | } 649 | } 650 | 651 | private: 652 | std::ostringstream ostm; 653 | bool after_newline; 654 | std::string delimiter; 655 | std::string escape_str; 656 | bool surround_quote_on_str; 657 | char surround_quote; 658 | }; 659 | 660 | 661 | } // ns csv 662 | 663 | template 664 | csv::istringstream& operator >> (csv::istringstream& istm, T& val) 665 | { 666 | std::string str = istm.get_delimited_str(); 667 | 668 | #ifdef USE_BOOST_LEXICAL_CAST 669 | val = boost::lexical_cast(str); 670 | #else 671 | std::istringstream is(str); 672 | is >> val; 673 | #endif 674 | 675 | return istm; 676 | } 677 | template<> 678 | inline csv::istringstream& operator >> (csv::istringstream& istm, std::string& val) 679 | { 680 | val = istm.get_delimited_str(); 681 | 682 | return istm; 683 | } 684 | 685 | template 686 | csv::ostringstream& operator << (csv::ostringstream& ostm, const T& val) 687 | { 688 | if (!ostm.get_after_newline()) 689 | ostm.get_ostringstream() << ostm.get_delimiter(); 690 | 691 | std::ostringstream os_temp; 692 | 693 | os_temp << val; 694 | 695 | ostm.escape_and_output(os_temp.str()); 696 | 697 | ostm.set_after_newline(false); 698 | 699 | return ostm; 700 | } 701 | template 702 | csv::ostringstream& operator << (csv::ostringstream& ostm, const T* val) 703 | { 704 | if (!ostm.get_after_newline()) 705 | ostm.get_ostringstream() << ostm.get_delimiter(); 706 | 707 | std::ostringstream os_temp; 708 | 709 | os_temp << val; 710 | 711 | ostm.escape_and_output(os_temp.str()); 712 | 713 | ostm.set_after_newline(false); 714 | 715 | return ostm; 716 | } 717 | template<> 718 | inline csv::ostringstream& operator << (csv::ostringstream& ostm, const std::string& val) 719 | { 720 | if (!ostm.get_after_newline()) 721 | ostm.get_ostringstream() << ostm.get_delimiter(); 722 | 723 | std::string temp = val; 724 | ostm.escape_str_and_output(temp); 725 | 726 | ostm.set_after_newline(false); 727 | 728 | return ostm; 729 | } 730 | 731 | template<> 732 | inline csv::ostringstream& operator << (csv::ostringstream& ostm, const char& val) 733 | { 734 | if (val == NEWLINE) 735 | { 736 | ostm.get_ostringstream() << NEWLINE; 737 | 738 | ostm.set_after_newline(true); 739 | } 740 | else 741 | { 742 | std::ostringstream os_temp; 743 | 744 | os_temp << val; 745 | 746 | ostm.escape_and_output(os_temp.str()); 747 | } 748 | 749 | return ostm; 750 | } 751 | template<> 752 | inline csv::ostringstream& operator << (csv::ostringstream& ostm, const char* val) 753 | { 754 | const std::string temp = val; 755 | 756 | ostm << temp; 757 | 758 | return ostm; 759 | } 760 | 761 | 762 | #endif // MiniCSV_H -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | project(myxrm) 4 | 5 | set(SOURCE_HEADERS 6 | MicroCore.h 7 | tools.h 8 | monero_headers.h 9 | tx_details.h) 10 | 11 | set(SOURCE_FILES 12 | MicroCore.cpp 13 | tools.cpp 14 | CmdLineOptions.cpp 15 | tx_details.cpp) 16 | 17 | # make static library called libmyxrm 18 | # that we are going to link to 19 | # in the root CMakeLists.txt file 20 | add_library(myxrm 21 | STATIC 22 | ${SOURCE_FILES}) 23 | -------------------------------------------------------------------------------- /src/CmdLineOptions.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by mwo on 6/11/15. 3 | // 4 | 5 | #include "CmdLineOptions.h" 6 | 7 | 8 | namespace xmreg 9 | { 10 | /** 11 | * Take the acc and *avv[] from the main() and check and parse 12 | * all the options given 13 | */ 14 | CmdLineOptions::CmdLineOptions(int acc, const char *avv[]) { 15 | 16 | positional_options_description p; 17 | 18 | options_description desc( 19 | "xmr2csv, export all your transactions into csv file"); 20 | 21 | desc.add_options() 22 | ("help,h", value()->default_value(false)->implicit_value(true), 23 | "produce help message") 24 | ("address,a", value(), 25 | "monero address string") 26 | ("viewkey,v", value(), 27 | "private view key string") 28 | ("spendkey,s", value(), 29 | "private spend key string") 30 | ("start-height,t", value(), 31 | "start from given height") 32 | ("stop-height,g", value()->default_value(0), 33 | "stop scan at the given height. 0 is scan till end") 34 | ("no-of-blocks,n", value()->default_value(0), 35 | "number of blocks to search starting from start-height (0 = all blocks)") 36 | ("start-date,d", value(), 37 | "start roughly from given date: yyyy-mm-dd") 38 | ("out-csv-file,c", value()->default_value("xmr_report.csv"), 39 | "name of outputs csv file") 40 | ("out-csv-file2,r", value()->default_value("xmr_report_ring_members.csv"), 41 | "name of outputs csv file for file containing out outputs as ring members") 42 | ("out-csv-file3,r", value()->default_value("xmr_report_ring_members_freq.csv"), 43 | "name of outputs csv file for file containing frequencies of outputs as ring members") 44 | ("out-csv-file4,r", value()->default_value("xmr_report_real_outputs_in_key_images.csv"), 45 | "name of outputs csv file for file containing all key " 46 | "images scanned with the referenced output public keys") 47 | ("out-csv-file5,r", value()->default_value("xmr_report_outgoing_txs.csv"), 48 | "locate outgoing transactions with marked real mixin in key images. " 49 | "This requires spend key.") 50 | ("bc-path,b", value(), 51 | "path to lmdb blockchain") 52 | ("testnet", value()->default_value(false)->implicit_value(true), 53 | "is the address from testnet network") 54 | ("stagenet", value()->default_value(false)->implicit_value(true), 55 | "is the address from stagenet network") 56 | ("ring-members,m", value()->default_value(false)->implicit_value(true), 57 | "search where our outputs are as ring members") 58 | ("all-outputs", value()->default_value(false)->implicit_value(true), 59 | "save all outputs, whether they are ours or not") 60 | ("all-key-images", value()->default_value(false)->implicit_value(true), 61 | "save all key_images, whether they are ours or not, with referenced output public keys"); 62 | 63 | 64 | store(command_line_parser(acc, avv) 65 | .options(desc) 66 | .run(), vm); 67 | 68 | notify(vm); 69 | 70 | if (vm.count("help")) 71 | { 72 | if (vm["help"].as()) 73 | cout << desc << "\n"; 74 | } 75 | 76 | 77 | } 78 | 79 | /** 80 | * Return the value of the argument passed to the program 81 | * in wrapped around boost::optional 82 | */ 83 | template 84 | boost::optional 85 | CmdLineOptions::get_option(const string & opt_name) const 86 | { 87 | 88 | if (!vm.count(opt_name)) 89 | { 90 | return boost::none; 91 | } 92 | 93 | return vm[opt_name].as(); 94 | } 95 | 96 | 97 | // explicit instantiations of get_option template function 98 | template boost::optional 99 | CmdLineOptions::get_option(const string & opt_name) const; 100 | 101 | template boost::optional 102 | CmdLineOptions::get_option(const string & opt_name) const; 103 | 104 | template boost::optional 105 | CmdLineOptions::get_option(const string & opt_name) const; 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/CmdLineOptions.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by mwo on 6/11/15. 3 | // 4 | 5 | #ifndef XMREG01_CMDLINEOPTIONS_H 6 | #define XMREG01_CMDLINEOPTIONS_H 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | namespace xmreg 15 | { 16 | 17 | using namespace std; 18 | using namespace boost::program_options; 19 | 20 | /** 21 | * Manages program options of this example program. 22 | * 23 | * Basically a wrapper for boost::program_options 24 | */ 25 | class CmdLineOptions { 26 | variables_map vm; 27 | 28 | public: 29 | CmdLineOptions(int acc, const char *avv[]); 30 | 31 | template 32 | boost::optional get_option(const string & opt_name) const; 33 | }; 34 | } 35 | 36 | 37 | #endif //XMREG01_CMDLINEOPTIONS_H 38 | -------------------------------------------------------------------------------- /src/MicroCore.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by mwo on 5/11/15. 3 | // 4 | 5 | #include "MicroCore.h" 6 | 7 | 8 | namespace xmreg 9 | { 10 | /** 11 | * The constructor is interesting, as 12 | * m_mempool and m_blockchain_storage depend 13 | * on each other. 14 | * 15 | * So basically m_mempool is initialized with 16 | * reference to Blockchain (i.e., Blockchain&) 17 | * and m_blockchain_storage is initialized with 18 | * reference to m_mempool (i.e., tx_memory_pool&) 19 | * 20 | * The same is done in cryptonode::core. 21 | */ 22 | MicroCore::MicroCore(): 23 | m_mempool(m_blockchain_storage), 24 | m_blockchain_storage(m_mempool) 25 | { 26 | m_device = &hw::get_device("default"); 27 | } 28 | 29 | 30 | /** 31 | * Initialized the MicroCore object. 32 | * 33 | * Create BlockchainLMDB on the heap. 34 | * Open database files located in blockchain_path. 35 | * Initialize m_blockchain_storage with the BlockchainLMDB object. 36 | */ 37 | bool 38 | MicroCore::init(const string& _blockchain_path, network_type nt) 39 | { 40 | int db_flags = 0; 41 | 42 | blockchain_path = _blockchain_path; 43 | 44 | nettype = nt; 45 | 46 | db_flags |= MDB_RDONLY; 47 | db_flags |= MDB_NOLOCK; 48 | 49 | BlockchainDB* db = nullptr; 50 | db = new BlockchainLMDB(); 51 | 52 | try 53 | { 54 | // try opening lmdb database files 55 | db->open(blockchain_path, db_flags); 56 | } 57 | catch (const std::exception& e) 58 | { 59 | cerr << "Error opening database: " << e.what(); 60 | return false; 61 | } 62 | 63 | // check if the blockchain database 64 | // is successful opened 65 | if(!db->is_open()) 66 | return false; 67 | 68 | // initialize Blockchain object to manage 69 | // the database. 70 | return m_blockchain_storage.init(db, nettype); 71 | } 72 | 73 | /** 74 | * Get m_blockchain_storage. 75 | * Initialize m_blockchain_storage with the BlockchainLMDB object. 76 | */ 77 | Blockchain& 78 | MicroCore::get_core() 79 | { 80 | return m_blockchain_storage; 81 | } 82 | 83 | /** 84 | * Get block by its height 85 | * 86 | * returns true if success 87 | */ 88 | bool 89 | MicroCore::get_block_by_height(const uint64_t& height, block& blk) 90 | { 91 | try 92 | { 93 | blk = m_blockchain_storage.get_db().get_block_from_height(height); 94 | } 95 | catch (const BLOCK_DNE& e) 96 | { 97 | cerr << "Block of height " << height 98 | << " not found in the blockchain!" 99 | << e.what() 100 | << endl; 101 | 102 | return false; 103 | } 104 | catch (const DB_ERROR& e) 105 | { 106 | cerr << "Blockchain access error when getting block " << height 107 | << e.what() 108 | << endl; 109 | 110 | return false; 111 | } 112 | catch (...) 113 | { 114 | cerr << "Something went terribly wrong when getting block " << height 115 | << endl; 116 | 117 | return false; 118 | } 119 | 120 | return true; 121 | } 122 | 123 | /** 124 | * Finds the first block created in a given day, e.g., 2015-05-22 125 | * 126 | * 127 | */ 128 | bool 129 | MicroCore::get_block_by_date(const string& date, /* searched date */ 130 | block& blk, /* block to be returned */ 131 | uint64_t init_height, /* start looking from this height */ 132 | const char* format) 133 | { 134 | 135 | // get the current blockchain height. 136 | uint64_t max_height = m_blockchain_storage.get_current_blockchain_height(); 137 | 138 | if (init_height > max_height) 139 | { 140 | cerr << "Initial height higher than current blockchain height!" << endl; 141 | return false; 142 | } 143 | 144 | 145 | // first parse the string date into boost's ptime object 146 | dateparser parser {format}; 147 | 148 | if (!parser(date)) 149 | { 150 | throw runtime_error(string("Date format is incorrect: ") + date); 151 | } 152 | 153 | // change the requested date ptime into timestamp 154 | uint64_t searched_timestamp = static_cast(xmreg::to_time_t(parser.pt)); 155 | 156 | //cout << "searched_timestamp: " << searched_timestamp << endl; 157 | 158 | // get block at initial guest height 159 | block tmp_blk; 160 | 161 | if (!get_block_by_height(init_height, tmp_blk)) 162 | { 163 | cerr << "Cant find block of height " << init_height << endl; 164 | return false; 165 | } 166 | 167 | // assume the initall block is correct 168 | blk = tmp_blk; 169 | 170 | // get timestamp of the initial block 171 | //cout << tmp_blk.timestamp << ", " << searched_timestamp << endl; 172 | 173 | // if init block and time do not match, do iterative search for the correct block 174 | 175 | // if found init block was earlier than the search time, iterate increasing block heigths 176 | if (tmp_blk.timestamp < searched_timestamp) 177 | { 178 | for (uint64_t i = init_height + 1; i < max_height; ++i) 179 | { 180 | block tmp_blk2; 181 | if (!get_block_by_height(i, tmp_blk2)) 182 | { 183 | cerr << "Cant find block of height " << i << endl; 184 | return false; 185 | } 186 | 187 | //cout << tmp_blk2.timestamp - searched_timestamp << endl; 188 | 189 | if (tmp_blk2.timestamp >= searched_timestamp) 190 | { 191 | 192 | // take one before this one: 193 | 194 | if (!get_block_by_height(--i, tmp_blk2)) 195 | { 196 | cerr << "Cant find block of height " << i << endl; 197 | return false; 198 | } 199 | 200 | blk = tmp_blk2; 201 | break; 202 | } 203 | 204 | } 205 | } 206 | else 207 | { 208 | for (uint64_t i = init_height - 1; i >= 0; --i) 209 | { 210 | block tmp_blk2; 211 | if (!get_block_by_height(i, tmp_blk2)) 212 | { 213 | cerr << "Cant find block of height " << i << endl; 214 | return false; 215 | } 216 | 217 | //cout << tmp_blk2.timestamp - searched_timestamp << endl; 218 | 219 | if (tmp_blk2.timestamp <= searched_timestamp) 220 | { 221 | blk = tmp_blk2; 222 | break; 223 | } 224 | } 225 | } 226 | 227 | return true; 228 | } 229 | 230 | /** 231 | * Get transaction tx from the blockchain using it hash 232 | */ 233 | bool 234 | MicroCore::get_tx(const crypto::hash& tx_hash, transaction& tx) 235 | { 236 | if (m_blockchain_storage.have_tx(tx_hash)) 237 | { 238 | // get transaction with given hash 239 | tx = m_blockchain_storage.get_db().get_tx(tx_hash); 240 | } 241 | else 242 | { 243 | cerr << "MicroCore::get_tx tx does not exist in blockchain: " 244 | << epee::string_tools::pod_to_hex(tx_hash) << endl; 245 | return false; 246 | } 247 | 248 | 249 | return true; 250 | } 251 | 252 | bool 253 | MicroCore::get_tx(const string& tx_hash_str, transaction& tx) 254 | { 255 | 256 | // parse tx hash string to hash object 257 | crypto::hash tx_hash; 258 | 259 | if (!xmreg::parse_str_secret_key(tx_hash_str, tx_hash)) 260 | { 261 | cerr << "Cant parse tx hash: " << tx_hash_str << endl; 262 | return false; 263 | } 264 | 265 | 266 | if (!get_tx(tx_hash, tx)) 267 | { 268 | return false; 269 | } 270 | 271 | 272 | return true; 273 | } 274 | 275 | 276 | 277 | 278 | /** 279 | * Find output with given public key in a given transaction 280 | */ 281 | bool 282 | MicroCore::find_output_in_tx(const transaction& tx, 283 | const public_key& output_pubkey, 284 | tx_out& out, 285 | size_t& output_index) 286 | { 287 | 288 | size_t idx {0}; 289 | 290 | 291 | // search in the ouputs for an output which 292 | // public key matches to what we want 293 | auto it = std::find_if(tx.vout.begin(), tx.vout.end(), 294 | [&](const tx_out& o) 295 | { 296 | const txout_to_key& tx_in_to_key 297 | = boost::get(o.target); 298 | 299 | ++idx; 300 | 301 | return tx_in_to_key.key == output_pubkey; 302 | }); 303 | 304 | if (it != tx.vout.end()) 305 | { 306 | // we found the desired public key 307 | out = *it; 308 | output_index = idx > 0 ? idx - 1 : idx; 309 | 310 | //cout << idx << " " << output_index << endl; 311 | 312 | return true; 313 | } 314 | 315 | return false; 316 | } 317 | 318 | 319 | 320 | 321 | uint64_t 322 | MicroCore::get_blk_timestamp(uint64_t blk_height) 323 | { 324 | cryptonote::block blk; 325 | 326 | if (!get_block_by_height(blk_height, blk)) 327 | { 328 | cerr << "Cant get block by height: " << blk_height << endl; 329 | return 0; 330 | } 331 | 332 | return blk.timestamp; 333 | } 334 | 335 | 336 | /** 337 | * De-initialized Blockchain. 338 | * 339 | * since blockchain is opened as MDB_RDONLY 340 | * need to manually free memory taken on heap 341 | * by BlockchainLMDB 342 | */ 343 | //MicroCore::~MicroCore() 344 | //{ 345 | // //m_blockchain_storage.get_db().close(); 346 | // delete &m_blockchain_storage.get_db(); 347 | //} 348 | 349 | 350 | bool 351 | init_blockchain(const string& path, 352 | MicroCore& mcore, 353 | Blockchain*& core_storage, 354 | network_type nt) 355 | { 356 | 357 | // initialize the core using the blockchain path 358 | if (!mcore.init(path, nt)) 359 | { 360 | cerr << "Error accessing blockchain." << endl; 361 | return false; 362 | } 363 | 364 | // get the high level Blockchain object to interact 365 | // with the blockchain lmdb database 366 | core_storage = &(mcore.get_core()); 367 | 368 | return true; 369 | } 370 | 371 | string 372 | MicroCore::get_blkchain_path() 373 | { 374 | return blockchain_path; 375 | } 376 | 377 | hw::device* const 378 | MicroCore::get_device() const 379 | { 380 | return m_device; 381 | } 382 | 383 | } 384 | -------------------------------------------------------------------------------- /src/MicroCore.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by mwo on 5/11/15. 3 | // 4 | 5 | #ifndef XMREG01_MICROCORE_H 6 | #define XMREG01_MICROCORE_H 7 | 8 | #include 9 | 10 | #include "monero_headers.h" 11 | #include "tools.h" 12 | 13 | namespace xmreg 14 | { 15 | using namespace cryptonote; 16 | using namespace crypto; 17 | using namespace std; 18 | 19 | /** 20 | * Micro version of cryptonode::core class 21 | * Micro version of constructor, 22 | * init and destructor are implemted. 23 | * 24 | * Just enough to read the blockchain 25 | * database for use in the example. 26 | */ 27 | class MicroCore { 28 | 29 | string blockchain_path; 30 | 31 | tx_memory_pool m_mempool; 32 | Blockchain m_blockchain_storage; 33 | 34 | hw::device* m_device; 35 | 36 | network_type nettype; 37 | 38 | public: 39 | MicroCore(); 40 | 41 | bool 42 | init(const string& _blockchain_path, network_type nt); 43 | 44 | Blockchain& 45 | get_core(); 46 | 47 | bool 48 | get_block_by_height(const uint64_t& height, block& blk); 49 | 50 | bool 51 | get_block_by_date(const string& date, /* searched date */ 52 | block& blk, /* block to be returned */ 53 | uint64_t init_height = 0, /* start looking from this height */ 54 | const char* format = "%Y-%m-%d"); 55 | 56 | bool 57 | get_tx(const crypto::hash& tx_hash, transaction& tx); 58 | 59 | bool 60 | get_tx(const string& tx_hash, transaction& tx); 61 | 62 | bool 63 | find_output_in_tx(const transaction& tx, 64 | const public_key& output_pubkey, 65 | tx_out& out, 66 | size_t& output_index); 67 | 68 | uint64_t 69 | get_blk_timestamp(uint64_t blk_height); 70 | 71 | string 72 | get_blkchain_path(); 73 | 74 | hw::device* const 75 | get_device() const; 76 | 77 | // virtual ~MicroCore(); 78 | }; 79 | 80 | 81 | 82 | bool 83 | init_blockchain(const string& path, 84 | MicroCore& mcore, 85 | Blockchain*& core_storage, 86 | network_type nt); 87 | 88 | 89 | } 90 | 91 | 92 | 93 | #endif //XMREG01_MICROCORE_H 94 | -------------------------------------------------------------------------------- /src/monero_headers.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by mwo on 5/11/15. 3 | // 4 | 5 | #ifndef XMREG01_MONERO_HEADERS_H_H 6 | #define XMREG01_MONERO_HEADERS_H_H 7 | 8 | #define DB_LMDB 2 9 | #define BLOCKCHAIN_DB DB_LMDB 10 | 11 | 12 | #define UNSIGNED_TX_PREFIX "Monero unsigned tx set\003" 13 | #define SIGNED_TX_PREFIX "Monero signed tx set\003" 14 | #define KEY_IMAGE_EXPORT_FILE_MAGIC "Monero key image export\002" 15 | #define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\003" 16 | 17 | 18 | #ifdef MONERO_VERSION_VERSION 19 | #include "release/version/version.h" 20 | #else 21 | #include "version.h" 22 | #endif 23 | 24 | #include "net/http_client.h" 25 | #include "storages/http_abstract_invoke.h" 26 | 27 | #include "cryptonote_core/tx_pool.h" 28 | #include "cryptonote_core/blockchain.h" 29 | #include "blockchain_db/lmdb/db_lmdb.h" 30 | 31 | #include "wallet/wallet2.h" 32 | 33 | #include "serialization/binary_utils.h" 34 | 35 | #include "ringct/rctTypes.h" 36 | #include "ringct/rctOps.h" 37 | #include "ringct/rctSigs.h" 38 | 39 | #include "easylogging++.h" 40 | 41 | #include "common/base58.h" 42 | 43 | #include "string_coding.h" 44 | 45 | 46 | #endif //XMREG01_MONERO_HEADERS_H_H 47 | 48 | -------------------------------------------------------------------------------- /src/tools.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by mwo on 5/11/15. 3 | // 4 | 5 | #ifndef XMREG01_TOOLS_H 6 | #define XMREG01_TOOLS_H 7 | 8 | #define PATH_SEPARARTOR '/' 9 | 10 | #define XMR_AMOUNT(value) \ 11 | static_cast(value) / 1e12 12 | 13 | #define REMOVE_HASH_BRAKETS(a_hash) \ 14 | a_hash.substr(1, a_hash.size()-2) 15 | 16 | #include "monero_headers.h" 17 | #include "tx_details.h" 18 | 19 | #include "../ext/dateparser.h" 20 | #include "../ext/infix_iterator.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | /** 34 | * Some helper functions used in the example. 35 | * Names are rather self-explanatory, so I think 36 | * there is no reason for any detailed explanations here 37 | */ 38 | namespace xmreg 39 | { 40 | using namespace cryptonote; 41 | using namespace crypto; 42 | using namespace std; 43 | 44 | namespace bf = boost::filesystem; 45 | namespace pt = boost::posix_time; 46 | namespace gt = boost::gregorian; 47 | 48 | 49 | 50 | struct outputs_visitor 51 | { 52 | std::vector& m_output_keys; 53 | 54 | const Blockchain& m_bch; 55 | 56 | outputs_visitor(std::vector& output_keys, const Blockchain& bch) : 57 | m_output_keys(output_keys), m_bch(bch) 58 | { 59 | } 60 | 61 | bool handle_output(uint64_t unlock_time, const crypto::public_key &pubkey) 62 | { 63 | //check tx unlock time 64 | // if (!m_bch.is_tx_spendtime_unlocked(unlock_time)) 65 | // { 66 | // LOG_PRINT_L1("One of outputs for one of inputs has wrong tx.unlock_time = " << unlock_time); 67 | // return false; 68 | // } 69 | 70 | m_output_keys.push_back(pubkey); 71 | 72 | return true; 73 | } 74 | }; 75 | 76 | 77 | template 78 | bool 79 | parse_str_secret_key(const string& key_str, T& secret_key); 80 | 81 | 82 | bool 83 | get_tx_pub_key_from_str_hash(Blockchain& core_storage, 84 | const string& hash_str, 85 | transaction& tx); 86 | 87 | inline bool 88 | is_separator(char c); 89 | 90 | string 91 | print_sig (const signature& sig); 92 | 93 | string 94 | remove_trailing_path_separator(const string& in_path); 95 | 96 | bf::path 97 | remove_trailing_path_separator(const bf::path& in_path); 98 | 99 | string 100 | timestamp_to_str(time_t timestamp, const char* format = "%F %T"); 101 | 102 | std::string my_get_account_address_as_str( 103 | network_type nettype, account_public_address const & adr); 104 | 105 | // ostream& 106 | // operator<< (ostream& os, const address_parse_info& addr_info); 107 | 108 | 109 | string 110 | get_default_lmdb_folder(cryptonote::network_type nettype = cryptonote::network_type::MAINNET); 111 | 112 | bool 113 | generate_key_image(const crypto::key_derivation& derivation, 114 | const std::size_t output_index, 115 | const crypto::secret_key& sec_key, 116 | const crypto::public_key& pub_key, 117 | crypto::key_image& key_img); 118 | 119 | bool 120 | get_blockchain_path(const boost::optional& bc_path, 121 | bf::path& blockchain_path, 122 | cryptonote::network_type nettype = cryptonote::network_type::MAINNET); 123 | 124 | uint64_t 125 | sum_money_in_outputs(const transaction& tx); 126 | 127 | uint64_t 128 | sum_money_in_inputs(const transaction& tx); 129 | 130 | array 131 | sum_money_in_tx(const transaction& tx); 132 | 133 | array 134 | sum_money_in_txs(const vector& txs); 135 | 136 | uint64_t 137 | sum_fees_in_txs(const vector& txs); 138 | 139 | uint64_t 140 | get_mixin_no(const transaction& tx); 141 | 142 | vector 143 | get_mixin_no_in_txs(const vector& txs); 144 | 145 | vector> 146 | get_ouputs(const transaction& tx); 147 | 148 | vector> 149 | get_ouputs_tuple(const transaction& tx); 150 | 151 | vector 152 | get_key_images(const transaction& tx); 153 | 154 | 155 | bool 156 | get_payment_id(const vector& extra, 157 | crypto::hash& payment_id, 158 | crypto::hash8& payment_id8); 159 | 160 | bool 161 | get_payment_id(const transaction& tx, 162 | crypto::hash& payment_id, 163 | crypto::hash8& payment_id8); 164 | 165 | uint64_t 166 | estimate_bc_height(const string& date, const char* format = "%Y-%m-%d"); 167 | 168 | 169 | inline double 170 | get_xmr(uint64_t core_amount) 171 | { 172 | return static_cast(core_amount) / 1e12; 173 | } 174 | 175 | array 176 | timestamp_difference(uint64_t t1, uint64_t t2); 177 | 178 | string 179 | read(string filename); 180 | 181 | 182 | 183 | /** 184 | * prints an iterable such as vector 185 | */ 186 | template 187 | void print_iterable(const T & elems) { 188 | 189 | infix_ostream_iterator 190 | oiter(std::cout, ","); 191 | 192 | std::cout << "["; 193 | std::copy(elems.begin(), elems.end(),oiter); 194 | std::cout << "]" << std::endl; 195 | } 196 | 197 | pair 198 | timestamps_time_scale(const vector& timestamps, 199 | uint64_t timeN, uint64_t resolution = 80, 200 | uint64_t time0 = 1397818193 /* timestamp of the second block */); 201 | 202 | 203 | bool 204 | decode_ringct(const rct::rctSig & rv, 205 | const crypto::public_key& pub, 206 | const crypto::secret_key& sec, 207 | unsigned int i, 208 | rct::key & mask, 209 | uint64_t & amount); 210 | 211 | bool 212 | url_decode(const std::string& in, std::string& out); 213 | 214 | map 215 | parse_crow_post_data(const string& req_body); 216 | 217 | time_t 218 | to_time_t(pt::ptime t); 219 | 220 | // based on 221 | // crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const 222 | public_key 223 | get_tx_pub_key_from_received_outs(const transaction &tx); 224 | 225 | string 226 | print_address(const address_parse_info& address, 227 | cryptonote::network_type nettype = cryptonote::network_type::MAINNET); 228 | 229 | } 230 | 231 | #endif //XMREG01_TOOLS_H 232 | -------------------------------------------------------------------------------- /src/tx_details.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by mwo on 14/11/15. 3 | // 4 | 5 | #include "tx_details.h" 6 | 7 | 8 | namespace xmreg 9 | { 10 | 11 | 12 | 13 | crypto::hash 14 | transfer_details::tx_hash() const 15 | { 16 | return get_transaction_hash(m_tx); 17 | }; 18 | 19 | 20 | public_key 21 | transfer_details::tx_pub_key() const 22 | { 23 | return get_tx_pub_key_from_extra(m_tx); 24 | }; 25 | 26 | 27 | uint64_t 28 | transfer_details::amount() const 29 | { 30 | uint64_t xmr_amount = m_tx.vout[m_internal_output_index].amount; 31 | 32 | return xmr_amount; 33 | } 34 | 35 | 36 | uint64_t 37 | transfer_details::amount(secret_key prv_view_key) const 38 | { 39 | uint64_t xmr_amount = m_tx.vout[m_internal_output_index].amount; 40 | 41 | // cointbase txs have amounts in plain sight. 42 | // so use amount from ringct, only for non-coinbase txs 43 | if (m_tx.version > 1 && !is_coinbase(m_tx)) 44 | { 45 | uint64_t rct_amount = 0; 46 | 47 | bool r; 48 | 49 | public_key addr_pub_view_key = m_addr.m_view_public_key; 50 | 51 | uint64_t out_idx = m_internal_output_index; 52 | 53 | rct::key mask = m_tx.rct_signatures.ecdhInfo[out_idx].mask; 54 | 55 | r = decode_ringct(m_tx.rct_signatures, 56 | get_tx_pub_key_from_extra(m_tx), 57 | prv_view_key, 58 | out_idx, 59 | mask, 60 | rct_amount); 61 | 62 | if (!r) 63 | { 64 | cerr << "Cant decode ringCT!" << endl; 65 | } 66 | 67 | xmr_amount = rct_amount; 68 | } 69 | 70 | return xmr_amount; 71 | } 72 | 73 | 74 | 75 | ostream& 76 | operator<<(ostream& os, const transfer_details& td) 77 | { 78 | os << "Block: " << td.m_block_height 79 | << " time: " << timestamp_to_str(td.m_block_timestamp) 80 | << " tx hash: " << td.tx_hash() 81 | << " tx pub key: " << td.tx_pub_key() 82 | << " out idx: " << td.m_internal_output_index 83 | << " out pk: " << td.out_pub_key 84 | << " key img: " << td.key_img 85 | << " amount: " << td.m_amount; //print_money(td.amount()); 86 | 87 | return os; 88 | } 89 | 90 | 91 | 92 | 93 | /** 94 | * Get tx outputs associated with the given private view and public spend keys 95 | * 96 | * 97 | */ 98 | vector 99 | get_belonging_outputs(const block& blk, 100 | const transaction& tx, 101 | const account_public_address& addr, 102 | const secret_key& private_view_key, 103 | uint64_t block_height) 104 | { 105 | // vector to be returned 106 | vector our_outputs; 107 | 108 | 109 | // get transaction's public key 110 | public_key pub_tx_key = get_tx_pub_key_from_received_outs(tx); 111 | 112 | // check if transaction has valid public key 113 | // if no, then skip 114 | if (pub_tx_key == null_pkey) 115 | { 116 | return our_outputs; 117 | } 118 | 119 | 120 | // get tx payment id 121 | crypto::hash payment_id; 122 | 123 | if (!xmreg::get_payment_id(tx, payment_id)) 124 | { 125 | payment_id = null_hash; 126 | } 127 | 128 | // get the total number of outputs in a transaction. 129 | size_t output_no = tx.vout.size(); 130 | 131 | // check if the given transaction has any outputs 132 | // if no, then finish 133 | if (output_no == 0) 134 | { 135 | return our_outputs; 136 | } 137 | 138 | 139 | // public transaction key is combined with our viewkey 140 | // to create, so called, derived key. 141 | key_derivation derivation; 142 | 143 | if (!generate_key_derivation(pub_tx_key, private_view_key, derivation)) 144 | { 145 | cerr << "Cant get dervied key for: " << "\n" 146 | << "pub_tx_key: " << private_view_key << " and " 147 | << "prv_view_key" << private_view_key << endl; 148 | return our_outputs; 149 | } 150 | 151 | 152 | // each tx that we (or the address we are checking) received 153 | // contains a number of outputs. 154 | // some of them are ours, some not. so we need to go through 155 | // all of them in a given tx block, to check which outputs are ours. 156 | 157 | 158 | 159 | // sum amount of xmr sent to us 160 | // in the given transaction 161 | uint64_t money_transfered {0}; 162 | 163 | // loop through outputs in the given tx 164 | // to check which outputs our ours. we compare outputs' 165 | // public keys with the public key that would had been 166 | // generated for us if we had gotten the outputs. 167 | // not sure this is the case though, but that's my understanding. 168 | for (size_t i = 0; i < output_no; ++i) 169 | { 170 | // get the tx output public key 171 | // that normally would be generated for us, 172 | // if someone had sent us some xmr. 173 | public_key pubkey; 174 | 175 | derive_public_key(derivation, 176 | i, 177 | addr.m_spend_public_key, 178 | pubkey); 179 | 180 | public_key output_pub_key; 181 | 182 | if (!cryptonote::get_output_public_key(tx.vout[i], output_pub_key)) 183 | { 184 | continue; 185 | } 186 | 187 | // check if generated public key matches the current output's key 188 | //bool mine_output = (tx_out_to_key.key == pubkey); 189 | bool mine_output = (output_pub_key == pubkey); 190 | 191 | // check if the output's public key is ours 192 | if (mine_output) 193 | { 194 | uint64_t xmr_amount = tx.vout[i].amount; 195 | 196 | // if mine output has RingCT, i.e., tx version is 2 197 | // need to decode its amount. otherwise its zero. 198 | if (mine_output && tx.version > 1) 199 | { 200 | // initialize with regular amount value 201 | // for ringct, except coinbase, it will be 0 202 | uint64_t rct_amount_val = xmr_amount; 203 | 204 | if (!is_coinbase(tx)) 205 | { 206 | bool r; 207 | 208 | rct::key mask = tx.rct_signatures.ecdhInfo[i].mask; 209 | 210 | r = decode_ringct(tx.rct_signatures, 211 | pub_tx_key, 212 | private_view_key, 213 | i, 214 | mask, 215 | rct_amount_val); 216 | 217 | if (!r) { 218 | cerr << "Cant decode ringCT!" << endl; 219 | throw std::runtime_error("Cant decode ringCT!"); 220 | } 221 | 222 | xmr_amount = rct_amount_val; 223 | } 224 | 225 | } // if (mine_output && tx.version > 1) 226 | 227 | 228 | // if so, then add this output to the 229 | // returned vector 230 | //our_outputs.push_back(tx.vout[i]); 231 | our_outputs.push_back( 232 | xmreg::transfer_details {addr, 233 | block_height, 234 | blk.timestamp, 235 | xmr_amount, 236 | tx, 237 | payment_id, 238 | i, 239 | output_pub_key, 240 | key_image{}, 241 | false} 242 | ); 243 | 244 | xmreg::transfer_details& last = our_outputs.back(); 245 | 246 | last.m_amount = last.amount(private_view_key); 247 | 248 | } // if (mine_output) 249 | 250 | } // for (size_t i = 0; i < output_no; ++i) 251 | 252 | return our_outputs; 253 | } 254 | 255 | 256 | 257 | /** 258 | * Get tx outputs associated with the given private view and public spend keys 259 | * 260 | * 261 | */ 262 | vector 263 | get_outputs(const block& blk, 264 | const transaction& tx, 265 | uint64_t block_height) 266 | { 267 | // vector to be returned 268 | vector our_outputs; 269 | 270 | 271 | // get transaction's public key 272 | public_key pub_tx_key = get_tx_pub_key_from_extra(tx); 273 | 274 | // check if transaction has valid public key 275 | // if no, then skip 276 | if (pub_tx_key == null_pkey) 277 | { 278 | return our_outputs; 279 | } 280 | 281 | 282 | 283 | // get tx payment id 284 | crypto::hash payment_id; 285 | 286 | if (!xmreg::get_payment_id(tx, payment_id)) 287 | { 288 | payment_id = null_hash; 289 | } 290 | 291 | // get the total number of outputs in a transaction. 292 | size_t output_no = tx.vout.size(); 293 | 294 | // check if the given transaction has any outputs 295 | // if no, then finish 296 | if (output_no == 0) 297 | { 298 | return our_outputs; 299 | } 300 | 301 | // sum amount of xmr sent to us 302 | // in the given transaction 303 | uint64_t money_transfered {0}; 304 | 305 | // loop through outputs in the given tx 306 | // to check which outputs our ours. we compare outputs' 307 | // public keys with the public key that would had been 308 | // generated for us if we had gotten the outputs. 309 | // not sure this is the case though, but that's my understanding. 310 | for (size_t i = 0; i < output_no; ++i) 311 | { 312 | 313 | public_key output_pub_key; 314 | cryptonote::get_output_public_key(tx.vout[i], output_pub_key); 315 | 316 | // if so, then add this output to the 317 | // returned vector 318 | //our_outputs.push_back(tx.vout[i]); 319 | our_outputs.push_back( 320 | xmreg::transfer_details {account_public_address(), 321 | block_height, 322 | blk.timestamp, 323 | tx.vout[i].amount, 324 | tx, 325 | payment_id, 326 | i, 327 | output_pub_key, 328 | key_image{}, 329 | false} 330 | ); 331 | } 332 | 333 | return our_outputs; 334 | } 335 | 336 | 337 | bool 338 | get_payment_id(const transaction& tx, 339 | const account_public_address& addr, 340 | crypto::hash& payment_id) 341 | { 342 | 343 | payment_id = null_hash; 344 | 345 | //crypto::secret_key secret_key = get_tx_pub_key_from_extra(tx); 346 | 347 | std::vector tx_extra_fields; 348 | 349 | if(!parse_tx_extra(tx.extra, tx_extra_fields)) 350 | { 351 | return false; 352 | } 353 | 354 | tx_extra_nonce extra_nonce; 355 | 356 | if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) 357 | { 358 | // crypto::hash8 payment_id8 = null_hash8; 359 | // 360 | // if(get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8)) 361 | // { 362 | // if (decrypt_payment_id(payment_id8, addr.m_view_public_key, ptx.tx_key)) 363 | // { 364 | // memcpy(payment_id.data, payment_id8.data, 8); 365 | // } 366 | // } 367 | // else if (!get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) 368 | // { 369 | // payment_id = cryptonote::null_hash; 370 | // } 371 | 372 | if (!get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) 373 | { 374 | return false; 375 | } 376 | } 377 | return true; 378 | } 379 | 380 | bool 381 | get_payment_id(const transaction& tx, 382 | crypto::hash& payment_id) 383 | { 384 | 385 | payment_id = null_hash; 386 | 387 | //crypto::secret_key secret_key = get_tx_pub_key_from_extra(tx); 388 | 389 | std::vector tx_extra_fields; 390 | 391 | if(!parse_tx_extra(tx.extra, tx_extra_fields)) 392 | { 393 | return false; 394 | } 395 | 396 | tx_extra_nonce extra_nonce; 397 | 398 | if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) 399 | { 400 | if (!get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) 401 | { 402 | return false; 403 | } 404 | } 405 | return true; 406 | } 407 | 408 | 409 | } 410 | 411 | template<> 412 | csv::ofstream& 413 | operator<<(csv::ofstream& ostm, const xmreg::transfer_details& td) 414 | { 415 | 416 | std::stringstream ss; 417 | 418 | // get strings to remove "<" and ">" from begining and end of hashes 419 | ss << td.tx_hash(); 420 | std::string tx_hash_str = ss.str(); 421 | 422 | ss.str(std::string()); 423 | 424 | // get tx pub key str 425 | ss << td.tx_pub_key(); 426 | std::string tx_pub_key_str = ss.str(); 427 | 428 | ss.str(std::string()); 429 | 430 | // get strings to remove "<" and ">" from begining and end of hashes 431 | ss << td.payment_id; 432 | std::string payment_id_str = ss.str(); 433 | 434 | ss.str(std::string()); 435 | 436 | // get strings to remove "<" and ">" from begining and end of keys 437 | ss << td.out_pub_key; 438 | std::string out_pk_str = ss.str(); 439 | 440 | ss.str(std::string()); 441 | 442 | // get strings for key_image 443 | ss << td.key_img; 444 | std::string key_img = ss.str(); 445 | 446 | uint64_t fee {0}; 447 | 448 | if (!is_coinbase(td.m_tx)) { 449 | if (td.m_tx.vin.at(0).type() != typeid(cryptonote::txin_gen)) { 450 | fee = get_tx_fee(td.m_tx); 451 | } 452 | } 453 | 454 | ostm << xmreg::timestamp_to_str(td.m_block_timestamp); 455 | ostm << td.m_block_height; 456 | ostm << tx_hash_str.substr(1, tx_hash_str.length()-2); 457 | ostm << tx_pub_key_str.substr(1, tx_hash_str.length()-2); 458 | ostm << td.m_tx.version; 459 | ostm << payment_id_str.substr(1, tx_hash_str.length()-2); 460 | ostm << td.m_internal_output_index; 461 | //ostm << cryptonote::print_money(td.m_amount); 462 | ostm << td.m_amount; 463 | ostm << out_pk_str.substr(1, out_pk_str.length()-2); 464 | ostm << key_img.substr(1, out_pk_str.length()-2); 465 | ostm << td.m_spent; 466 | ostm << fee; 467 | 468 | return ostm; 469 | } 470 | -------------------------------------------------------------------------------- /src/tx_details.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by mwo on 14/11/15. 3 | // 4 | 5 | #ifndef XMR2CSV_TXDATA_H 6 | #define XMR2CSV_TXDATA_H 7 | 8 | 9 | 10 | #include "../ext/minicsv.h" 11 | 12 | #include "monero_headers.h" 13 | #include "tools.h" 14 | 15 | #include 16 | #include 17 | 18 | namespace xmreg 19 | { 20 | 21 | using namespace cryptonote; 22 | using namespace crypto; 23 | using namespace std; 24 | 25 | 26 | struct transfer_details 27 | { 28 | account_public_address m_addr; 29 | uint64_t m_block_height; 30 | uint64_t m_block_timestamp; 31 | uint64_t m_amount; 32 | transaction m_tx; 33 | crypto::hash payment_id; 34 | size_t m_internal_output_index; 35 | public_key out_pub_key; 36 | key_image key_img; 37 | bool m_spent; 38 | 39 | crypto::hash tx_hash() const; 40 | 41 | public_key tx_pub_key() const; 42 | 43 | uint64_t amount() const; 44 | 45 | uint64_t amount(secret_key prv_view_key) const; 46 | }; 47 | 48 | 49 | ostream& 50 | operator<<(ostream& os, const transfer_details& dt); 51 | 52 | 53 | vector 54 | get_belonging_outputs(const block& blk, 55 | const transaction& tx, 56 | const account_public_address& addr, 57 | const secret_key& private_view_key, 58 | uint64_t block_height = 0); 59 | 60 | 61 | vector 62 | get_outputs(const block& blk, 63 | const transaction& tx, 64 | uint64_t block_height = 0); 65 | 66 | bool 67 | get_payment_id(const transaction& tx, 68 | const account_public_address& addr, 69 | crypto::hash& payment_id); 70 | 71 | bool 72 | get_payment_id(const transaction& tx, 73 | crypto::hash& payment_id); 74 | 75 | } 76 | 77 | template<> 78 | csv::ostringstream& 79 | operator<<(csv::ostringstream& ostm, const xmreg::transfer_details& td); 80 | 81 | 82 | #endif //XMR2CSV_TXDATA_H 83 | --------------------------------------------------------------------------------