├── .gitignore ├── CMakeLists.txt ├── Example_Images ├── example_automated_script_that_watches_a_folder.sh ├── example_command_invocation_for_test_input_jpg.sh ├── test_input.jpg ├── test_output_left_page.jpg └── test_output_right_page.jpg ├── LICENSE.md ├── README.md ├── docopt-0.6.2 ├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── LICENSE-Boost-1.0 ├── LICENSE-MIT ├── README.rst ├── docopt-config.cmake ├── docopt.cpp ├── docopt.h ├── docopt_private.h ├── docopt_util.h ├── docopt_value.h ├── examples │ └── naval_fate.cpp ├── main.cpp ├── run_testcase.cpp ├── run_tests.py └── testcases.docopt ├── main.cpp ├── marker.cpp ├── marker.h ├── markers ├── markers.ai ├── markers.pdf ├── markers_for_book_scanner.ai └── markers_for_book_scanner.pdf ├── page.cpp ├── page.h └── voussoir.spec /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | 6 | # Compiled Dynamic libraries 7 | *.so 8 | 9 | # Compiled Static libraries 10 | *.lai 11 | *.la 12 | *.a 13 | 14 | # Ignore SCons-related files 15 | .sconsign.dblite 16 | 17 | # Ignore CMake-related files 18 | CMakeFiles 19 | CMakeCache.txt 20 | cmake_install.cmake 21 | Makefile 22 | 23 | # Ignore OS X related files 24 | .DS_Store 25 | 26 | # Ignore executables 27 | bin 28 | 29 | # Ignore Miscellaneous 30 | scratchpad 31 | 32 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | PROJECT(voussoir CXX) 4 | 5 | set(ROOT ./) 6 | 7 | include_directories(${ROOT}) 8 | 9 | ADD_EXECUTABLE(voussoir main.cpp marker.cpp page.cpp) 10 | 11 | FIND_PACKAGE(OpenCV REQUIRED) 12 | 13 | TARGET_LINK_LIBRARIES(voussoir ${OpenCV_LIBS}) 14 | 15 | ############## 16 | # For getting docopt to work 17 | ############## 18 | 19 | target_compile_definitions(voussoir PRIVATE DOCOPT_HEADER_ONLY=1) # This is added because of an if statement at the bottom of docopt.h -- this enables actually including docopt.cpp from docopt.h. 20 | set(CMAKE_CXX_FLAGS "-std=c++11") # docopt needs this, per https://github.com/Qihoo360/logkafka/issues/1 21 | 22 | ############## 23 | 24 | FILE(COPY ${PROJECT_SOURCE_DIR}/Example_Images/test_input.jpg DESTINATION ${PROJECT_BINARY_DIR}/bin/docs) 25 | FILE(COPY ${PROJECT_SOURCE_DIR}/markers/markers_for_book_scanner.ai DESTINATION ${PROJECT_BINARY_DIR}/bin/docs) 26 | FILE(COPY ${PROJECT_SOURCE_DIR}/markers/markers_for_book_scanner.pdf DESTINATION ${PROJECT_BINARY_DIR}/bin/docs) 27 | 28 | SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) 29 | -------------------------------------------------------------------------------- /Example_Images/example_automated_script_that_watches_a_folder.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ################################ 4 | # Notes 5 | ################################ 6 | 7 | # This script requires that inotifywait be installed. Sometimes inotifywait comes in a larger package called inotify-tools. 8 | 9 | # You can stop the script with Ctrl+C 10 | 11 | ################################ 12 | # End Notes 13 | ################################ 14 | 15 | ################################ 16 | # Settings 17 | ################################ 18 | 19 | # Neither of these path names should have a trailing slash (i.e., a '/' at the end): 20 | directory_to_watch="/home/jacoblevernier/Downloads/test" 21 | output_directory="/home/jacoblevernier/Downloads/test/output" 22 | 23 | program_location="/home/jacoblevernier/Downloads/bookscan/bin/voussoir" 24 | 25 | program_arguments="--verbose" 26 | 27 | ################################ 28 | # End Settings 29 | ################################ 30 | 31 | # Create the output directory if necessary: 32 | mkdir --parents "$output_directory" 33 | cd "$directory_to_watch" 34 | 35 | # Watch for files to be placed into $directory_to_watch. When they are, run the program on them. 36 | inotifywait --monitor "$directory_to_watch" --format '%f' -e close_write | \ 37 | while read file; do 38 | "$program_location" \ 39 | $program_arguments \ 40 | --input-image "$file" \ 41 | "$output_directory/$file-left_page" \ 42 | "$output_directory/$file-right_page" 43 | done 44 | -------------------------------------------------------------------------------- /Example_Images/example_command_invocation_for_test_input_jpg.sh: -------------------------------------------------------------------------------- 1 | ./bin/voussoir \ 2 | --verbose \ 3 | --offset-left-page-left-side 0.3 \ 4 | --offset-left-page-right-side 0.3 \ 5 | --offset-left-page-top-side 0.2 \ 6 | --offset-left-page-bottom-side -0.2 \ 7 | --offset-right-page-left-side -0.4 \ 8 | --offset-right-page-right-side -0.3 \ 9 | --offset-right-page-top-side 0.25 \ 10 | --offset-right-page-bottom-side -0.2 \ 11 | -i bin/docs/test_input.jpg \ 12 | ~/Downloads/voussoir_output/test1.jpg \ 13 | ~/Downloads/voussoir_output/test2.jpg 14 | -------------------------------------------------------------------------------- /Example_Images/test_input.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jglev/voussoir/efff2fe86a986e2fc2052270749b8c3b99f71dc9/Example_Images/test_input.jpg -------------------------------------------------------------------------------- /Example_Images/test_output_left_page.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jglev/voussoir/efff2fe86a986e2fc2052270749b8c3b99f71dc9/Example_Images/test_output_left_page.jpg -------------------------------------------------------------------------------- /Example_Images/test_output_right_page.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jglev/voussoir/efff2fe86a986e2fc2052270749b8c3b99f71dc9/Example_Images/test_output_right_page.jpg -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, 2016, Yutaka Tsutano 2 | Portions copyright 2016, Jacob Levernier 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 10 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 13 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | PERFORMANCE OF THIS SOFTWARE. 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # voussoir *("voo-swar")* 2 | 3 | A single-camera solution for book scanning. 4 | 5 | This program takes images of books (each picture including either one page or a two-page spread), detects special glyphs pasted in the corners of the book's pages, and **[de-keystones](https://en.wikipedia.org/wiki/Keystone_effect "Wikipedia: 'Keystone Effect'") and thereby digitally flattens the pages.** It then **automatically separates the pages into separate, cropped image files.** 6 | 7 | ## Why is this program called "voussoir"? 8 | 9 | A [voussoir](https://en.wikipedia.org/wiki/Voussoir "Wikipedia: 'Voussoir'") is a stone in an archway; a [keystone](https://en.wikipedia.org/wiki/Keystone_(architecture) "Wikipedia: 'Keystone'") is a type of voussoir. Voussoir (the software) is meant to be one piece among several in a book scanning workflow (like one of several stones that compose an arch), and deals centrally with the ["keystone effect,"](https://en.wikipedia.org/wiki/Keystone_effect "Wikipedia: 'Keystone Effect'"), which is named after the type of arch stone : ) 10 | 11 | Other useful open-source programs to use when book scanning include [darktable](http://www.darktable.org/ "darktable") and [ScanTailor](http://scantailor.org/ "ScanTailor"). An example workflow could include voussoir to de-keystone and crop images, darktable to adjust contrast and sharpen images, and ScanTailor to do final processing. 12 | 13 | # Example 14 | 15 | The program takes images like this: 16 | 17 | 18 | [Keystoned image example] 19 | 20 | 21 | And turns them into images like these: 22 | 23 | 28 | [De-keystoned left page] 29 | 30 | 31 | 35 | [De-keystoned right page] 36 | 37 | 38 | # Background 39 | 40 | This program is an annotated and expanded fork of a one-day build written in 2012 by [Yutaka Tsutano](http://yutaka.tsutano.com/projects/others/ "Yutaka Tsutano's website"). Videos of Mr. Tsutano's prototype book scanner build and software tests are available here: 41 | 42 | * [Book Scanner: First Prototype](http://www.youtube.com/watch?v=rjzxlA9RWio) 43 | * [Book Scanner: Marker Test](http://www.youtube.com/watch?v=YXANjnry6CU) 44 | * [Book Scanner: Image Processing Test #1](http://www.youtube.com/watch?v=lHHPFBH2EkA) 45 | 46 | Mr. Tsutano posted the code after receiving requests from viewers of the above videos, and kindly released the code under the permissive [ISC license](https://en.wikipedia.org/wiki/ISC_license "ISC License") (which is [functionally equivalent to](http://choosealicense.com/licenses/#isc "ChooseALicense.com: ISC License") the [BSD 2-Clause](http://choosealicense.com/licenses/bsd-2-clause/ "BSD Two-Clause License") and [MIT](http://choosealicense.com/licenses/mit/ "MIT License") licenses) in 2016. 47 | 48 | Mr. Tsutano noted in the code's original readme that "this program was written in ad-hoc manner without thinking about publishing the source code. It was just a test of concept. So I published it. But it was a project that was done in a day years ago, and I have no time to verify it still works today." Jacob Levernier, a member of the [DIY Book Scanning](http://DIYBookscanner.org "DIYBookScanner.org") community, forked the codebase in 2016 in order to better document and clean it for a wider audience. The program now has a documented command-line interface that supports any size of book and fine-tuned adjustment of output image borders. 49 | 50 | ## Compilation 51 | 52 | ### Install OpenCV and `cmake`: 53 | 54 | * **MacPorts:** `sudo port install opencv cmake` 55 | * **Linux:** 56 | * **openSUSE**: `sudo zypper install opencv cmake libopencv2_4 opencv-devel` 57 | * **Ubuntu**: `sudo apt-get install opencv cmake opencv-dev` (This will also install opencv dependencies). 58 | 59 | ### Confirm that you have installed (and are using) `gcc` 4.9+ 60 | 61 | This program uses [docopt](https://github.com/docopt/docopt.cpp "Docopt.cpp GitHub page"). Docopt does not need to be installed separately (i.e., it's bundled within this repository), but it *does* depend on `gcc` 4.9 or greater. 62 | 63 | If you are getting an error when compiling that includes "undefined reference to `std::regex_token_iterator`", run `gcc --version` and confirm that your `gcc` version is greater than or equal to 4.9. If not, you need to upgrade `gcc.` Note that `g++ --version`, `c++ --version`, and `cpp --version` should similarly show 4.9 or greater. 64 | 65 | If you've upgraded and are still seeing the error, it's likely that your system is still trying to use the lower version. In this case, one way to explicitly tell cmake which version to use (for example, version 5) is to replace the command `cmake ./` below with `cmake -DCMAKE_CXX_COMPILER=g++-5 .`. 66 | 67 | *\[If you successfully build the program on another type of system, please let me know, and I'll add the required packages here.]* 68 | 69 | ### Compile using `cmake` (See above for information regarding telling `cmake` explicitly which version of c++ to use): 70 | 71 | ``` 72 | cd [directory of the source code] 73 | cmake ./ 74 | make 75 | ``` 76 | 77 | The binary executable will be saved under `./bin/voussoir`, along with .pdf and .ai copies of the glyphs/markers discussed below. 78 | 79 | # Usage 80 | 81 | ## Getting Ready to Use the Program / Taking Pictures 82 | 83 | Within the `markers` directory, you'll find PDF and Adobe Illustrator / Inkscape versions of a series of 15 "glyphs," small images that each comprises a unique pattern of pixels in a 6x6 grid. You'll need to print and cut out the glyphs; currently, only glyphs 0-3 (left page) and 4-7 (right page) are needed. 84 | 85 | Tape or otherwise affix the glyphs in **clockwise order** around the perimeter of each book page (for example, if you're using a glass or acrylic [platen](http://diybookscanner.org/ "DIYBookScanner.org -- Scroll down for a description of each part of a book scanner") to flatten the pages of a book, affix the glyphs in each corner of the platen: **starting at the top left and moving clockwise to the center/spine of the book,** glyphs 0, 1, 2, and 3 on the left page, and (again from top left and moving clockwise) glyphs 4, 5, 6, and 7 on the right page. 86 | 87 | Alternatively, instead of moving the markers for each book you scan, you can more permanently affix the markers to the outside of your platen or book cradle, and then use the program's "offset" command-line options to adjust the output images for each book. 88 | 89 | **Electrical tape** works well to affix the markers to glass or acrylic, because it is unlikely to leave residue. 90 | 91 | When you take photos, it should be under **bright, even lighting,** in order to make both the book pages and the glyphs visible and crisp. One of the advantages of using this program is that you can take photos from off center -- as long as the glyphs can all be clearly seen and are not blurry in the image, each page will be de-keystoned as if it had been photographed perpendicularly (i.e., straight on) from the camera. 92 | 93 | One useful workflow with this program is to set up your camera, take several test photos, and confirm that the program will be able to recognize the glyphs in them. Once you have confirmed this, you can scan the full book. 94 | 95 | Do note that the program only transforms the pages as two-dimensional objects; that is, it **assumes that the pages are each (individually) flat** (e.g., under a glass or acrylic platen). If the pages are **warped/curled** in addition to being keystoned, this program will not correct the curl (correcting curl is difficult to do; [ScanTailor](http://scantailor.org/ "ScanTailor"), also linked below, makes a good effort by looking for curves in lines of text on each page, as do commercial options like [Booksorber](http://booksorber.com/ "Booksorber"), which tries to find the outline of the book's pages; in general, though, dewarping a page consistently likely requires a more complicated setup using [lasers](https://github.com/duerig/laser-dewarp "Laser Dewarper DIY book scanning software") or [infrared light](https://www.youtube.com/watch?v=03ccxwNssmo "YouTube: 'BFS-Auto: High Speed Book Scanner at over 250 pages/min'"). 96 | 97 | ## Using the Program 98 | 99 | The program takes six basic arguments: 100 | 101 | `./voussoir --page-height 10 --page-width 6 --input-image test_input.jpg output_left.jpg output_right.jpg` 102 | 103 | In this example, `test_input.jpg` is the input file name, and the final two arguments are the 104 | output file names. The page is 10 units high and 6 units wide (these can be inches or any other unit -- what matters is their relation to each other). If you do not specify a height and width, they will default to 9.5 and 6, respectively. 105 | 106 | Run `./bin/voussoir --help` to see additional options for making cropping ("offset") adjustments to each edge of each page, and/or for specifying that you only want to process a left page or right page (vs. both pages). 107 | 108 | ### Debugging using a Webcam 109 | 110 | To debug using a webcam, execute the program without an input file argument: 111 | 112 | ``` 113 | ./voussoir 114 | ``` 115 | 116 | If a webcam is found, this will cause a window to open, showing output from the webcam. When the four "left page" glyphs (i.e., glyphs 0, 1, 2, and 3) are detected by the webcam, a new window will open showing the de-keystoned image that the four glyphs surround. Similarly, when the four "right page" glyphs (i.e., glyphs 4, 5, 6, and 7) are detected by the webcam, an additional new window will open, showing the de-keystoned image for those four glyphs. Throughout this process, debugging text will be given in the terminal window, including which glyphs are detected. 117 | 118 | ### Example Scripts 119 | 120 | The Example_Images directory in this repository contains two example scripts. 121 | 122 | * `example_automated_script_that_watches_a_folder.sh` provides an approach for watching a directory and automatically processing any images that are placed into it. 123 | * `example_command_invocation_for_test_input_jpg.sh` provides an example invocation for the program for the image located in this repository under `Example_Images/test_input.jpg`. 124 | 125 | ### More on Book Scanning 126 | 127 | * If you're interested in building some **hardware** to flatten the pages of the book, I recommend exploring the [DIYBookScanner.org gallery and forums](http://diybookscanner.org/intro.html "DIYBookScanner.org: Gallery"). An especially popular and easy-to-build model was created by a gentleman named David Landin in 2013 out of **PVC plumbing pipe,** and can be viewed [here](http://diybookscanner.org/forum/viewtopic.php?f=14&t=2914 "DIYBookScanner.org: David Landin model"). 128 | * Additionally, the founder of the DIYBookScanning community, [Daniel Reetz](http://www.danreetz.com/ "Daniel Reetz"), wrote an extensive [monograph](http://diybookscanner.org/archivist "DIYBookScanner.org: The Archivist") on DIY scanning hardware in 2015, when he retired from the project (see also [Mr. Reetz' blog post](http://www.danreetz.com/blog/2015/12/31/internet-dan-seems-to-be-dead/ "Daniel Reetz: 'Internet Dan Seems to be Dead'") about the site). Mr. Reetz' expertise on the topic is unmatched; his monograph is, in my opinion, the single best place to begin learning about DIY scanners (after the [DIYBookScanner.org home page](http://diybookscanner.org "DIYBookScanner.org: Home Page") and [gallery](http://diybookscanner.org/intro.html "DIYBookScanner.org: Gallery") linked above). 129 | * As noted above, if you are interested in post-processing for your book photos (e.g., white-balance correcting, further automatic cropping, etc., the output from this program can be used as input for **[darktable](http://www.darktable.org/ "darktable")** and **[ScanTailor](http://scantailor.org/ "ScanTailor"),** which are also free and open-source. 130 | 131 | # Todo 132 | 133 | * Add a command line option (perhaps '-a --list-of-images-with-alert-glyph ') for scanning workflows that involve occasional non-standard page sizes (for example, for books with inserts). The option would cause the program to behave differently if it recognizes an additional glyph (e.g., glyph #8 in the markers .pdf and .ai files); when the glyph is recognized, the input image name would be appended to a text file. 134 | * This would allow a scanner operator to take a picture of the book with image inserts extended, etc., but to temporarily affix the "alert" glyph anywhere near the book, and then remove the glyph and move on to the next pages of the book. This would simplify the process for going back and separately processing those non-standard pages later. 135 | * This could be defined in a separate function ("is_alert_glyph_present") called from main.cpp near where the page processing function is called for the left and right pages. 136 | * Establish a build process to allow distributing ready-to-use binaries for different platforms (including the major Linux distros 32- and 64-bit, OSX 64-bit, and Windows) 137 | * **Get more people on board!** I (Jacob) am not a C++ developer, so I see my role as primarily custodial right now (i.e., working on documentation and community-building). 138 | * **Hannes Künsebeck has written [a port of Voussoir to Python called `pyvoussoir`](https://github.com/hnesk/pyvoussoir). That project is likely to be more maintained than this one.** 139 | 140 | # Contributors (in reverse-chronological order) 141 | 142 | * [Jacob Levernier](http://adunumdatum.org "Jacob Levernier's website") 143 | * [Yutaka Tsutano](http://yutaka.tsutano.com "Yutaka Tsutano's website") 144 | -------------------------------------------------------------------------------- /docopt-0.6.2/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | 6 | # Compiled Dynamic libraries 7 | *.so 8 | *.dylib 9 | *.dll 10 | 11 | # Compiled Static libraries 12 | *.lai 13 | *.la 14 | *.a 15 | 16 | # Compiled examples, as per docs 17 | example 18 | naval_fate 19 | run_testcase 20 | run_testcase.exe 21 | 22 | # CMake temporary files 23 | CMakeCache.txt 24 | CMakeFiles 25 | CPackConfig.cmake 26 | CPackSourceConfig.cmake 27 | Makefile 28 | cmake_install.cmake 29 | docopt-config-version.cmake 30 | 31 | # Files configured by CMake 32 | run_tests 33 | -------------------------------------------------------------------------------- /docopt-0.6.2/.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | sudo: false # Use the new container infrastructure 3 | 4 | matrix: 5 | include: 6 | - os: linux 7 | env: 8 | - COMPILER=g++-5 STDLIB=libc++ 9 | addons: 10 | apt: 11 | sources: ['ubuntu-toolchain-r-test', 'george-edison55-precise-backports'] 12 | packages: ["g++-5", "cmake-data", "cmake"] 13 | - os: linux 14 | env: 15 | - COMPILER=g++-4.8 USE_BOOST_REGEX=ON 16 | addons: 17 | apt: 18 | sources: ['ubuntu-toolchain-r-test', 'george-edison55-precise-backports', 'boost-latest'] 19 | packages: ["g++-4.8", "cmake-data", "cmake", "libboost-regex1.55-dev"] 20 | 21 | - os: linux 22 | env: 23 | - COMPILER=clang++-3.6 STDLIB=libc++ 24 | addons: 25 | apt: 26 | sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6', 'george-edison55-precise-backports'] 27 | packages: ["clang-3.6", "cmake-data", "cmake"] 28 | 29 | - os: linux 30 | env: 31 | - COMPILER=clang++-3.7 STDLIB=libc++ 32 | addons: 33 | apt: 34 | sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.7', 'george-edison55-precise-backports'] 35 | packages: ["clang-3.7", "cmake-data", "cmake"] 36 | 37 | - os: osx 38 | osx_image: xcode6.4 39 | env: 40 | - COMPILER=clang++ V='Apple LLVM 6.4' 41 | - COMPILER=clang++ V='Apple LLVM 6.4' WITH_CPP14=true 42 | 43 | - os: osx 44 | osx_image: xcode7 45 | env: 46 | - COMPILER=clang++ V='Apple LLVM 7.0' 47 | - COMPILER=clang++ V='Apple LLVM 7.0' WITH_CPP14=true 48 | 49 | before_install: 50 | - | 51 | if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then 52 | brew rm --force cmake && brew install cmake 53 | fi 54 | 55 | - CMAKE_CXX_FLAGS+=" -Wall" 56 | 57 | - if [[ "${WITH_CPP14}" == "true" ]]; then CMAKE_OPTIONS+=" -DCMAKE_CXX_STANDARD=14"; fi 58 | - | 59 | if [[ "${USE_BOOST_REGEX}" == "ON" ]]; then 60 | CMAKE_OPTIONS+=" -DUSE_BOOST_REGEX=ON" 61 | CMAKE_OPTIONS+=" -DBoost_REGEX_LIBRARY_DEBUG=/usr/lib/x86_64-linux-gnu/libboost_regex.so.1.55.0" 62 | CMAKE_OPTIONS+=" -DBoost_REGEX_LIBRARY_RELEASE=/usr/lib/x86_64-linux-gnu/libboost_regex.so.1.55.0" 63 | fi 64 | - if [[ "${STDLIB}" == "libc++" ]]; then CMAKE_CXX_FLAGS+=" -stdlib=libc++"; fi 65 | 66 | - ${COMPILER} --version 67 | 68 | before_script: 69 | - rm -rf build/ 70 | - mkdir build 71 | - cd build 72 | - cmake -DCMAKE_CXX_COMPILER=${COMPILER} -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -DWITH_TESTS=1 -DWITH_EXAMPLE=1 ${CMAKE_OPTIONS} .. 73 | 74 | script: 75 | - cmake --build . 76 | - python run_tests 77 | -------------------------------------------------------------------------------- /docopt-0.6.2/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | project(docopt.cpp VERSION 0.6.1) 3 | 4 | include(GNUInstallDirs) 5 | 6 | #============================================================================ 7 | # Settable options 8 | #============================================================================ 9 | option(WITH_TESTS "Build tests." OFF) 10 | option(WITH_EXAMPLE "Build example." OFF) 11 | option(USE_BOOST_REGEX "Replace std::regex with Boost.Regex" OFF) 12 | 13 | #============================================================================ 14 | # Internal compiler options 15 | #============================================================================ 16 | # C++ standard 17 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 18 | set(CMAKE_CXX_EXTENSIONS OFF) 19 | if(NOT CMAKE_CXX_STANDARD OR CMAKE_CXX_STANDARD LESS 11) 20 | set(CMAKE_CXX_STANDARD 11) 21 | endif() 22 | 23 | # Suppression of "unknown pragma" warning on GCC 24 | if(CMAKE_COMPILER_IS_GNUCXX) 25 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-pragmas") # Code uses #pragma mark 26 | endif() 27 | 28 | #============================================================================ 29 | # Sources & headers 30 | #============================================================================ 31 | set(docopt_SOURCES docopt.cpp) 32 | set(docopt_HEADERS 33 | docopt.h 34 | docopt_private.h 35 | docopt_util.h 36 | docopt_value.h 37 | ) 38 | 39 | #============================================================================ 40 | # Compile targets 41 | #============================================================================ 42 | if(MSVC OR XCODE) 43 | # MSVC requires __declspec() attributes, which are achieved via the 44 | # DOCOPT_DLL and DOCOPT_EXPORTS macros below. Since those macros are only 45 | # defined when building a shared library, we must build the shared and 46 | # static libraries completely separately. 47 | # Xcode does not support libraries with only object files as sources. 48 | # See https://cmake.org/cmake/help/v3.0/command/add_library.html?highlight=add_library 49 | add_library(docopt SHARED ${docopt_SOURCES} ${docopt_HEADERS}) 50 | add_library(docopt_s STATIC ${docopt_SOURCES} ${docopt_HEADERS}) 51 | else() 52 | # If not using MSVC or Xcode, we will create an intermediate object target 53 | # to avoid compiling the source code twice. 54 | add_library(docopt_o OBJECT ${docopt_SOURCES} ${docopt_HEADERS}) 55 | set_target_properties(docopt_o PROPERTIES POSITION_INDEPENDENT_CODE TRUE) 56 | 57 | add_library(docopt SHARED $) 58 | add_library(docopt_s STATIC $) 59 | endif() 60 | 61 | target_include_directories(docopt PUBLIC $ $) 62 | target_include_directories(docopt_s PUBLIC $ $) 63 | 64 | if(MSVC) 65 | # DOCOPT_DLL: Must be specified when building *and* when using the DLL. 66 | # That's what the "PUBLIC" means. 67 | # DOCOPT_EXPORTS: Must use __declspec(dllexport) when building the DLL. 68 | # "PRIVATE" means it's only defined when building the DLL. 69 | target_compile_definitions(docopt PUBLIC DOCOPT_DLL 70 | PRIVATE DOCOPT_EXPORTS) 71 | endif() 72 | 73 | if(NOT MSVC) 74 | set_target_properties(docopt PROPERTIES OUTPUT_NAME docopt) 75 | set_target_properties(docopt_s PROPERTIES OUTPUT_NAME docopt) 76 | endif() 77 | 78 | if(USE_BOOST_REGEX) 79 | add_definitions("-DDOCTOPT_USE_BOOST_REGEX") 80 | # This is needed on Linux, where linking a static library into docopt.so 81 | # fails because boost static libs are not compiled with -fPIC 82 | set(Boost_USE_STATIC_LIBS OFF) 83 | find_package(Boost 1.53 REQUIRED COMPONENTS regex) 84 | include_directories(${Boost_INCLUDE_DIRS}) 85 | target_link_libraries(docopt ${Boost_LIBRARIES}) 86 | if(WITH_STATIC) 87 | target_link_libraries(docopt_s ${Boost_LIBRARIES}) 88 | endif() 89 | endif() 90 | 91 | #============================================================================ 92 | # Examples 93 | #============================================================================ 94 | if(WITH_EXAMPLE) 95 | add_executable(docopt_example examples/naval_fate.cpp) 96 | target_link_libraries(docopt_example docopt) 97 | endif() 98 | 99 | #============================================================================ 100 | # Tests 101 | #============================================================================ 102 | if(WITH_TESTS) 103 | set(TESTPROG "${CMAKE_CURRENT_BINARY_DIR}/run_testcase") 104 | set(TESTCASES "${PROJECT_SOURCE_DIR}/testcases.docopt") 105 | add_executable(run_testcase run_testcase.cpp) 106 | target_link_libraries(run_testcase docopt) 107 | configure_file( 108 | "${PROJECT_SOURCE_DIR}/run_tests.py" 109 | "${CMAKE_CURRENT_BINARY_DIR}/run_tests" 110 | ESCAPE_QUOTES 111 | ) 112 | add_test("Testcases docopt" ${TESTPROG}) 113 | endif() 114 | 115 | #============================================================================ 116 | # Install 117 | #============================================================================ 118 | set(export_name "docopt-targets") 119 | 120 | # Runtime package 121 | install(TARGETS docopt EXPORT ${export_name} DESTINATION ${CMAKE_INSTALL_LIBDIR}) 122 | 123 | # Development package 124 | install(TARGETS docopt_s EXPORT ${export_name} DESTINATION ${CMAKE_INSTALL_LIBDIR}) 125 | install(FILES ${docopt_HEADERS} DESTINATION include/docopt) 126 | 127 | # CMake Package 128 | include(CMakePackageConfigHelpers) 129 | write_basic_package_version_file("${PROJECT_BINARY_DIR}/docopt-config-version.cmake" COMPATIBILITY SameMajorVersion) 130 | install(FILES docopt-config.cmake ${PROJECT_BINARY_DIR}/docopt-config-version.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/docopt") 131 | install(EXPORT ${export_name} DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/docopt") 132 | 133 | #============================================================================ 134 | # CPack 135 | #============================================================================ 136 | set(CPACK_PACKAGE_NAME "docopt") 137 | set(CPACK_DEBIAN_PACKAGE_DEPENDS "") 138 | set(CPACK_RPM_PACKAGE_REQUIRES "") 139 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Beautiful command line interfaces") 140 | set(CPACK_PACKAGE_VENDOR "Jared Grubb") 141 | set(CPACK_PACKAGE_CONTACT ${CPACK_PACKAGE_VENDOR}) 142 | set(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/README.rst") 143 | set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE-MIT") 144 | set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) 145 | set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR}) 146 | set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH}) 147 | set(CPACK_DEBIAN_PACKAGE_SECTION "Development") 148 | set(CPACK_RPM_PACKAGE_GROUP "Development/Libraries") 149 | set(CPACK_RPM_PACKAGE_LICENSE "MIT") 150 | set(CPACK_STRIP_FILES TRUE) 151 | include(CPack) 152 | -------------------------------------------------------------------------------- /docopt-0.6.2/LICENSE-Boost-1.0: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /docopt-0.6.2/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Vladimir Keleshev, 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the Software 6 | without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, 8 | and/or sell copies of the Software, and to permit persons to 9 | whom the Software is furnished to do so, subject to the 10 | following conditions: 11 | 12 | The above copyright notice and this permission notice shall 13 | be included in all copies or substantial portions of the 14 | Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 17 | KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 18 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 19 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 20 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 22 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /docopt-0.6.2/README.rst: -------------------------------------------------------------------------------- 1 | ``docopt.cpp``: A C++11 Port 2 | ============================ 3 | docopt creates *beautiful* command-line interfaces 4 | -------------------------------------------------- 5 | 6 | Isn't it awesome how ``getopt`` (and ``boost::program_options`` for you fancy 7 | folk!) generate help messages based on your code?! These timeless functions 8 | have been around for decades and have proven we don't need anything better, right? 9 | 10 | *Hell no!* You know what's awesome? It's when the option parser *is* 11 | generated based on the beautiful help message that you write yourself! 12 | This way you don't need to write this stupid repeatable parser-code, 13 | and instead can write only the help message--*the way you want it*. 14 | 15 | **docopt** helps you create most beautiful command-line interfaces 16 | *easily*: 17 | 18 | .. code:: c++ 19 | 20 | #include "docopt.h" 21 | 22 | #include 23 | 24 | static const char USAGE[] = 25 | R"(Naval Fate. 26 | 27 | Usage: 28 | naval_fate ship new ... 29 | naval_fate ship move [--speed=] 30 | naval_fate ship shoot 31 | naval_fate mine (set|remove) [--moored | --drifting] 32 | naval_fate (-h | --help) 33 | naval_fate --version 34 | 35 | Options: 36 | -h --help Show this screen. 37 | --version Show version. 38 | --speed= Speed in knots [default: 10]. 39 | --moored Moored (anchored) mine. 40 | --drifting Drifting mine. 41 | )"; 42 | 43 | int main(int argc, const char** argv) 44 | { 45 | std::map args 46 | = docopt::docopt(USAGE, 47 | { argv + 1, argv + argc }, 48 | true, // show help if requested 49 | "Naval Fate 2.0"); // version string 50 | 51 | for(auto const& arg : args) { 52 | std::cout << arg.first << arg.second << std::endl; 53 | } 54 | 55 | return 0; 56 | } 57 | 58 | Beat that! The option parser is generated based on the docstring above 59 | that is passed to ``docopt::docopt`` function. ``docopt`` parses the usage 60 | pattern (``"Usage: ..."``) and option descriptions (lines starting 61 | with dash "``-``") and ensures that the program invocation matches the 62 | usage pattern; it parses options, arguments and commands based on 63 | that. The basic idea is that *a good help message has all necessary 64 | information in it to make a parser*. 65 | 66 | C++11 port details 67 | --------------------------------------------------- 68 | 69 | This is a port of the ``docopt.py`` module (https://github.com/docopt/docopt), 70 | and we have tried to maintain full feature parity (and code structure) as the 71 | original. 72 | 73 | This port is written in C++11 and also requires a good C++11 standard library 74 | (in particular, one with ``regex`` support). The following compilers are known 75 | to work with docopt: 76 | 77 | - Clang 3.3 and later 78 | - GCC 4.9 79 | - Visual C++ 2015 RC 80 | 81 | GCC-4.8 can work, but the std::regex module needs to be replaced with ``Boost.Regex``. 82 | In that case, you will need to define ``DOCTOPT_USE_BOOST_REGEX`` when compiling 83 | docopt, and link your code with the appropriated Boost libraries. A relativley 84 | recent version of Boost is needed: 1.55 works, but 1.46 does not for example. 85 | 86 | This port is licensed under the MIT license, just like the original module. 87 | However, we are also dual-licensing this code under the Boost License, version 1.0, 88 | as this is a popular C++ license. The licenses are similar and you are free to 89 | use this code under the terms of either license. 90 | 91 | The differences from the Python port are: 92 | 93 | * the addition of a ``docopt_parse`` function, which does not terminate 94 | the program on error 95 | * a ``docopt::value`` type to hold the various value types that can be parsed. 96 | We considered using boost::variant, but it seems better to have no external 97 | dependencies (beyond a good STL). 98 | * because C++ is statically-typed and Python is not, we had to make some 99 | changes to the interfaces of the internal parse tree types. 100 | * because ``std::regex`` does not have an equivalent to Python's regex.split, 101 | some of the regex's had to be restructured and additional loops used. 102 | 103 | API 104 | --------------------------------------------------- 105 | 106 | .. code:: c++ 107 | 108 | docopt::docopt(doc, argv, help /* =true */, version /* ="" */, options_first /* =false */) 109 | 110 | ``docopt`` takes 2 required and 3 optional arguments: 111 | 112 | - ``doc`` is a string that contains a **help message** that will be parsed to 113 | create the option parser. The simple rules of how to write such a 114 | help message are given in next sections. Here is a quick example of 115 | such a string (note that this example uses the "raw string literal" feature 116 | that was added to C++11): 117 | 118 | .. code:: c++ 119 | 120 | R"(Usage: my_program [-hso FILE] [--quiet | --verbose] [INPUT ...] 121 | 122 | -h --help show this 123 | -s --sorted sorted output 124 | -o FILE specify output file [default: ./test.txt] 125 | --quiet print less text 126 | --verbose print more text 127 | )" 128 | 129 | - ``argv`` is a vector of strings representing the args passed. Although 130 | main usually takes a ``(int argc, const char** argv)`` pair, you can 131 | pass the value ``{argv+1, argv+argc}`` to generate the vector automatically. 132 | (Note we skip the argv[0] argument!) Alternatively you can supply a list of 133 | strings like ``{ "--verbose", "-o", "hai.txt" }``. 134 | 135 | - ``help``, by default ``true``, specifies whether the parser should 136 | automatically print the help message (supplied as ``doc``) and 137 | terminate, in case ``-h`` or ``--help`` option is encountered 138 | (options should exist in usage pattern, more on that below). If you 139 | want to handle ``-h`` or ``--help`` options manually (as other 140 | options), set ``help=false``. 141 | 142 | - ``version``, by default empty, is an optional argument that 143 | specifies the version of your program. If supplied, then, (assuming 144 | ``--version`` option is mentioned in usage pattern) when parser 145 | encounters the ``--version`` option, it will print the supplied 146 | version and terminate. ``version`` could be any printable object, 147 | but most likely a string, e.g. ``"2.1.0rc1"``. 148 | 149 | Note, when ``docopt`` is set to automatically handle ``-h``, 150 | ``--help`` and ``--version`` options, you still need to mention 151 | them in usage pattern for this to work (also so your users to 152 | know about them!) 153 | 154 | - ``options_first``, by default ``false``. If set to ``true`` will 155 | disallow mixing options and positional argument. I.e. after first 156 | positional argument, all arguments will be interpreted as positional 157 | even if the look like options. This can be used for strict 158 | compatibility with POSIX, or if you want to dispatch your arguments 159 | to other programs. 160 | 161 | The **return** value is a ``map`` with options, 162 | arguments and commands as keys, spelled exactly like in your help message. 163 | Long versions of options are given priority. For example, if you invoke the 164 | top example as:: 165 | 166 | naval_fate ship Guardian move 100 150 --speed=15 167 | 168 | the return dictionary will be: 169 | 170 | .. code:: python 171 | 172 | {"--drifting": false, "mine": false, 173 | "--help": false, "move": true, 174 | "--moored": false, "new": false, 175 | "--speed": "15", "remove": false, 176 | "--version": false, "set": false, 177 | "": ["Guardian"], "ship": true, 178 | "": "100", "shoot": false, 179 | "": "150"} 180 | 181 | If any parsing error (in either the usage, or due to incorrect user inputs) is 182 | encountered, the program will exit with exit code -1. 183 | 184 | Note that there is another function that does not exit on error, and instead will 185 | propogate an exception that you can catch and process as you like. See the docopt.h file 186 | for information on the exceptions and usage: 187 | 188 | .. code:: c++ 189 | 190 | docopt::docopt_parse(doc, argv, help /* =true */, version /* =true */, options_first /* =false) 191 | 192 | 193 | Help message format 194 | --------------------------------------------------- 195 | 196 | Help message consists of 2 parts: 197 | 198 | - Usage pattern, e.g.:: 199 | 200 | Usage: my_program [-hso FILE] [--quiet | --verbose] [INPUT ...] 201 | 202 | - Option descriptions, e.g.:: 203 | 204 | -h --help show this 205 | -s --sorted sorted output 206 | -o FILE specify output file [default: ./test.txt] 207 | --quiet print less text 208 | --verbose print more text 209 | 210 | Their format is described below; other text is ignored. 211 | 212 | Usage pattern format 213 | ---------------------------------------------------------------------- 214 | 215 | **Usage pattern** is a substring of ``doc`` that starts with 216 | ``usage:`` (case *insensitive*) and ends with a *visibly* empty line. 217 | Minimum example: 218 | 219 | .. code:: python 220 | 221 | """Usage: my_program 222 | 223 | """ 224 | 225 | The first word after ``usage:`` is interpreted as your program's name. 226 | You can specify your program's name several times to signify several 227 | exclusive patterns: 228 | 229 | .. code:: python 230 | 231 | """Usage: my_program FILE 232 | my_program COUNT FILE 233 | 234 | """ 235 | 236 | Each pattern can consist of the following elements: 237 | 238 | - ****, **ARGUMENTS**. Arguments are specified as either 239 | upper-case words, e.g. ``my_program CONTENT-PATH`` or words 240 | surrounded by angular brackets: ``my_program ``. 241 | - **--options**. Options are words started with dash (``-``), e.g. 242 | ``--output``, ``-o``. You can "stack" several of one-letter 243 | options, e.g. ``-oiv`` which will be the same as ``-o -i -v``. The 244 | options can have arguments, e.g. ``--input=FILE`` or ``-i FILE`` or 245 | even ``-iFILE``. However it is important that you specify option 246 | descriptions if you want your option to have an argument, a default 247 | value, or specify synonymous short/long versions of the option (see 248 | next section on option descriptions). 249 | - **commands** are words that do *not* follow the described above 250 | conventions of ``--options`` or ```` or ``ARGUMENTS``, 251 | plus two special commands: dash "``-``" and double dash "``--``" 252 | (see below). 253 | 254 | Use the following constructs to specify patterns: 255 | 256 | - **[ ]** (brackets) **optional** elements. e.g.: ``my_program 257 | [-hvqo FILE]`` 258 | - **( )** (parens) **required** elements. All elements that are *not* 259 | put in **[ ]** are also required, e.g.: ``my_program 260 | --path= ...`` is the same as ``my_program 261 | (--path= ...)``. (Note, "required options" might be not 262 | a good idea for your users). 263 | - **|** (pipe) **mutually exclusive** elements. Group them using **( 264 | )** if one of the mutually exclusive elements is required: 265 | ``my_program (--clockwise | --counter-clockwise) TIME``. Group 266 | them using **[ ]** if none of the mutually-exclusive elements are 267 | required: ``my_program [--left | --right]``. 268 | - **...** (ellipsis) **one or more** elements. To specify that 269 | arbitrary number of repeating elements could be accepted, use 270 | ellipsis (``...``), e.g. ``my_program FILE ...`` means one or 271 | more ``FILE``-s are accepted. If you want to accept zero or more 272 | elements, use brackets, e.g.: ``my_program [FILE ...]``. Ellipsis 273 | works as a unary operator on the expression to the left. 274 | - **[options]** (case sensitive) shortcut for any options. You can 275 | use it if you want to specify that the usage pattern could be 276 | provided with any options defined below in the option-descriptions 277 | and do not want to enumerate them all in usage-pattern. 278 | - "``[--]``". Double dash "``--``" is used by convention to separate 279 | positional arguments that can be mistaken for options. In order to 280 | support this convention add "``[--]``" to your usage patterns. 281 | - "``[-]``". Single dash "``-``" is used by convention to signify that 282 | ``stdin`` is used instead of a file. To support this add "``[-]``" 283 | to your usage patterns. "``-``" acts as a normal command. 284 | 285 | If your pattern allows to match argument-less option (a flag) several 286 | times:: 287 | 288 | Usage: my_program [-v | -vv | -vvv] 289 | 290 | then number of occurrences of the option will be counted. I.e. 291 | ``args['-v']`` will be ``2`` if program was invoked as ``my_program 292 | -vv``. Same works for commands. 293 | 294 | If your usage patterns allows to match same-named option with argument 295 | or positional argument several times, the matched arguments will be 296 | collected into a list:: 297 | 298 | Usage: my_program --path=... 299 | 300 | I.e. invoked with ``my_program file1 file2 --path=./here 301 | --path=./there`` the returned dict will contain ``args[''] == 302 | ['file1', 'file2']`` and ``args['--path'] == ['./here', './there']``. 303 | 304 | 305 | Option descriptions format 306 | ---------------------------------------------------------------------- 307 | 308 | **Option descriptions** consist of a list of options that you put 309 | below your usage patterns. 310 | 311 | It is necessary to list option descriptions in order to specify: 312 | 313 | - synonymous short and long options, 314 | - if an option has an argument, 315 | - if option's argument has a default value. 316 | 317 | The rules are as follows: 318 | 319 | - Every line in ``doc`` that starts with ``-`` or ``--`` (not counting 320 | spaces) is treated as an option description, e.g.:: 321 | 322 | Options: 323 | --verbose # GOOD 324 | -o FILE # GOOD 325 | Other: --bad # BAD, line does not start with dash "-" 326 | 327 | - To specify that option has an argument, put a word describing that 328 | argument after space (or equals "``=``" sign) as shown below. Follow 329 | either or UPPER-CASE convention for options' 330 | arguments. You can use comma if you want to separate options. In 331 | the example below, both lines are valid, however you are recommended 332 | to stick to a single style.:: 333 | 334 | -o FILE --output=FILE # without comma, with "=" sign 335 | -i , --input # with comma, without "=" sing 336 | 337 | - Use two spaces to separate options with their informal description:: 338 | 339 | --verbose More text. # BAD, will be treated as if verbose option had 340 | # an argument "More", so use 2 spaces instead 341 | -q Quit. # GOOD 342 | -o FILE Output file. # GOOD 343 | --stdout Use stdout. # GOOD, 2 spaces 344 | 345 | - If you want to set a default value for an option with an argument, 346 | put it into the option-description, in form ``[default: 347 | ]``:: 348 | 349 | --coefficient=K The K coefficient [default: 2.95] 350 | --output=FILE Output file [default: test.txt] 351 | --directory=DIR Some directory [default: ./] 352 | 353 | - If the option is not repeatable, the value inside ``[default: ...]`` 354 | will be interpreted as string. If it *is* repeatable, it will be 355 | splited into a list on whitespace:: 356 | 357 | Usage: my_program [--repeatable= --repeatable=] 358 | [--another-repeatable=]... 359 | [--not-repeatable=] 360 | 361 | # will be ['./here', './there'] 362 | --repeatable= [default: ./here ./there] 363 | 364 | # will be ['./here'] 365 | --another-repeatable= [default: ./here] 366 | 367 | # will be './here ./there', because it is not repeatable 368 | --not-repeatable= [default: ./here ./there] 369 | 370 | Examples 371 | ---------------------------------------------------------------------- 372 | 373 | We have an extensive list of `examples 374 | `_ which cover 375 | every aspect of functionality of **docopt**. Try them out, read the 376 | source if in doubt. 377 | 378 | There are also very intersting applications and ideas at that page. 379 | Check out the sister project for more information! 380 | 381 | Subparsers, multi-level help and *huge* applications (like git) 382 | ---------------------------------------------------------------------- 383 | 384 | If you want to split your usage-pattern into several, implement 385 | multi-level help (with separate help-screen for each subcommand), 386 | want to interface with existing scripts that don't use **docopt**, or 387 | you're building the next "git", you will need the new ``options_first`` 388 | parameter (described in API section above). To get you started quickly 389 | we implemented a subset of git command-line interface as an example: 390 | `examples/git 391 | `_ 392 | 393 | Compiling the example / Running the tests 394 | ---------------------------------------------------------------------- 395 | The original Python module includes some language-agnostic unit tests, 396 | and these can be run with this port as well. 397 | 398 | The tests are a Python driver that uses the testcases.docopt file to then invoke 399 | a C++ test case runner (run_testcase.cpp):: 400 | 401 | $ clang++ --std=c++11 --stdlib=libc++ docopt.cpp run_testcase.cpp -o run_testcase 402 | $ python run_tests.py 403 | PASS (175) 404 | 405 | You can also compile the example shown at the start (included as example.cpp):: 406 | 407 | $ clang++ --std=c++11 --stdlib=libc++ -I . docopt.cpp examples/naval_fate.cpp -o naval_fate 408 | $ ./naval_fate --help 409 | [ ... ] 410 | $ ./naval_fate ship Guardian move 100 150 --speed=15 411 | --drifting: false 412 | --help: false 413 | --moored: false 414 | --speed: "15" 415 | --version: false 416 | : ["Guardian"] 417 | : "100" 418 | : "150" 419 | mine: false 420 | move: true 421 | new: false 422 | remove: false 423 | set: false 424 | ship: true 425 | shoot: false 426 | 427 | Development 428 | --------------------------------------------------- 429 | 430 | Comments and suggestions are *very* welcome! If you find issues, please 431 | file them and help improve our code! 432 | 433 | Please note, however, that we have tried to stay true to the original 434 | Python code. If you have any major patches, structural changes, or new features, 435 | we might want to first negotiate these changes into the Python code first. 436 | However, bring it up! Let's hear it! 437 | 438 | Changelog 439 | --------------------------------------------------- 440 | 441 | **docopt** follows `semantic versioning `_. The 442 | first release with stable API will be 1.0.0 (soon). 443 | 444 | - 0.6.2 Bugfix release (still based on docopt 0.6.1) 445 | - 0.6.1 The initial C++ port of docopt.py (based on docopt 0.6.1) 446 | -------------------------------------------------------------------------------- /docopt-0.6.2/docopt-config.cmake: -------------------------------------------------------------------------------- 1 | include("${CMAKE_CURRENT_LIST_DIR}/docopt-targets.cmake") 2 | -------------------------------------------------------------------------------- /docopt-0.6.2/docopt.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // docopt.cpp 3 | // docopt 4 | // 5 | // Created by Jared Grubb on 2013-11-03. 6 | // Copyright (c) 2013 Jared Grubb. All rights reserved. 7 | // 8 | 9 | #include "docopt.h" 10 | #include "docopt_util.h" 11 | #include "docopt_private.h" 12 | 13 | #include "docopt_value.h" 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | using namespace docopt; 25 | 26 | DOCOPT_INLINE 27 | std::ostream& docopt::operator<<(std::ostream& os, value const& val) 28 | { 29 | if (val.isBool()) { 30 | bool b = val.asBool(); 31 | os << (b ? "true" : "false"); 32 | } else if (val.isLong()) { 33 | long v = val.asLong(); 34 | os << v; 35 | } else if (val.isString()) { 36 | std::string const& str = val.asString(); 37 | os << '"' << str << '"'; 38 | } else if (val.isStringList()) { 39 | auto const& list = val.asStringList(); 40 | os << "["; 41 | bool first = true; 42 | for(auto const& el : list) { 43 | if (first) { 44 | first = false; 45 | } else { 46 | os << ", "; 47 | } 48 | os << '"' << el << '"'; 49 | } 50 | os << "]"; 51 | } else { 52 | os << "null"; 53 | } 54 | return os; 55 | } 56 | 57 | #pragma mark - 58 | #pragma mark Parsing stuff 59 | 60 | class Tokens { 61 | public: 62 | Tokens(std::vector tokens, bool isParsingArgv = true) 63 | : fTokens(std::move(tokens)), 64 | fIsParsingArgv(isParsingArgv) 65 | {} 66 | 67 | explicit operator bool() const { 68 | return fIndex < fTokens.size(); 69 | } 70 | 71 | static Tokens from_pattern(std::string const& source) { 72 | static const std::regex re_separators { 73 | "(?:\\s*)" // any spaces (non-matching subgroup) 74 | "(" 75 | "[\\[\\]\\(\\)\\|]" // one character of brackets or parens or pipe character 76 | "|" 77 | "\\.\\.\\." // elipsis 78 | ")" }; 79 | 80 | static const std::regex re_strings { 81 | "(?:\\s*)" // any spaces (non-matching subgroup) 82 | "(" 83 | "\\S*<.*?>" // strings, but make sure to keep "< >" strings together 84 | "|" 85 | "[^<>\\s]+" // string without <> 86 | ")" }; 87 | 88 | // We do two stages of regex matching. The '[]()' and '...' are strong delimeters 89 | // and need to be split out anywhere they occur (even at the end of a token). We 90 | // first split on those, and then parse the stuff between them to find the string 91 | // tokens. This is a little harder than the python version, since they have regex.split 92 | // and we dont have anything like that. 93 | 94 | std::vector tokens; 95 | std::for_each(std::sregex_iterator{ source.begin(), source.end(), re_separators }, 96 | std::sregex_iterator{}, 97 | [&](std::smatch const& match) 98 | { 99 | // handle anything before the separator (this is the "stuff" between the delimeters) 100 | if (match.prefix().matched) { 101 | std::for_each(std::sregex_iterator{match.prefix().first, match.prefix().second, re_strings}, 102 | std::sregex_iterator{}, 103 | [&](std::smatch const& m) 104 | { 105 | tokens.push_back(m[1].str()); 106 | }); 107 | } 108 | 109 | // handle the delimter token itself 110 | if (match[1].matched) { 111 | tokens.push_back(match[1].str()); 112 | } 113 | }); 114 | 115 | return Tokens(tokens, false); 116 | } 117 | 118 | std::string const& current() const { 119 | if (*this) 120 | return fTokens[fIndex]; 121 | 122 | static std::string const empty; 123 | return empty; 124 | } 125 | 126 | std::string the_rest() const { 127 | if (!*this) 128 | return {}; 129 | return join(fTokens.begin()+static_cast(fIndex), 130 | fTokens.end(), 131 | " "); 132 | } 133 | 134 | std::string pop() { 135 | return std::move(fTokens.at(fIndex++)); 136 | } 137 | 138 | bool isParsingArgv() const { return fIsParsingArgv; } 139 | 140 | struct OptionError : std::runtime_error { using runtime_error::runtime_error; }; 141 | 142 | private: 143 | std::vector fTokens; 144 | size_t fIndex = 0; 145 | bool fIsParsingArgv; 146 | }; 147 | 148 | // Get all instances of 'T' from the pattern 149 | template 150 | std::vector flat_filter(Pattern& pattern) { 151 | std::vector flattened = pattern.flat([](Pattern const* p) -> bool { 152 | return dynamic_cast(p) != nullptr; 153 | }); 154 | 155 | // now, we're guaranteed to have T*'s, so just use static_cast 156 | std::vector ret; 157 | std::transform(flattened.begin(), flattened.end(), std::back_inserter(ret), [](Pattern* p) { 158 | return static_cast(p); 159 | }); 160 | return ret; 161 | } 162 | 163 | static std::vector parse_section(std::string const& name, std::string const& source) { 164 | // ECMAScript regex only has "?=" for a non-matching lookahead. In order to make sure we always have 165 | // a newline to anchor our matching, we have to avoid matching the final newline of each grouping. 166 | // Therefore, our regex is adjusted from the docopt Python one to use ?= to match the newlines before 167 | // the following lines, rather than after. 168 | std::regex const re_section_pattern { 169 | "(?:^|\\n)" // anchored at a linebreak (or start of string) 170 | "(" 171 | "[^\\n]*" + name + "[^\\n]*(?=\\n?)" // a line that contains the name 172 | "(?:\\n[ \\t].*?(?=\\n|$))*" // followed by any number of lines that are indented 173 | ")", 174 | std::regex::icase 175 | }; 176 | 177 | std::vector ret; 178 | std::for_each(std::sregex_iterator(source.begin(), source.end(), re_section_pattern), 179 | std::sregex_iterator(), 180 | [&](std::smatch const& match) 181 | { 182 | ret.push_back(trim(match[1].str())); 183 | }); 184 | 185 | return ret; 186 | } 187 | 188 | static bool is_argument_spec(std::string const& token) { 189 | if (token.empty()) 190 | return false; 191 | 192 | if (token[0]=='<' && token[token.size()-1]=='>') 193 | return true; 194 | 195 | if (std::all_of(token.begin(), token.end(), &::isupper)) 196 | return true; 197 | 198 | return false; 199 | } 200 | 201 | template 202 | std::vector longOptions(I iter, I end) { 203 | std::vector ret; 204 | std::transform(iter, end, 205 | std::back_inserter(ret), 206 | [](typename I::reference opt) { return opt->longOption(); }); 207 | return ret; 208 | } 209 | 210 | static PatternList parse_long(Tokens& tokens, std::vector